9c65b1d2a5691763baf6b24f61bfc79eacab3e95
[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 void TestFieldAfterSnapping(int, int, int, int, int);
1123
1124 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1125
1126 // for detection of endless loops, caused by custom element programming
1127 // (using maximal playfield width x 10 is just a rough approximation)
1128 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1129
1130 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1131 {                                                                       \
1132   if (recursion_loop_detected)                                          \
1133     return (rc);                                                        \
1134                                                                         \
1135   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1136   {                                                                     \
1137     recursion_loop_detected = TRUE;                                     \
1138     recursion_loop_element = (e);                                       \
1139   }                                                                     \
1140                                                                         \
1141   recursion_loop_depth++;                                               \
1142 }
1143
1144 #define RECURSION_LOOP_DETECTION_END()                                  \
1145 {                                                                       \
1146   recursion_loop_depth--;                                               \
1147 }
1148
1149 static int recursion_loop_depth;
1150 static boolean recursion_loop_detected;
1151 static boolean recursion_loop_element;
1152
1153 static int map_player_action[MAX_PLAYERS];
1154
1155
1156 // ----------------------------------------------------------------------------
1157 // definition of elements that automatically change to other elements after
1158 // a specified time, eventually calling a function when changing
1159 // ----------------------------------------------------------------------------
1160
1161 // forward declaration for changer functions
1162 static void InitBuggyBase(int, int);
1163 static void WarnBuggyBase(int, int);
1164
1165 static void InitTrap(int, int);
1166 static void ActivateTrap(int, int);
1167 static void ChangeActiveTrap(int, int);
1168
1169 static void InitRobotWheel(int, int);
1170 static void RunRobotWheel(int, int);
1171 static void StopRobotWheel(int, int);
1172
1173 static void InitTimegateWheel(int, int);
1174 static void RunTimegateWheel(int, int);
1175
1176 static void InitMagicBallDelay(int, int);
1177 static void ActivateMagicBall(int, int);
1178
1179 struct ChangingElementInfo
1180 {
1181   int element;
1182   int target_element;
1183   int change_delay;
1184   void (*pre_change_function)(int x, int y);
1185   void (*change_function)(int x, int y);
1186   void (*post_change_function)(int x, int y);
1187 };
1188
1189 static struct ChangingElementInfo change_delay_list[] =
1190 {
1191   {
1192     EL_NUT_BREAKING,
1193     EL_EMERALD,
1194     6,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_PEARL_BREAKING,
1201     EL_EMPTY,
1202     8,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EXIT_OPENING,
1209     EL_EXIT_OPEN,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_CLOSING,
1217     EL_EXIT_CLOSED,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_STEEL_EXIT_OPENING,
1225     EL_STEEL_EXIT_OPEN,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_CLOSING,
1233     EL_STEEL_EXIT_CLOSED,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EM_EXIT_OPENING,
1241     EL_EM_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_CLOSING,
1249     EL_EMPTY,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_OPENING,
1257     EL_EM_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_CLOSING,
1265     EL_STEELWALL,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_SP_EXIT_OPENING,
1273     EL_SP_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_CLOSING,
1281     EL_SP_EXIT_CLOSED,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SWITCHGATE_OPENING,
1289     EL_SWITCHGATE_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_CLOSING,
1297     EL_SWITCHGATE_CLOSED,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_TIMEGATE_OPENING,
1305     EL_TIMEGATE_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_CLOSING,
1313     EL_TIMEGATE_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319
1320   {
1321     EL_ACID_SPLASH_LEFT,
1322     EL_EMPTY,
1323     8,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_ACID_SPLASH_RIGHT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_SP_BUGGY_BASE,
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     0,
1340     InitBuggyBase,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE_ACTIVATING,
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVE,
1354     EL_SP_BUGGY_BASE,
1355     0,
1356     InitBuggyBase,
1357     WarnBuggyBase,
1358     NULL
1359   },
1360   {
1361     EL_TRAP,
1362     EL_TRAP_ACTIVE,
1363     0,
1364     InitTrap,
1365     NULL,
1366     ActivateTrap
1367   },
1368   {
1369     EL_TRAP_ACTIVE,
1370     EL_TRAP,
1371     31,
1372     NULL,
1373     ChangeActiveTrap,
1374     NULL
1375   },
1376   {
1377     EL_ROBOT_WHEEL_ACTIVE,
1378     EL_ROBOT_WHEEL,
1379     0,
1380     InitRobotWheel,
1381     RunRobotWheel,
1382     StopRobotWheel
1383   },
1384   {
1385     EL_TIMEGATE_SWITCH_ACTIVE,
1386     EL_TIMEGATE_SWITCH,
1387     0,
1388     InitTimegateWheel,
1389     RunTimegateWheel,
1390     NULL
1391   },
1392   {
1393     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394     EL_DC_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     EL_EMC_MAGIC_BALL_ACTIVE,
1403     0,
1404     InitMagicBallDelay,
1405     NULL,
1406     ActivateMagicBall
1407   },
1408   {
1409     EL_EMC_SPRING_BUMPER_ACTIVE,
1410     EL_EMC_SPRING_BUMPER,
1411     8,
1412     NULL,
1413     NULL,
1414     NULL
1415   },
1416   {
1417     EL_DIAGONAL_SHRINKING,
1418     EL_UNDEFINED,
1419     0,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_GROWING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL,
1431   },
1432
1433   {
1434     EL_UNDEFINED,
1435     EL_UNDEFINED,
1436     -1,
1437     NULL,
1438     NULL,
1439     NULL
1440   }
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int push_delay_fixed, push_delay_random;
1447 }
1448 push_delay_list[] =
1449 {
1450   { EL_SPRING,                  0, 0 },
1451   { EL_BALLOON,                 0, 0 },
1452
1453   { EL_SOKOBAN_OBJECT,          2, 0 },
1454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1455   { EL_SATELLITE,               2, 0 },
1456   { EL_SP_DISK_YELLOW,          2, 0 },
1457
1458   { EL_UNDEFINED,               0, 0 },
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int move_stepsize;
1465 }
1466 move_stepsize_list[] =
1467 {
1468   { EL_AMOEBA_DROP,             2 },
1469   { EL_AMOEBA_DROPPING,         2 },
1470   { EL_QUICKSAND_FILLING,       1 },
1471   { EL_QUICKSAND_EMPTYING,      1 },
1472   { EL_QUICKSAND_FAST_FILLING,  2 },
1473   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474   { EL_MAGIC_WALL_FILLING,      2 },
1475   { EL_MAGIC_WALL_EMPTYING,     2 },
1476   { EL_BD_MAGIC_WALL_FILLING,   2 },
1477   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1478   { EL_DC_MAGIC_WALL_FILLING,   2 },
1479   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1480
1481   { EL_UNDEFINED,               0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int count;
1488 }
1489 collect_count_list[] =
1490 {
1491   { EL_EMERALD,                 1 },
1492   { EL_BD_DIAMOND,              1 },
1493   { EL_EMERALD_YELLOW,          1 },
1494   { EL_EMERALD_RED,             1 },
1495   { EL_EMERALD_PURPLE,          1 },
1496   { EL_DIAMOND,                 3 },
1497   { EL_SP_INFOTRON,             1 },
1498   { EL_PEARL,                   5 },
1499   { EL_CRYSTAL,                 8 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int direction;
1508 }
1509 access_direction_list[] =
1510 {
1511   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1513   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1514   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1515   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1517   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1518   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1519   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1520   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1521   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1522
1523   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1524   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1525   { EL_SP_PORT_UP,                                                   MV_DOWN },
1526   { EL_SP_PORT_DOWN,                                         MV_UP           },
1527   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1528   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1529   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1531   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1532   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1533   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1534   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1535   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1536   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1537   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1538   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1539   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1540   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1541   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1542
1543   { EL_UNDEFINED,                       MV_NONE                              }
1544 };
1545
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547
1548 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1551                                  IS_JUST_CHANGING(x, y))
1552
1553 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1554
1555 // static variables for playfield scan mode (scanning forward or backward)
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1560
1561 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1562                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1563                                      (y) += playfield_scan_delta_y)     \
1564                                 for ((x) = playfield_scan_start_x;      \
1565                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1566                                      (x) += playfield_scan_delta_x)
1567
1568 #ifdef DEBUG
1569 void DEBUG_SetMaximumDynamite(void)
1570 {
1571   int i;
1572
1573   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575       local_player->inventory_element[local_player->inventory_size++] =
1576         EL_DYNAMITE;
1577 }
1578 #endif
1579
1580 static void InitPlayfieldScanModeVars(void)
1581 {
1582   if (game.use_reverse_scan_direction)
1583   {
1584     playfield_scan_start_x = lev_fieldx - 1;
1585     playfield_scan_start_y = lev_fieldy - 1;
1586
1587     playfield_scan_delta_x = -1;
1588     playfield_scan_delta_y = -1;
1589   }
1590   else
1591   {
1592     playfield_scan_start_x = 0;
1593     playfield_scan_start_y = 0;
1594
1595     playfield_scan_delta_x = 1;
1596     playfield_scan_delta_y = 1;
1597   }
1598 }
1599
1600 static void InitPlayfieldScanMode(int mode)
1601 {
1602   game.use_reverse_scan_direction =
1603     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604
1605   InitPlayfieldScanModeVars();
1606 }
1607
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1609 {
1610   move_stepsize =
1611     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612
1613   // make sure that stepsize value is always a power of 2
1614   move_stepsize = (1 << log_2(move_stepsize));
1615
1616   return TILEX / move_stepsize;
1617 }
1618
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620                                boolean init_game)
1621 {
1622   int player_nr = player->index_nr;
1623   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625
1626   // do no immediately change move delay -- the player might just be moving
1627   player->move_delay_value_next = move_delay;
1628
1629   // information if player can move must be set separately
1630   player->cannot_move = cannot_move;
1631
1632   if (init_game)
1633   {
1634     player->move_delay       = game.initial_move_delay[player_nr];
1635     player->move_delay_value = game.initial_move_delay_value[player_nr];
1636
1637     player->move_delay_value_next = -1;
1638
1639     player->move_delay_reset_counter = 0;
1640   }
1641 }
1642
1643 void GetPlayerConfig(void)
1644 {
1645   GameFrameDelay = setup.game_frame_delay;
1646
1647   if (!audio.sound_available)
1648     setup.sound_simple = FALSE;
1649
1650   if (!audio.loops_available)
1651     setup.sound_loops = FALSE;
1652
1653   if (!audio.music_available)
1654     setup.sound_music = FALSE;
1655
1656   if (!video.fullscreen_available)
1657     setup.fullscreen = FALSE;
1658
1659   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660
1661   SetAudioMode(setup.sound);
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void IncrementSokobanFieldsNeeded(void)
1691 {
1692   if (level.sb_fields_needed)
1693     game.sokoban_fields_still_needed++;
1694 }
1695
1696 static void IncrementSokobanObjectsNeeded(void)
1697 {
1698   if (level.sb_objects_needed)
1699     game.sokoban_objects_still_needed++;
1700 }
1701
1702 static void DecrementSokobanFieldsNeeded(void)
1703 {
1704   if (game.sokoban_fields_still_needed > 0)
1705     game.sokoban_fields_still_needed--;
1706 }
1707
1708 static void DecrementSokobanObjectsNeeded(void)
1709 {
1710   if (game.sokoban_objects_still_needed > 0)
1711     game.sokoban_objects_still_needed--;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Tile[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Tile[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     // ---------- initialize player's last field block delay ------------------
1751
1752     // always start with reliable default value (no adjustment needed)
1753     player->block_delay_adjustment = 0;
1754
1755     // special case 1: in Supaplex, Murphy blocks last field one more frame
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     // special case 2: in game engines before 3.1.1, blocking was different
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!network.enabled || player->connected_network)
1764     {
1765       player->active = TRUE;
1766
1767       // remove potentially duplicate players
1768       if (StorePlayer[jx][jy] == Tile[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Tile[x][y];
1772
1773 #if DEBUG_INIT_PLAYER
1774       Debug("game:init:player", "- player element %d activated",
1775             player->element_nr);
1776       Debug("game:init:player", "  (local player is %d and currently %s)",
1777             local_player->element_nr,
1778             local_player->active ? "active" : "not active");
1779     }
1780 #endif
1781
1782     Tile[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Tile[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Tile[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1832         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Tile[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Tile[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Tile[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Tile[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Tile[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error("'game_panel_controls' structure corrupted at %d", i);
2153
2154       Fail("this should not happen -- please debug");
2155     }
2156
2157     // force update of game controls after initialization
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     // determine panel value width for later calculation of alignment
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     // fill structure for game panel draw order
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   // sort game panel controls according to sort_priority and control number
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 static void UpdatePlayfieldElementCount(void)
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   // first check if it is needed at all to calculate playfield element count
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Tile[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 static void UpdateGameControlValues(void)
2213 {
2214   int i, k;
2215   int time = (game.LevelSolved ?
2216               game.LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               game_em.lev->time :
2219               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2220               game_sp.time_played :
2221               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2222               game_mm.energy_left :
2223               game.no_time_limit ? TimePlayed : TimeLeft);
2224   int score = (game.LevelSolved ?
2225                game.LevelSolved_CountingScore :
2226                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227                game_em.lev->score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2229                game_sp.score :
2230                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2231                game_mm.score :
2232                game.score);
2233   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234               game_em.lev->gems_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2236               game_sp.infotrons_still_needed :
2237               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2238               game_mm.kettles_still_needed :
2239               game.gems_still_needed);
2240   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241                      game_em.lev->gems_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243                      game_sp.infotrons_still_needed > 0 :
2244                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2245                      game_mm.kettles_still_needed > 0 ||
2246                      game_mm.lights_still_needed > 0 :
2247                      game.gems_still_needed > 0 ||
2248                      game.sokoban_fields_still_needed > 0 ||
2249                      game.sokoban_objects_still_needed > 0 ||
2250                      game.lights_still_needed > 0);
2251   int health = (game.LevelSolved ?
2252                 game.LevelSolved_CountingHealth :
2253                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2254                 MM_HEALTH(game_mm.laser_overload_value) :
2255                 game.health);
2256   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2257
2258   UpdatePlayfieldElementCount();
2259
2260   // update game panel control values
2261
2262   // used instead of "level_nr" (for network games)
2263   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2264   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2265
2266   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2267   for (i = 0; i < MAX_NUM_KEYS; i++)
2268     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2269   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2270   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2271
2272   if (game.centered_player_nr == -1)
2273   {
2274     for (i = 0; i < MAX_PLAYERS; i++)
2275     {
2276       // only one player in Supaplex game engine
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2278         break;
2279
2280       for (k = 0; k < MAX_NUM_KEYS; k++)
2281       {
2282         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283         {
2284           if (game_em.ply[i]->keys & (1 << k))
2285             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286               get_key_element_from_nr(k);
2287         }
2288         else if (stored_player[i].key[k])
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292
2293       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294         getPlayerInventorySize(i);
2295
2296       if (stored_player[i].num_white_keys > 0)
2297         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2298           EL_DC_KEY_WHITE;
2299
2300       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301         stored_player[i].num_white_keys;
2302     }
2303   }
2304   else
2305   {
2306     int player_nr = game.centered_player_nr;
2307
2308     for (k = 0; k < MAX_NUM_KEYS; k++)
2309     {
2310       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2311       {
2312         if (game_em.ply[player_nr]->keys & (1 << k))
2313           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314             get_key_element_from_nr(k);
2315       }
2316       else if (stored_player[player_nr].key[k])
2317         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318           get_key_element_from_nr(k);
2319     }
2320
2321     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2322       getPlayerInventorySize(player_nr);
2323
2324     if (stored_player[player_nr].num_white_keys > 0)
2325       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2326
2327     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328       stored_player[player_nr].num_white_keys;
2329   }
2330
2331   // re-arrange keys on game panel, if needed or if defined by style settings
2332   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2333   {
2334     int nr = GAME_PANEL_KEY_1 + i;
2335     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2336     struct TextPosInfo *pos = gpc->pos;
2337
2338     // skip check if key is not in the player's inventory
2339     if (gpc->value == EL_EMPTY)
2340       continue;
2341
2342     // check if keys should be arranged on panel from left to right
2343     if (pos->style == STYLE_LEFTMOST_POSITION)
2344     {
2345       // check previous key positions (left from current key)
2346       for (k = 0; k < i; k++)
2347       {
2348         int nr_new = GAME_PANEL_KEY_1 + k;
2349
2350         if (game_panel_controls[nr_new].value == EL_EMPTY)
2351         {
2352           game_panel_controls[nr_new].value = gpc->value;
2353           gpc->value = EL_EMPTY;
2354
2355           break;
2356         }
2357       }
2358     }
2359
2360     // check if "undefined" keys can be placed at some other position
2361     if (pos->x == -1 && pos->y == -1)
2362     {
2363       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2364
2365       // 1st try: display key at the same position as normal or EM keys
2366       if (game_panel_controls[nr_new].value == EL_EMPTY)
2367       {
2368         game_panel_controls[nr_new].value = gpc->value;
2369       }
2370       else
2371       {
2372         // 2nd try: display key at the next free position in the key panel
2373         for (k = 0; k < STD_NUM_KEYS; k++)
2374         {
2375           nr_new = GAME_PANEL_KEY_1 + k;
2376
2377           if (game_panel_controls[nr_new].value == EL_EMPTY)
2378           {
2379             game_panel_controls[nr_new].value = gpc->value;
2380
2381             break;
2382           }
2383         }
2384       }
2385     }
2386   }
2387
2388   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2389   {
2390     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, i);
2392     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2393       get_inventory_element_from_pos(local_player, -i - 1);
2394   }
2395
2396   game_panel_controls[GAME_PANEL_SCORE].value = score;
2397   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2398
2399   game_panel_controls[GAME_PANEL_TIME].value = time;
2400
2401   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2402   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2403   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2404
2405   if (level.time == 0)
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2407   else
2408     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2409
2410   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2411   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2412
2413   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2414
2415   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2416     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2417      EL_EMPTY);
2418   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2419     local_player->shield_normal_time_left;
2420   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2421     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2422      EL_EMPTY);
2423   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2424     local_player->shield_deadly_time_left;
2425
2426   game_panel_controls[GAME_PANEL_EXIT].value =
2427     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2428
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2431   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2432     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2433      EL_EMC_MAGIC_BALL_SWITCH);
2434
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2436     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2437   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2438     game.light_time_left;
2439
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2441     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2442   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2443     game.timegate_time_left;
2444
2445   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2446     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2447
2448   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2449     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2451     game.lenses_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2454     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2455   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2456     game.magnify_time_left;
2457
2458   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2459     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2460      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2461      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2462      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2463      EL_BALLOON_SWITCH_NONE);
2464
2465   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2466     local_player->dynabomb_count;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2468     local_player->dynabomb_size;
2469   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2470     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2471
2472   game_panel_controls[GAME_PANEL_PENGUINS].value =
2473     game.friends_still_needed;
2474
2475   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2476     game.sokoban_objects_still_needed;
2477   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2478     game.sokoban_fields_still_needed;
2479
2480   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2481     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2482
2483   for (i = 0; i < NUM_BELTS; i++)
2484   {
2485     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2486       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2487        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2488     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2489       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2490   }
2491
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2493     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2494   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2495     game.magic_wall_time_left;
2496
2497   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2498     local_player->gravity;
2499
2500   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2501     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2502
2503   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2504     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2505       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2506        game.panel.element[i].id : EL_UNDEFINED);
2507
2508   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2509     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2510       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2511        element_info[game.panel.element_count[i].id].element_count : 0);
2512
2513   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2514     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2515       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2516        element_info[game.panel.ce_score[i].id].collect_score : 0);
2517
2518   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2519     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2520       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2521        element_info[game.panel.ce_score_element[i].id].collect_score :
2522        EL_UNDEFINED);
2523
2524   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2525   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2526   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2527
2528   // update game panel control frames
2529
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531   {
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533
2534     if (gpc->type == TYPE_ELEMENT)
2535     {
2536       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2537       {
2538         int last_anim_random_frame = gfx.anim_random_frame;
2539         int element = gpc->value;
2540         int graphic = el2panelimg(element);
2541         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2542                                sync_random_frame : INIT_GFX_RANDOM());
2543
2544         if (gpc->value != gpc->last_value)
2545         {
2546           gpc->gfx_frame = 0;
2547           gpc->gfx_random = init_gfx_random;
2548         }
2549         else
2550         {
2551           gpc->gfx_frame++;
2552
2553           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2554               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2555             gpc->gfx_random = init_gfx_random;
2556         }
2557
2558         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2559           gfx.anim_random_frame = gpc->gfx_random;
2560
2561         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2562           gpc->gfx_frame = element_info[element].collect_score;
2563
2564         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = last_anim_random_frame;
2568       }
2569     }
2570     else if (gpc->type == TYPE_GRAPHIC)
2571     {
2572       if (gpc->graphic != IMG_UNDEFINED)
2573       {
2574         int last_anim_random_frame = gfx.anim_random_frame;
2575         int graphic = gpc->graphic;
2576         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2577                                sync_random_frame : INIT_GFX_RANDOM());
2578
2579         if (gpc->value != gpc->last_value)
2580         {
2581           gpc->gfx_frame = 0;
2582           gpc->gfx_random = init_gfx_random;
2583         }
2584         else
2585         {
2586           gpc->gfx_frame++;
2587
2588           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2589               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2590             gpc->gfx_random = init_gfx_random;
2591         }
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = gpc->gfx_random;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602   }
2603 }
2604
2605 static void DisplayGameControlValues(void)
2606 {
2607   boolean redraw_panel = FALSE;
2608   int i;
2609
2610   for (i = 0; game_panel_controls[i].nr != -1; i++)
2611   {
2612     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2613
2614     if (PANEL_DEACTIVATED(gpc->pos))
2615       continue;
2616
2617     if (gpc->value == gpc->last_value &&
2618         gpc->frame == gpc->last_frame)
2619       continue;
2620
2621     redraw_panel = TRUE;
2622   }
2623
2624   if (!redraw_panel)
2625     return;
2626
2627   // copy default game door content to main double buffer
2628
2629   // !!! CHECK AGAIN !!!
2630   SetPanelBackground();
2631   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2632   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2633
2634   // redraw game control buttons
2635   RedrawGameButtons();
2636
2637   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2638
2639   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2640   {
2641     int nr = game_panel_order[i].nr;
2642     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2643     struct TextPosInfo *pos = gpc->pos;
2644     int type = gpc->type;
2645     int value = gpc->value;
2646     int frame = gpc->frame;
2647     int size = pos->size;
2648     int font = pos->font;
2649     boolean draw_masked = pos->draw_masked;
2650     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2651
2652     if (PANEL_DEACTIVATED(pos))
2653       continue;
2654
2655     if (pos->class == get_hash_from_key("extra_panel_items") &&
2656         !setup.prefer_extra_panel_items)
2657       continue;
2658
2659     gpc->last_value = value;
2660     gpc->last_frame = frame;
2661
2662     if (type == TYPE_INTEGER)
2663     {
2664       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2665           nr == GAME_PANEL_TIME)
2666       {
2667         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2668
2669         if (use_dynamic_size)           // use dynamic number of digits
2670         {
2671           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2672           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2673           int size2 = size1 + 1;
2674           int font1 = pos->font;
2675           int font2 = pos->font_alt;
2676
2677           size = (value < value_change ? size1 : size2);
2678           font = (value < value_change ? font1 : font2);
2679         }
2680       }
2681
2682       // correct text size if "digits" is zero or less
2683       if (size <= 0)
2684         size = strlen(int2str(value, size));
2685
2686       // dynamically correct text alignment
2687       pos->width = size * getFontWidth(font);
2688
2689       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2690                   int2str(value, size), font, mask_mode);
2691     }
2692     else if (type == TYPE_ELEMENT)
2693     {
2694       int element, graphic;
2695       Bitmap *src_bitmap;
2696       int src_x, src_y;
2697       int width, height;
2698       int dst_x = PANEL_XPOS(pos);
2699       int dst_y = PANEL_YPOS(pos);
2700
2701       if (value != EL_UNDEFINED && value != EL_EMPTY)
2702       {
2703         element = value;
2704         graphic = el2panelimg(value);
2705
2706 #if 0
2707         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2708               element, EL_NAME(element), size);
2709 #endif
2710
2711         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2712           size = TILESIZE;
2713
2714         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2715                               &src_x, &src_y);
2716
2717         width  = graphic_info[graphic].width  * size / TILESIZE;
2718         height = graphic_info[graphic].height * size / TILESIZE;
2719
2720         if (draw_masked)
2721           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2722                            dst_x, dst_y);
2723         else
2724           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2725                      dst_x, dst_y);
2726       }
2727     }
2728     else if (type == TYPE_GRAPHIC)
2729     {
2730       int graphic        = gpc->graphic;
2731       int graphic_active = gpc->graphic_active;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2738                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2739
2740       if (graphic != IMG_UNDEFINED && !skip)
2741       {
2742         if (pos->style == STYLE_REVERSE)
2743           value = 100 - value;
2744
2745         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2746
2747         if (pos->direction & MV_HORIZONTAL)
2748         {
2749           width  = graphic_info[graphic_active].width * value / 100;
2750           height = graphic_info[graphic_active].height;
2751
2752           if (pos->direction == MV_LEFT)
2753           {
2754             src_x += graphic_info[graphic_active].width - width;
2755             dst_x += graphic_info[graphic_active].width - width;
2756           }
2757         }
2758         else
2759         {
2760           width  = graphic_info[graphic_active].width;
2761           height = graphic_info[graphic_active].height * value / 100;
2762
2763           if (pos->direction == MV_UP)
2764           {
2765             src_y += graphic_info[graphic_active].height - height;
2766             dst_y += graphic_info[graphic_active].height - height;
2767           }
2768         }
2769
2770         if (draw_masked)
2771           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2772                            dst_x, dst_y);
2773         else
2774           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2775                      dst_x, dst_y);
2776
2777         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2778
2779         if (pos->direction & MV_HORIZONTAL)
2780         {
2781           if (pos->direction == MV_RIGHT)
2782           {
2783             src_x += width;
2784             dst_x += width;
2785           }
2786           else
2787           {
2788             dst_x = PANEL_XPOS(pos);
2789           }
2790
2791           width = graphic_info[graphic].width - width;
2792         }
2793         else
2794         {
2795           if (pos->direction == MV_DOWN)
2796           {
2797             src_y += height;
2798             dst_y += height;
2799           }
2800           else
2801           {
2802             dst_y = PANEL_YPOS(pos);
2803           }
2804
2805           height = graphic_info[graphic].height - height;
2806         }
2807
2808         if (draw_masked)
2809           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2810                            dst_x, dst_y);
2811         else
2812           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2813                      dst_x, dst_y);
2814       }
2815     }
2816     else if (type == TYPE_STRING)
2817     {
2818       boolean active = (value != 0);
2819       char *state_normal = "off";
2820       char *state_active = "on";
2821       char *state = (active ? state_active : state_normal);
2822       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2823                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2824                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2825                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2826
2827       if (nr == GAME_PANEL_GRAVITY_STATE)
2828       {
2829         int font1 = pos->font;          // (used for normal state)
2830         int font2 = pos->font_alt;      // (used for active state)
2831
2832         font = (active ? font2 : font1);
2833       }
2834
2835       if (s != NULL)
2836       {
2837         char *s_cut;
2838
2839         if (size <= 0)
2840         {
2841           // don't truncate output if "chars" is zero or less
2842           size = strlen(s);
2843
2844           // dynamically correct text alignment
2845           pos->width = size * getFontWidth(font);
2846         }
2847
2848         s_cut = getStringCopyN(s, size);
2849
2850         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2851                     s_cut, font, mask_mode);
2852
2853         free(s_cut);
2854       }
2855     }
2856
2857     redraw_mask |= REDRAW_DOOR_1;
2858   }
2859
2860   SetGameStatus(GAME_MODE_PLAYING);
2861 }
2862
2863 void UpdateAndDisplayGameControlValues(void)
2864 {
2865   if (tape.deactivate_display)
2866     return;
2867
2868   UpdateGameControlValues();
2869   DisplayGameControlValues();
2870 }
2871
2872 #if 0
2873 static void UpdateGameDoorValues(void)
2874 {
2875   UpdateGameControlValues();
2876 }
2877 #endif
2878
2879 void DrawGameDoorValues(void)
2880 {
2881   DisplayGameControlValues();
2882 }
2883
2884
2885 // ============================================================================
2886 // InitGameEngine()
2887 // ----------------------------------------------------------------------------
2888 // initialize game engine due to level / tape version number
2889 // ============================================================================
2890
2891 static void InitGameEngine(void)
2892 {
2893   int i, j, k, l, x, y;
2894
2895   // set game engine from tape file when re-playing, else from level file
2896   game.engine_version = (tape.playing ? tape.engine_version :
2897                          level.game_version);
2898
2899   // set single or multi-player game mode (needed for re-playing tapes)
2900   game.team_mode = setup.team_mode;
2901
2902   if (tape.playing)
2903   {
2904     int num_players = 0;
2905
2906     for (i = 0; i < MAX_PLAYERS; i++)
2907       if (tape.player_participates[i])
2908         num_players++;
2909
2910     // multi-player tapes contain input data for more than one player
2911     game.team_mode = (num_players > 1);
2912   }
2913
2914 #if 0
2915   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2916         level.game_version);
2917   Debug("game:init:level", "          tape.file_version   == %06d",
2918         tape.file_version);
2919   Debug("game:init:level", "          tape.game_version   == %06d",
2920         tape.game_version);
2921   Debug("game:init:level", "          tape.engine_version == %06d",
2922         tape.engine_version);
2923   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2924         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2925 #endif
2926
2927   // --------------------------------------------------------------------------
2928   // set flags for bugs and changes according to active game engine version
2929   // --------------------------------------------------------------------------
2930
2931   /*
2932     Summary of bugfix:
2933     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2934
2935     Bug was introduced in version:
2936     2.0.1
2937
2938     Bug was fixed in version:
2939     4.2.0.0
2940
2941     Description:
2942     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2943     but the property "can fall" was missing, which caused some levels to be
2944     unsolvable. This was fixed in version 4.2.0.0.
2945
2946     Affected levels/tapes:
2947     An example for a tape that was fixed by this bugfix is tape 029 from the
2948     level set "rnd_sam_bateman".
2949     The wrong behaviour will still be used for all levels or tapes that were
2950     created/recorded with it. An example for this is tape 023 from the level
2951     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2952   */
2953
2954   boolean use_amoeba_dropping_cannot_fall_bug =
2955     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2956       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2957      (tape.playing &&
2958       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2959       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2960
2961   /*
2962     Summary of bugfix/change:
2963     Fixed move speed of elements entering or leaving magic wall.
2964
2965     Fixed/changed in version:
2966     2.0.1
2967
2968     Description:
2969     Before 2.0.1, move speed of elements entering or leaving magic wall was
2970     twice as fast as it is now.
2971     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2972
2973     Affected levels/tapes:
2974     The first condition is generally needed for all levels/tapes before version
2975     2.0.1, which might use the old behaviour before it was changed; known tapes
2976     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2977     The second condition is an exception from the above case and is needed for
2978     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2979     above, but before it was known that this change would break tapes like the
2980     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2981     although the engine version while recording maybe was before 2.0.1. There
2982     are a lot of tapes that are affected by this exception, like tape 006 from
2983     the level set "rnd_conor_mancone".
2984   */
2985
2986   boolean use_old_move_stepsize_for_magic_wall =
2987     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2988      !(tape.playing &&
2989        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2990        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2991
2992   /*
2993     Summary of bugfix/change:
2994     Fixed handling for custom elements that change when pushed by the player.
2995
2996     Fixed/changed in version:
2997     3.1.0
2998
2999     Description:
3000     Before 3.1.0, custom elements that "change when pushing" changed directly
3001     after the player started pushing them (until then handled in "DigField()").
3002     Since 3.1.0, these custom elements are not changed until the "pushing"
3003     move of the element is finished (now handled in "ContinueMoving()").
3004
3005     Affected levels/tapes:
3006     The first condition is generally needed for all levels/tapes before version
3007     3.1.0, which might use the old behaviour before it was changed; known tapes
3008     that are affected are some tapes from the level set "Walpurgis Gardens" by
3009     Jamie Cullen.
3010     The second condition is an exception from the above case and is needed for
3011     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3012     above (including some development versions of 3.1.0), but before it was
3013     known that this change would break tapes like the above and was fixed in
3014     3.1.1, so that the changed behaviour was active although the engine version
3015     while recording maybe was before 3.1.0. There is at least one tape that is
3016     affected by this exception, which is the tape for the one-level set "Bug
3017     Machine" by Juergen Bonhagen.
3018   */
3019
3020   game.use_change_when_pushing_bug =
3021     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3022      !(tape.playing &&
3023        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3024        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3025
3026   /*
3027     Summary of bugfix/change:
3028     Fixed handling for blocking the field the player leaves when moving.
3029
3030     Fixed/changed in version:
3031     3.1.1
3032
3033     Description:
3034     Before 3.1.1, when "block last field when moving" was enabled, the field
3035     the player is leaving when moving was blocked for the time of the move,
3036     and was directly unblocked afterwards. This resulted in the last field
3037     being blocked for exactly one less than the number of frames of one player
3038     move. Additionally, even when blocking was disabled, the last field was
3039     blocked for exactly one frame.
3040     Since 3.1.1, due to changes in player movement handling, the last field
3041     is not blocked at all when blocking is disabled. When blocking is enabled,
3042     the last field is blocked for exactly the number of frames of one player
3043     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3044     last field is blocked for exactly one more than the number of frames of
3045     one player move.
3046
3047     Affected levels/tapes:
3048     (!!! yet to be determined -- probably many !!!)
3049   */
3050
3051   game.use_block_last_field_bug =
3052     (game.engine_version < VERSION_IDENT(3,1,1,0));
3053
3054   /* various special flags and settings for native Emerald Mine game engine */
3055
3056   game_em.use_single_button =
3057     (game.engine_version > VERSION_IDENT(4,0,0,2));
3058
3059   game_em.use_snap_key_bug =
3060     (game.engine_version < VERSION_IDENT(4,0,1,0));
3061
3062   game_em.use_random_bug =
3063     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3064
3065   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3066
3067   game_em.use_old_explosions            = use_old_em_engine;
3068   game_em.use_old_android               = use_old_em_engine;
3069   game_em.use_old_push_elements         = use_old_em_engine;
3070   game_em.use_old_push_into_acid        = use_old_em_engine;
3071
3072   game_em.use_wrap_around               = !use_old_em_engine;
3073
3074   // --------------------------------------------------------------------------
3075
3076   // set maximal allowed number of custom element changes per game frame
3077   game.max_num_changes_per_frame = 1;
3078
3079   // default scan direction: scan playfield from top/left to bottom/right
3080   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3081
3082   // dynamically adjust element properties according to game engine version
3083   InitElementPropertiesEngine(game.engine_version);
3084
3085   // ---------- initialize special element properties -------------------------
3086
3087   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3088   if (use_amoeba_dropping_cannot_fall_bug)
3089     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3090
3091   // ---------- initialize player's initial move delay ------------------------
3092
3093   // dynamically adjust player properties according to level information
3094   for (i = 0; i < MAX_PLAYERS; i++)
3095     game.initial_move_delay_value[i] =
3096       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3097
3098   // dynamically adjust player properties according to game engine version
3099   for (i = 0; i < MAX_PLAYERS; i++)
3100     game.initial_move_delay[i] =
3101       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3102        game.initial_move_delay_value[i] : 0);
3103
3104   // ---------- initialize player's initial push delay ------------------------
3105
3106   // dynamically adjust player properties according to game engine version
3107   game.initial_push_delay_value =
3108     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3109
3110   // ---------- initialize changing elements ----------------------------------
3111
3112   // initialize changing elements information
3113   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3114   {
3115     struct ElementInfo *ei = &element_info[i];
3116
3117     // this pointer might have been changed in the level editor
3118     ei->change = &ei->change_page[0];
3119
3120     if (!IS_CUSTOM_ELEMENT(i))
3121     {
3122       ei->change->target_element = EL_EMPTY_SPACE;
3123       ei->change->delay_fixed = 0;
3124       ei->change->delay_random = 0;
3125       ei->change->delay_frames = 1;
3126     }
3127
3128     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3129     {
3130       ei->has_change_event[j] = FALSE;
3131
3132       ei->event_page_nr[j] = 0;
3133       ei->event_page[j] = &ei->change_page[0];
3134     }
3135   }
3136
3137   // add changing elements from pre-defined list
3138   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3139   {
3140     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3141     struct ElementInfo *ei = &element_info[ch_delay->element];
3142
3143     ei->change->target_element       = ch_delay->target_element;
3144     ei->change->delay_fixed          = ch_delay->change_delay;
3145
3146     ei->change->pre_change_function  = ch_delay->pre_change_function;
3147     ei->change->change_function      = ch_delay->change_function;
3148     ei->change->post_change_function = ch_delay->post_change_function;
3149
3150     ei->change->can_change = TRUE;
3151     ei->change->can_change_or_has_action = TRUE;
3152
3153     ei->has_change_event[CE_DELAY] = TRUE;
3154
3155     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3156     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3157   }
3158
3159   // ---------- initialize internal run-time variables ------------------------
3160
3161   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3162   {
3163     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3164
3165     for (j = 0; j < ei->num_change_pages; j++)
3166     {
3167       ei->change_page[j].can_change_or_has_action =
3168         (ei->change_page[j].can_change |
3169          ei->change_page[j].has_action);
3170     }
3171   }
3172
3173   // add change events from custom element configuration
3174   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3175   {
3176     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3177
3178     for (j = 0; j < ei->num_change_pages; j++)
3179     {
3180       if (!ei->change_page[j].can_change_or_has_action)
3181         continue;
3182
3183       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3184       {
3185         // only add event page for the first page found with this event
3186         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3187         {
3188           ei->has_change_event[k] = TRUE;
3189
3190           ei->event_page_nr[k] = j;
3191           ei->event_page[k] = &ei->change_page[j];
3192         }
3193       }
3194     }
3195   }
3196
3197   // ---------- initialize reference elements in change conditions ------------
3198
3199   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3200   {
3201     int element = EL_CUSTOM_START + i;
3202     struct ElementInfo *ei = &element_info[element];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       int trigger_element = ei->change_page[j].initial_trigger_element;
3207
3208       if (trigger_element >= EL_PREV_CE_8 &&
3209           trigger_element <= EL_NEXT_CE_8)
3210         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3211
3212       ei->change_page[j].trigger_element = trigger_element;
3213     }
3214   }
3215
3216   // ---------- initialize run-time trigger player and element ----------------
3217
3218   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3219   {
3220     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3221
3222     for (j = 0; j < ei->num_change_pages; j++)
3223     {
3224       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3225       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3226       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3227       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3228       ei->change_page[j].actual_trigger_ce_value = 0;
3229       ei->change_page[j].actual_trigger_ce_score = 0;
3230     }
3231   }
3232
3233   // ---------- initialize trigger events -------------------------------------
3234
3235   // initialize trigger events information
3236   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3237     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3238       trigger_events[i][j] = FALSE;
3239
3240   // add trigger events from element change event properties
3241   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3242   {
3243     struct ElementInfo *ei = &element_info[i];
3244
3245     for (j = 0; j < ei->num_change_pages; j++)
3246     {
3247       if (!ei->change_page[j].can_change_or_has_action)
3248         continue;
3249
3250       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3251       {
3252         int trigger_element = ei->change_page[j].trigger_element;
3253
3254         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3255         {
3256           if (ei->change_page[j].has_event[k])
3257           {
3258             if (IS_GROUP_ELEMENT(trigger_element))
3259             {
3260               struct ElementGroupInfo *group =
3261                 element_info[trigger_element].group;
3262
3263               for (l = 0; l < group->num_elements_resolved; l++)
3264                 trigger_events[group->element_resolved[l]][k] = TRUE;
3265             }
3266             else if (trigger_element == EL_ANY_ELEMENT)
3267               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3268                 trigger_events[l][k] = TRUE;
3269             else
3270               trigger_events[trigger_element][k] = TRUE;
3271           }
3272         }
3273       }
3274     }
3275   }
3276
3277   // ---------- initialize push delay -----------------------------------------
3278
3279   // initialize push delay values to default
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     if (!IS_CUSTOM_ELEMENT(i))
3283     {
3284       // set default push delay values (corrected since version 3.0.7-1)
3285       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3286       {
3287         element_info[i].push_delay_fixed = 2;
3288         element_info[i].push_delay_random = 8;
3289       }
3290       else
3291       {
3292         element_info[i].push_delay_fixed = 8;
3293         element_info[i].push_delay_random = 8;
3294       }
3295     }
3296   }
3297
3298   // set push delay value for certain elements from pre-defined list
3299   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3300   {
3301     int e = push_delay_list[i].element;
3302
3303     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3304     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3305   }
3306
3307   // set push delay value for Supaplex elements for newer engine versions
3308   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3309   {
3310     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     {
3312       if (IS_SP_ELEMENT(i))
3313       {
3314         // set SP push delay to just enough to push under a falling zonk
3315         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3316
3317         element_info[i].push_delay_fixed  = delay;
3318         element_info[i].push_delay_random = 0;
3319       }
3320     }
3321   }
3322
3323   // ---------- initialize move stepsize --------------------------------------
3324
3325   // initialize move stepsize values to default
3326   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327     if (!IS_CUSTOM_ELEMENT(i))
3328       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3329
3330   // set move stepsize value for certain elements from pre-defined list
3331   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3332   {
3333     int e = move_stepsize_list[i].element;
3334
3335     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3336
3337     // set move stepsize value for certain elements for older engine versions
3338     if (use_old_move_stepsize_for_magic_wall)
3339     {
3340       if (e == EL_MAGIC_WALL_FILLING ||
3341           e == EL_MAGIC_WALL_EMPTYING ||
3342           e == EL_BD_MAGIC_WALL_FILLING ||
3343           e == EL_BD_MAGIC_WALL_EMPTYING)
3344         element_info[e].move_stepsize *= 2;
3345     }
3346   }
3347
3348   // ---------- initialize collect score --------------------------------------
3349
3350   // initialize collect score values for custom elements from initial value
3351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3352     if (IS_CUSTOM_ELEMENT(i))
3353       element_info[i].collect_score = element_info[i].collect_score_initial;
3354
3355   // ---------- initialize collect count --------------------------------------
3356
3357   // initialize collect count values for non-custom elements
3358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359     if (!IS_CUSTOM_ELEMENT(i))
3360       element_info[i].collect_count_initial = 0;
3361
3362   // add collect count values for all elements from pre-defined list
3363   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3364     element_info[collect_count_list[i].element].collect_count_initial =
3365       collect_count_list[i].count;
3366
3367   // ---------- initialize access direction -----------------------------------
3368
3369   // initialize access direction values to default (access from every side)
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371     if (!IS_CUSTOM_ELEMENT(i))
3372       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3373
3374   // set access direction value for certain elements from pre-defined list
3375   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3376     element_info[access_direction_list[i].element].access_direction =
3377       access_direction_list[i].direction;
3378
3379   // ---------- initialize explosion content ----------------------------------
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381   {
3382     if (IS_CUSTOM_ELEMENT(i))
3383       continue;
3384
3385     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3386     {
3387       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3388
3389       element_info[i].content.e[x][y] =
3390         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3391          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3392          i == EL_PLAYER_3 ? EL_EMERALD :
3393          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3394          i == EL_MOLE ? EL_EMERALD_RED :
3395          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3396          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3397          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3398          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3399          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3400          i == EL_WALL_EMERALD ? EL_EMERALD :
3401          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3402          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3403          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3404          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3405          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3406          i == EL_WALL_PEARL ? EL_PEARL :
3407          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3408          EL_EMPTY);
3409     }
3410   }
3411
3412   // ---------- initialize recursion detection --------------------------------
3413   recursion_loop_depth = 0;
3414   recursion_loop_detected = FALSE;
3415   recursion_loop_element = EL_UNDEFINED;
3416
3417   // ---------- initialize graphics engine ------------------------------------
3418   game.scroll_delay_value =
3419     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3420      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3421      !setup.forced_scroll_delay           ? 0 :
3422      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3423   game.scroll_delay_value =
3424     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3425
3426   // ---------- initialize game engine snapshots ------------------------------
3427   for (i = 0; i < MAX_PLAYERS; i++)
3428     game.snapshot.last_action[i] = 0;
3429   game.snapshot.changed_action = FALSE;
3430   game.snapshot.collected_item = FALSE;
3431   game.snapshot.mode =
3432     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3433      SNAPSHOT_MODE_EVERY_STEP :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3435      SNAPSHOT_MODE_EVERY_MOVE :
3436      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3437      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3438   game.snapshot.save_snapshot = FALSE;
3439
3440   // ---------- initialize level time for Supaplex engine ---------------------
3441   // Supaplex levels with time limit currently unsupported -- should be added
3442   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3443     level.time = 0;
3444
3445   // ---------- initialize flags for handling game actions --------------------
3446
3447   // set flags for game actions to default values
3448   game.use_key_actions = TRUE;
3449   game.use_mouse_actions = FALSE;
3450
3451   // when using Mirror Magic game engine, handle mouse events only
3452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3453   {
3454     game.use_key_actions = FALSE;
3455     game.use_mouse_actions = TRUE;
3456   }
3457
3458   // check for custom elements with mouse click events
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3460   {
3461     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3462     {
3463       int element = EL_CUSTOM_START + i;
3464
3465       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3466           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3467           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3468           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3469         game.use_mouse_actions = TRUE;
3470     }
3471   }
3472 }
3473
3474 static int get_num_special_action(int element, int action_first,
3475                                   int action_last)
3476 {
3477   int num_special_action = 0;
3478   int i, j;
3479
3480   for (i = action_first; i <= action_last; i++)
3481   {
3482     boolean found = FALSE;
3483
3484     for (j = 0; j < NUM_DIRECTIONS; j++)
3485       if (el_act_dir2img(element, i, j) !=
3486           el_act_dir2img(element, ACTION_DEFAULT, j))
3487         found = TRUE;
3488
3489     if (found)
3490       num_special_action++;
3491     else
3492       break;
3493   }
3494
3495   return num_special_action;
3496 }
3497
3498
3499 // ============================================================================
3500 // InitGame()
3501 // ----------------------------------------------------------------------------
3502 // initialize and start new game
3503 // ============================================================================
3504
3505 #if DEBUG_INIT_PLAYER
3506 static void DebugPrintPlayerStatus(char *message)
3507 {
3508   int i;
3509
3510   if (!options.debug)
3511     return;
3512
3513   Debug("game:init:player", "%s:", message);
3514
3515   for (i = 0; i < MAX_PLAYERS; i++)
3516   {
3517     struct PlayerInfo *player = &stored_player[i];
3518
3519     Debug("game:init:player",
3520           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3521           i + 1,
3522           player->present,
3523           player->connected,
3524           player->connected_locally,
3525           player->connected_network,
3526           player->active,
3527           (local_player == player ? " (local player)" : ""));
3528   }
3529 }
3530 #endif
3531
3532 void InitGame(void)
3533 {
3534   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3535   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3536   int fade_mask = REDRAW_FIELD;
3537
3538   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3539   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3540   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3541   int initial_move_dir = MV_DOWN;
3542   int i, j, x, y;
3543
3544   // required here to update video display before fading (FIX THIS)
3545   DrawMaskedBorder(REDRAW_DOOR_2);
3546
3547   if (!game.restart_level)
3548     CloseDoor(DOOR_CLOSE_1);
3549
3550   SetGameStatus(GAME_MODE_PLAYING);
3551
3552   if (level_editor_test_game)
3553     FadeSkipNextFadeOut();
3554   else
3555     FadeSetEnterScreen();
3556
3557   if (CheckFadeAll())
3558     fade_mask = REDRAW_ALL;
3559
3560   FadeLevelSoundsAndMusic();
3561
3562   ExpireSoundLoops(TRUE);
3563
3564   FadeOut(fade_mask);
3565
3566   if (level_editor_test_game)
3567     FadeSkipNextFadeIn();
3568
3569   // needed if different viewport properties defined for playing
3570   ChangeViewportPropertiesIfNeeded();
3571
3572   ClearField();
3573
3574   DrawCompleteVideoDisplay();
3575
3576   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3577
3578   InitGameEngine();
3579   InitGameControlValues();
3580
3581   if (tape.recording)
3582   {
3583     // initialize tape actions from game when recording tape
3584     tape.use_key_actions   = game.use_key_actions;
3585     tape.use_mouse_actions = game.use_mouse_actions;
3586
3587     // initialize visible playfield size when recording tape (for team mode)
3588     tape.scr_fieldx = SCR_FIELDX;
3589     tape.scr_fieldy = SCR_FIELDY;
3590   }
3591
3592   // don't play tapes over network
3593   network_playing = (network.enabled && !tape.playing);
3594
3595   for (i = 0; i < MAX_PLAYERS; i++)
3596   {
3597     struct PlayerInfo *player = &stored_player[i];
3598
3599     player->index_nr = i;
3600     player->index_bit = (1 << i);
3601     player->element_nr = EL_PLAYER_1 + i;
3602
3603     player->present = FALSE;
3604     player->active = FALSE;
3605     player->mapped = FALSE;
3606
3607     player->killed = FALSE;
3608     player->reanimated = FALSE;
3609     player->buried = FALSE;
3610
3611     player->action = 0;
3612     player->effective_action = 0;
3613     player->programmed_action = 0;
3614     player->snap_action = 0;
3615
3616     player->mouse_action.lx = 0;
3617     player->mouse_action.ly = 0;
3618     player->mouse_action.button = 0;
3619     player->mouse_action.button_hint = 0;
3620
3621     player->effective_mouse_action.lx = 0;
3622     player->effective_mouse_action.ly = 0;
3623     player->effective_mouse_action.button = 0;
3624     player->effective_mouse_action.button_hint = 0;
3625
3626     for (j = 0; j < MAX_NUM_KEYS; j++)
3627       player->key[j] = FALSE;
3628
3629     player->num_white_keys = 0;
3630
3631     player->dynabomb_count = 0;
3632     player->dynabomb_size = 1;
3633     player->dynabombs_left = 0;
3634     player->dynabomb_xl = FALSE;
3635
3636     player->MovDir = initial_move_dir;
3637     player->MovPos = 0;
3638     player->GfxPos = 0;
3639     player->GfxDir = initial_move_dir;
3640     player->GfxAction = ACTION_DEFAULT;
3641     player->Frame = 0;
3642     player->StepFrame = 0;
3643
3644     player->initial_element = player->element_nr;
3645     player->artwork_element =
3646       (level.use_artwork_element[i] ? level.artwork_element[i] :
3647        player->element_nr);
3648     player->use_murphy = FALSE;
3649
3650     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3651     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3652
3653     player->gravity = level.initial_player_gravity[i];
3654
3655     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3656
3657     player->actual_frame_counter = 0;
3658
3659     player->step_counter = 0;
3660
3661     player->last_move_dir = initial_move_dir;
3662
3663     player->is_active = FALSE;
3664
3665     player->is_waiting = FALSE;
3666     player->is_moving = FALSE;
3667     player->is_auto_moving = FALSE;
3668     player->is_digging = FALSE;
3669     player->is_snapping = FALSE;
3670     player->is_collecting = FALSE;
3671     player->is_pushing = FALSE;
3672     player->is_switching = FALSE;
3673     player->is_dropping = FALSE;
3674     player->is_dropping_pressed = FALSE;
3675
3676     player->is_bored = FALSE;
3677     player->is_sleeping = FALSE;
3678
3679     player->was_waiting = TRUE;
3680     player->was_moving = FALSE;
3681     player->was_snapping = FALSE;
3682     player->was_dropping = FALSE;
3683
3684     player->force_dropping = FALSE;
3685
3686     player->frame_counter_bored = -1;
3687     player->frame_counter_sleeping = -1;
3688
3689     player->anim_delay_counter = 0;
3690     player->post_delay_counter = 0;
3691
3692     player->dir_waiting = initial_move_dir;
3693     player->action_waiting = ACTION_DEFAULT;
3694     player->last_action_waiting = ACTION_DEFAULT;
3695     player->special_action_bored = ACTION_DEFAULT;
3696     player->special_action_sleeping = ACTION_DEFAULT;
3697
3698     player->switch_x = -1;
3699     player->switch_y = -1;
3700
3701     player->drop_x = -1;
3702     player->drop_y = -1;
3703
3704     player->show_envelope = 0;
3705
3706     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3707
3708     player->push_delay       = -1;      // initialized when pushing starts
3709     player->push_delay_value = game.initial_push_delay_value;
3710
3711     player->drop_delay = 0;
3712     player->drop_pressed_delay = 0;
3713
3714     player->last_jx = -1;
3715     player->last_jy = -1;
3716     player->jx = -1;
3717     player->jy = -1;
3718
3719     player->shield_normal_time_left = 0;
3720     player->shield_deadly_time_left = 0;
3721
3722     player->last_removed_element = EL_UNDEFINED;
3723
3724     player->inventory_infinite_element = EL_UNDEFINED;
3725     player->inventory_size = 0;
3726
3727     if (level.use_initial_inventory[i])
3728     {
3729       for (j = 0; j < level.initial_inventory_size[i]; j++)
3730       {
3731         int element = level.initial_inventory_content[i][j];
3732         int collect_count = element_info[element].collect_count_initial;
3733         int k;
3734
3735         if (!IS_CUSTOM_ELEMENT(element))
3736           collect_count = 1;
3737
3738         if (collect_count == 0)
3739           player->inventory_infinite_element = element;
3740         else
3741           for (k = 0; k < collect_count; k++)
3742             if (player->inventory_size < MAX_INVENTORY_SIZE)
3743               player->inventory_element[player->inventory_size++] = element;
3744       }
3745     }
3746
3747     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3748     SnapField(player, 0, 0);
3749
3750     map_player_action[i] = i;
3751   }
3752
3753   network_player_action_received = FALSE;
3754
3755   // initial null action
3756   if (network_playing)
3757     SendToServer_MovePlayer(MV_NONE);
3758
3759   FrameCounter = 0;
3760   TimeFrames = 0;
3761   TimePlayed = 0;
3762   TimeLeft = level.time;
3763   TapeTime = 0;
3764
3765   ScreenMovDir = MV_NONE;
3766   ScreenMovPos = 0;
3767   ScreenGfxPos = 0;
3768
3769   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3770
3771   game.robot_wheel_x = -1;
3772   game.robot_wheel_y = -1;
3773
3774   game.exit_x = -1;
3775   game.exit_y = -1;
3776
3777   game.all_players_gone = FALSE;
3778
3779   game.LevelSolved = FALSE;
3780   game.GameOver = FALSE;
3781
3782   game.GamePlayed = !tape.playing;
3783
3784   game.LevelSolved_GameWon = FALSE;
3785   game.LevelSolved_GameEnd = FALSE;
3786   game.LevelSolved_SaveTape = FALSE;
3787   game.LevelSolved_SaveScore = FALSE;
3788
3789   game.LevelSolved_CountingTime = 0;
3790   game.LevelSolved_CountingScore = 0;
3791   game.LevelSolved_CountingHealth = 0;
3792
3793   game.panel.active = TRUE;
3794
3795   game.no_time_limit = (level.time == 0);
3796
3797   game.yamyam_content_nr = 0;
3798   game.robot_wheel_active = FALSE;
3799   game.magic_wall_active = FALSE;
3800   game.magic_wall_time_left = 0;
3801   game.light_time_left = 0;
3802   game.timegate_time_left = 0;
3803   game.switchgate_pos = 0;
3804   game.wind_direction = level.wind_direction_initial;
3805
3806   game.score = 0;
3807   game.score_final = 0;
3808
3809   game.health = MAX_HEALTH;
3810   game.health_final = MAX_HEALTH;
3811
3812   game.gems_still_needed = level.gems_needed;
3813   game.sokoban_fields_still_needed = 0;
3814   game.sokoban_objects_still_needed = 0;
3815   game.lights_still_needed = 0;
3816   game.players_still_needed = 0;
3817   game.friends_still_needed = 0;
3818
3819   game.lenses_time_left = 0;
3820   game.magnify_time_left = 0;
3821
3822   game.ball_active = level.ball_active_initial;
3823   game.ball_content_nr = 0;
3824
3825   game.explosions_delayed = TRUE;
3826
3827   game.envelope_active = FALSE;
3828
3829   for (i = 0; i < NUM_BELTS; i++)
3830   {
3831     game.belt_dir[i] = MV_NONE;
3832     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3833   }
3834
3835   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3836     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3837
3838 #if DEBUG_INIT_PLAYER
3839   DebugPrintPlayerStatus("Player status at level initialization");
3840 #endif
3841
3842   SCAN_PLAYFIELD(x, y)
3843   {
3844     Tile[x][y] = Last[x][y] = level.field[x][y];
3845     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3846     ChangeDelay[x][y] = 0;
3847     ChangePage[x][y] = -1;
3848     CustomValue[x][y] = 0;              // initialized in InitField()
3849     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3850     AmoebaNr[x][y] = 0;
3851     WasJustMoving[x][y] = 0;
3852     WasJustFalling[x][y] = 0;
3853     CheckCollision[x][y] = 0;
3854     CheckImpact[x][y] = 0;
3855     Stop[x][y] = FALSE;
3856     Pushed[x][y] = FALSE;
3857
3858     ChangeCount[x][y] = 0;
3859     ChangeEvent[x][y] = -1;
3860
3861     ExplodePhase[x][y] = 0;
3862     ExplodeDelay[x][y] = 0;
3863     ExplodeField[x][y] = EX_TYPE_NONE;
3864
3865     RunnerVisit[x][y] = 0;
3866     PlayerVisit[x][y] = 0;
3867
3868     GfxFrame[x][y] = 0;
3869     GfxRandom[x][y] = INIT_GFX_RANDOM();
3870     GfxElement[x][y] = EL_UNDEFINED;
3871     GfxAction[x][y] = ACTION_DEFAULT;
3872     GfxDir[x][y] = MV_NONE;
3873     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3874   }
3875
3876   SCAN_PLAYFIELD(x, y)
3877   {
3878     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3879       emulate_bd = FALSE;
3880     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3881       emulate_sb = FALSE;
3882     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3883       emulate_sp = FALSE;
3884
3885     InitField(x, y, TRUE);
3886
3887     ResetGfxAnimation(x, y);
3888   }
3889
3890   InitBeltMovement();
3891
3892   for (i = 0; i < MAX_PLAYERS; i++)
3893   {
3894     struct PlayerInfo *player = &stored_player[i];
3895
3896     // set number of special actions for bored and sleeping animation
3897     player->num_special_action_bored =
3898       get_num_special_action(player->artwork_element,
3899                              ACTION_BORING_1, ACTION_BORING_LAST);
3900     player->num_special_action_sleeping =
3901       get_num_special_action(player->artwork_element,
3902                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3903   }
3904
3905   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3906                     emulate_sb ? EMU_SOKOBAN :
3907                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3908
3909   // initialize type of slippery elements
3910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3911   {
3912     if (!IS_CUSTOM_ELEMENT(i))
3913     {
3914       // default: elements slip down either to the left or right randomly
3915       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3916
3917       // SP style elements prefer to slip down on the left side
3918       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3919         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3920
3921       // BD style elements prefer to slip down on the left side
3922       if (game.emulation == EMU_BOULDERDASH)
3923         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3924     }
3925   }
3926
3927   // initialize explosion and ignition delay
3928   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3929   {
3930     if (!IS_CUSTOM_ELEMENT(i))
3931     {
3932       int num_phase = 8;
3933       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3934                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3935                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3936       int last_phase = (num_phase + 1) * delay;
3937       int half_phase = (num_phase / 2) * delay;
3938
3939       element_info[i].explosion_delay = last_phase - 1;
3940       element_info[i].ignition_delay = half_phase;
3941
3942       if (i == EL_BLACK_ORB)
3943         element_info[i].ignition_delay = 1;
3944     }
3945   }
3946
3947   // correct non-moving belts to start moving left
3948   for (i = 0; i < NUM_BELTS; i++)
3949     if (game.belt_dir[i] == MV_NONE)
3950       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3951
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953   // use preferred player also in local single-player mode
3954   if (!network.enabled && !game.team_mode)
3955   {
3956     int new_index_nr = setup.network_player_nr;
3957
3958     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3959     {
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961         stored_player[i].connected_locally = FALSE;
3962
3963       stored_player[new_index_nr].connected_locally = TRUE;
3964     }
3965   }
3966
3967   for (i = 0; i < MAX_PLAYERS; i++)
3968   {
3969     stored_player[i].connected = FALSE;
3970
3971     // in network game mode, the local player might not be the first player
3972     if (stored_player[i].connected_locally)
3973       local_player = &stored_player[i];
3974   }
3975
3976   if (!network.enabled)
3977     local_player->connected = TRUE;
3978
3979   if (tape.playing)
3980   {
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       stored_player[i].connected = tape.player_participates[i];
3983   }
3984   else if (network.enabled)
3985   {
3986     // add team mode players connected over the network (needed for correct
3987     // assignment of player figures from level to locally playing players)
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       if (stored_player[i].connected_network)
3991         stored_player[i].connected = TRUE;
3992   }
3993   else if (game.team_mode)
3994   {
3995     // try to guess locally connected team mode players (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (setup.input[i].use_joystick ||
4000           setup.input[i].key.left != KSYM_UNDEFINED)
4001         stored_player[i].connected = TRUE;
4002   }
4003
4004 #if DEBUG_INIT_PLAYER
4005   DebugPrintPlayerStatus("Player status after level initialization");
4006 #endif
4007
4008 #if DEBUG_INIT_PLAYER
4009   Debug("game:init:player", "Reassigning players ...");
4010 #endif
4011
4012   // check if any connected player was not found in playfield
4013   for (i = 0; i < MAX_PLAYERS; i++)
4014   {
4015     struct PlayerInfo *player = &stored_player[i];
4016
4017     if (player->connected && !player->present)
4018     {
4019       struct PlayerInfo *field_player = NULL;
4020
4021 #if DEBUG_INIT_PLAYER
4022       Debug("game:init:player",
4023             "- looking for field player for player %d ...", i + 1);
4024 #endif
4025
4026       // assign first free player found that is present in the playfield
4027
4028       // first try: look for unmapped playfield player that is not connected
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped &&
4033             !stored_player[j].connected)
4034           field_player = &stored_player[j];
4035
4036       // second try: look for *any* unmapped playfield player
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped)
4041           field_player = &stored_player[j];
4042
4043       if (field_player != NULL)
4044       {
4045         int jx = field_player->jx, jy = field_player->jy;
4046
4047 #if DEBUG_INIT_PLAYER
4048         Debug("game:init:player", "- found player %d",
4049               field_player->index_nr + 1);
4050 #endif
4051
4052         player->present = FALSE;
4053         player->active = FALSE;
4054
4055         field_player->present = TRUE;
4056         field_player->active = TRUE;
4057
4058         /*
4059         player->initial_element = field_player->initial_element;
4060         player->artwork_element = field_player->artwork_element;
4061
4062         player->block_last_field       = field_player->block_last_field;
4063         player->block_delay_adjustment = field_player->block_delay_adjustment;
4064         */
4065
4066         StorePlayer[jx][jy] = field_player->element_nr;
4067
4068         field_player->jx = field_player->last_jx = jx;
4069         field_player->jy = field_player->last_jy = jy;
4070
4071         if (local_player == player)
4072           local_player = field_player;
4073
4074         map_player_action[field_player->index_nr] = i;
4075
4076         field_player->mapped = TRUE;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- map_player_action[%d] == %d",
4080               field_player->index_nr + 1, i + 1);
4081 #endif
4082       }
4083     }
4084
4085     if (player->connected && player->present)
4086       player->mapped = TRUE;
4087   }
4088
4089 #if DEBUG_INIT_PLAYER
4090   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4091 #endif
4092
4093 #else
4094
4095   // check if any connected player was not found in playfield
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103       {
4104         struct PlayerInfo *field_player = &stored_player[j];
4105         int jx = field_player->jx, jy = field_player->jy;
4106
4107         // assign first free player found that is present in the playfield
4108         if (field_player->present && !field_player->connected)
4109         {
4110           player->present = TRUE;
4111           player->active = TRUE;
4112
4113           field_player->present = FALSE;
4114           field_player->active = FALSE;
4115
4116           player->initial_element = field_player->initial_element;
4117           player->artwork_element = field_player->artwork_element;
4118
4119           player->block_last_field       = field_player->block_last_field;
4120           player->block_delay_adjustment = field_player->block_delay_adjustment;
4121
4122           StorePlayer[jx][jy] = player->element_nr;
4123
4124           player->jx = player->last_jx = jx;
4125           player->jy = player->last_jy = jy;
4126
4127           break;
4128         }
4129       }
4130     }
4131   }
4132 #endif
4133
4134 #if 0
4135   Debug("game:init:player", "local_player->present == %d",
4136         local_player->present);
4137 #endif
4138
4139   // set focus to local player for network games, else to all players
4140   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4141   game.centered_player_nr_next = game.centered_player_nr;
4142   game.set_centered_player = FALSE;
4143   game.set_centered_player_wrap = FALSE;
4144
4145   if (network_playing && tape.recording)
4146   {
4147     // store client dependent player focus when recording network games
4148     tape.centered_player_nr_next = game.centered_player_nr_next;
4149     tape.set_centered_player = TRUE;
4150   }
4151
4152   if (tape.playing)
4153   {
4154     // when playing a tape, eliminate all players who do not participate
4155
4156 #if USE_NEW_PLAYER_ASSIGNMENTS
4157
4158     if (!game.team_mode)
4159     {
4160       for (i = 0; i < MAX_PLAYERS; i++)
4161       {
4162         if (stored_player[i].active &&
4163             !tape.player_participates[map_player_action[i]])
4164         {
4165           struct PlayerInfo *player = &stored_player[i];
4166           int jx = player->jx, jy = player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169           Debug("game:init:player", "Removing player %d at (%d, %d)",
4170                 i + 1, jx, jy);
4171 #endif
4172
4173           player->active = FALSE;
4174           StorePlayer[jx][jy] = 0;
4175           Tile[jx][jy] = EL_EMPTY;
4176         }
4177       }
4178     }
4179
4180 #else
4181
4182     for (i = 0; i < MAX_PLAYERS; i++)
4183     {
4184       if (stored_player[i].active &&
4185           !tape.player_participates[i])
4186       {
4187         struct PlayerInfo *player = &stored_player[i];
4188         int jx = player->jx, jy = player->jy;
4189
4190         player->active = FALSE;
4191         StorePlayer[jx][jy] = 0;
4192         Tile[jx][jy] = EL_EMPTY;
4193       }
4194     }
4195 #endif
4196   }
4197   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4198   {
4199     // when in single player mode, eliminate all but the local player
4200
4201     for (i = 0; i < MAX_PLAYERS; i++)
4202     {
4203       struct PlayerInfo *player = &stored_player[i];
4204
4205       if (player->active && player != local_player)
4206       {
4207         int jx = player->jx, jy = player->jy;
4208
4209         player->active = FALSE;
4210         player->present = FALSE;
4211
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216   }
4217
4218   for (i = 0; i < MAX_PLAYERS; i++)
4219     if (stored_player[i].active)
4220       game.players_still_needed++;
4221
4222   if (level.solved_by_one_player)
4223     game.players_still_needed = 1;
4224
4225   // when recording the game, store which players take part in the game
4226   if (tape.recording)
4227   {
4228 #if USE_NEW_PLAYER_ASSIGNMENTS
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230       if (stored_player[i].connected)
4231         tape.player_participates[i] = TRUE;
4232 #else
4233     for (i = 0; i < MAX_PLAYERS; i++)
4234       if (stored_player[i].active)
4235         tape.player_participates[i] = TRUE;
4236 #endif
4237   }
4238
4239 #if DEBUG_INIT_PLAYER
4240   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4241 #endif
4242
4243   if (BorderElement == EL_EMPTY)
4244   {
4245     SBX_Left = 0;
4246     SBX_Right = lev_fieldx - SCR_FIELDX;
4247     SBY_Upper = 0;
4248     SBY_Lower = lev_fieldy - SCR_FIELDY;
4249   }
4250   else
4251   {
4252     SBX_Left = -1;
4253     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4254     SBY_Upper = -1;
4255     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4256   }
4257
4258   if (full_lev_fieldx <= SCR_FIELDX)
4259     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4260   if (full_lev_fieldy <= SCR_FIELDY)
4261     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4262
4263   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4264     SBX_Left--;
4265   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4266     SBY_Upper--;
4267
4268   // if local player not found, look for custom element that might create
4269   // the player (make some assumptions about the right custom element)
4270   if (!local_player->present)
4271   {
4272     int start_x = 0, start_y = 0;
4273     int found_rating = 0;
4274     int found_element = EL_UNDEFINED;
4275     int player_nr = local_player->index_nr;
4276
4277     SCAN_PLAYFIELD(x, y)
4278     {
4279       int element = Tile[x][y];
4280       int content;
4281       int xx, yy;
4282       boolean is_player;
4283
4284       if (level.use_start_element[player_nr] &&
4285           level.start_element[player_nr] == element &&
4286           found_rating < 4)
4287       {
4288         start_x = x;
4289         start_y = y;
4290
4291         found_rating = 4;
4292         found_element = element;
4293       }
4294
4295       if (!IS_CUSTOM_ELEMENT(element))
4296         continue;
4297
4298       if (CAN_CHANGE(element))
4299       {
4300         for (i = 0; i < element_info[element].num_change_pages; i++)
4301         {
4302           // check for player created from custom element as single target
4303           content = element_info[element].change_page[i].target_element;
4304           is_player = ELEM_IS_PLAYER(content);
4305
4306           if (is_player && (found_rating < 3 ||
4307                             (found_rating == 3 && element < found_element)))
4308           {
4309             start_x = x;
4310             start_y = y;
4311
4312             found_rating = 3;
4313             found_element = element;
4314           }
4315         }
4316       }
4317
4318       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4319       {
4320         // check for player created from custom element as explosion content
4321         content = element_info[element].content.e[xx][yy];
4322         is_player = ELEM_IS_PLAYER(content);
4323
4324         if (is_player && (found_rating < 2 ||
4325                           (found_rating == 2 && element < found_element)))
4326         {
4327           start_x = x + xx - 1;
4328           start_y = y + yy - 1;
4329
4330           found_rating = 2;
4331           found_element = element;
4332         }
4333
4334         if (!CAN_CHANGE(element))
4335           continue;
4336
4337         for (i = 0; i < element_info[element].num_change_pages; i++)
4338         {
4339           // check for player created from custom element as extended target
4340           content =
4341             element_info[element].change_page[i].target_content.e[xx][yy];
4342
4343           is_player = ELEM_IS_PLAYER(content);
4344
4345           if (is_player && (found_rating < 1 ||
4346                             (found_rating == 1 && element < found_element)))
4347           {
4348             start_x = x + xx - 1;
4349             start_y = y + yy - 1;
4350
4351             found_rating = 1;
4352             found_element = element;
4353           }
4354         }
4355       }
4356     }
4357
4358     scroll_x = SCROLL_POSITION_X(start_x);
4359     scroll_y = SCROLL_POSITION_Y(start_y);
4360   }
4361   else
4362   {
4363     scroll_x = SCROLL_POSITION_X(local_player->jx);
4364     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4365   }
4366
4367   // !!! FIX THIS (START) !!!
4368   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4369   {
4370     InitGameEngine_EM();
4371   }
4372   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4373   {
4374     InitGameEngine_SP();
4375   }
4376   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4377   {
4378     InitGameEngine_MM();
4379   }
4380   else
4381   {
4382     DrawLevel(REDRAW_FIELD);
4383     DrawAllPlayers();
4384
4385     // after drawing the level, correct some elements
4386     if (game.timegate_time_left == 0)
4387       CloseAllOpenTimegates();
4388   }
4389
4390   // blit playfield from scroll buffer to normal back buffer for fading in
4391   BlitScreenToBitmap(backbuffer);
4392   // !!! FIX THIS (END) !!!
4393
4394   DrawMaskedBorder(fade_mask);
4395
4396   FadeIn(fade_mask);
4397
4398 #if 1
4399   // full screen redraw is required at this point in the following cases:
4400   // - special editor door undrawn when game was started from level editor
4401   // - drawing area (playfield) was changed and has to be removed completely
4402   redraw_mask = REDRAW_ALL;
4403   BackToFront();
4404 #endif
4405
4406   if (!game.restart_level)
4407   {
4408     // copy default game door content to main double buffer
4409
4410     // !!! CHECK AGAIN !!!
4411     SetPanelBackground();
4412     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4413     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4414   }
4415
4416   SetPanelBackground();
4417   SetDrawBackgroundMask(REDRAW_DOOR_1);
4418
4419   UpdateAndDisplayGameControlValues();
4420
4421   if (!game.restart_level)
4422   {
4423     UnmapGameButtons();
4424     UnmapTapeButtons();
4425
4426     FreeGameButtons();
4427     CreateGameButtons();
4428
4429     MapGameButtons();
4430     MapTapeButtons();
4431
4432     // copy actual game door content to door double buffer for OpenDoor()
4433     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4434
4435     OpenDoor(DOOR_OPEN_ALL);
4436
4437     KeyboardAutoRepeatOffUnlessAutoplay();
4438
4439 #if DEBUG_INIT_PLAYER
4440     DebugPrintPlayerStatus("Player status (final)");
4441 #endif
4442   }
4443
4444   UnmapAllGadgets();
4445
4446   MapGameButtons();
4447   MapTapeButtons();
4448
4449   if (!game.restart_level && !tape.playing)
4450   {
4451     LevelStats_incPlayed(level_nr);
4452
4453     SaveLevelSetup_SeriesInfo();
4454   }
4455
4456   game.restart_level = FALSE;
4457   game.restart_game_message = NULL;
4458
4459   game.request_active = FALSE;
4460   game.request_active_or_moving = FALSE;
4461
4462   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4463     InitGameActions_MM();
4464
4465   SaveEngineSnapshotToListInitial();
4466
4467   if (!game.restart_level)
4468   {
4469     PlaySound(SND_GAME_STARTING);
4470
4471     if (setup.sound_music)
4472       PlayLevelMusic();
4473   }
4474 }
4475
4476 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4477                         int actual_player_x, int actual_player_y)
4478 {
4479   // this is used for non-R'n'D game engines to update certain engine values
4480
4481   // needed to determine if sounds are played within the visible screen area
4482   scroll_x = actual_scroll_x;
4483   scroll_y = actual_scroll_y;
4484
4485   // needed to get player position for "follow finger" playing input method
4486   local_player->jx = actual_player_x;
4487   local_player->jy = actual_player_y;
4488 }
4489
4490 void InitMovDir(int x, int y)
4491 {
4492   int i, element = Tile[x][y];
4493   static int xy[4][2] =
4494   {
4495     {  0, +1 },
4496     { +1,  0 },
4497     {  0, -1 },
4498     { -1,  0 }
4499   };
4500   static int direction[3][4] =
4501   {
4502     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4503     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4504     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4505   };
4506
4507   switch (element)
4508   {
4509     case EL_BUG_RIGHT:
4510     case EL_BUG_UP:
4511     case EL_BUG_LEFT:
4512     case EL_BUG_DOWN:
4513       Tile[x][y] = EL_BUG;
4514       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4515       break;
4516
4517     case EL_SPACESHIP_RIGHT:
4518     case EL_SPACESHIP_UP:
4519     case EL_SPACESHIP_LEFT:
4520     case EL_SPACESHIP_DOWN:
4521       Tile[x][y] = EL_SPACESHIP;
4522       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4523       break;
4524
4525     case EL_BD_BUTTERFLY_RIGHT:
4526     case EL_BD_BUTTERFLY_UP:
4527     case EL_BD_BUTTERFLY_LEFT:
4528     case EL_BD_BUTTERFLY_DOWN:
4529       Tile[x][y] = EL_BD_BUTTERFLY;
4530       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4531       break;
4532
4533     case EL_BD_FIREFLY_RIGHT:
4534     case EL_BD_FIREFLY_UP:
4535     case EL_BD_FIREFLY_LEFT:
4536     case EL_BD_FIREFLY_DOWN:
4537       Tile[x][y] = EL_BD_FIREFLY;
4538       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4539       break;
4540
4541     case EL_PACMAN_RIGHT:
4542     case EL_PACMAN_UP:
4543     case EL_PACMAN_LEFT:
4544     case EL_PACMAN_DOWN:
4545       Tile[x][y] = EL_PACMAN;
4546       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4547       break;
4548
4549     case EL_YAMYAM_LEFT:
4550     case EL_YAMYAM_RIGHT:
4551     case EL_YAMYAM_UP:
4552     case EL_YAMYAM_DOWN:
4553       Tile[x][y] = EL_YAMYAM;
4554       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4555       break;
4556
4557     case EL_SP_SNIKSNAK:
4558       MovDir[x][y] = MV_UP;
4559       break;
4560
4561     case EL_SP_ELECTRON:
4562       MovDir[x][y] = MV_LEFT;
4563       break;
4564
4565     case EL_MOLE_LEFT:
4566     case EL_MOLE_RIGHT:
4567     case EL_MOLE_UP:
4568     case EL_MOLE_DOWN:
4569       Tile[x][y] = EL_MOLE;
4570       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4571       break;
4572
4573     case EL_SPRING_LEFT:
4574     case EL_SPRING_RIGHT:
4575       Tile[x][y] = EL_SPRING;
4576       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4577       break;
4578
4579     default:
4580       if (IS_CUSTOM_ELEMENT(element))
4581       {
4582         struct ElementInfo *ei = &element_info[element];
4583         int move_direction_initial = ei->move_direction_initial;
4584         int move_pattern = ei->move_pattern;
4585
4586         if (move_direction_initial == MV_START_PREVIOUS)
4587         {
4588           if (MovDir[x][y] != MV_NONE)
4589             return;
4590
4591           move_direction_initial = MV_START_AUTOMATIC;
4592         }
4593
4594         if (move_direction_initial == MV_START_RANDOM)
4595           MovDir[x][y] = 1 << RND(4);
4596         else if (move_direction_initial & MV_ANY_DIRECTION)
4597           MovDir[x][y] = move_direction_initial;
4598         else if (move_pattern == MV_ALL_DIRECTIONS ||
4599                  move_pattern == MV_TURNING_LEFT ||
4600                  move_pattern == MV_TURNING_RIGHT ||
4601                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4602                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4603                  move_pattern == MV_TURNING_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_pattern == MV_HORIZONTAL)
4606           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4607         else if (move_pattern == MV_VERTICAL)
4608           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4609         else if (move_pattern & MV_ANY_DIRECTION)
4610           MovDir[x][y] = element_info[element].move_pattern;
4611         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4612                  move_pattern == MV_ALONG_RIGHT_SIDE)
4613         {
4614           // use random direction as default start direction
4615           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4616             MovDir[x][y] = 1 << RND(4);
4617
4618           for (i = 0; i < NUM_DIRECTIONS; i++)
4619           {
4620             int x1 = x + xy[i][0];
4621             int y1 = y + xy[i][1];
4622
4623             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4624             {
4625               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4626                 MovDir[x][y] = direction[0][i];
4627               else
4628                 MovDir[x][y] = direction[1][i];
4629
4630               break;
4631             }
4632           }
4633         }                
4634       }
4635       else
4636       {
4637         MovDir[x][y] = 1 << RND(4);
4638
4639         if (element != EL_BUG &&
4640             element != EL_SPACESHIP &&
4641             element != EL_BD_BUTTERFLY &&
4642             element != EL_BD_FIREFLY)
4643           break;
4644
4645         for (i = 0; i < NUM_DIRECTIONS; i++)
4646         {
4647           int x1 = x + xy[i][0];
4648           int y1 = y + xy[i][1];
4649
4650           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4651           {
4652             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4653             {
4654               MovDir[x][y] = direction[0][i];
4655               break;
4656             }
4657             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4658                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4659             {
4660               MovDir[x][y] = direction[1][i];
4661               break;
4662             }
4663           }
4664         }
4665       }
4666       break;
4667   }
4668
4669   GfxDir[x][y] = MovDir[x][y];
4670 }
4671
4672 void InitAmoebaNr(int x, int y)
4673 {
4674   int i;
4675   int group_nr = AmoebaNeighbourNr(x, y);
4676
4677   if (group_nr == 0)
4678   {
4679     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4680     {
4681       if (AmoebaCnt[i] == 0)
4682       {
4683         group_nr = i;
4684         break;
4685       }
4686     }
4687   }
4688
4689   AmoebaNr[x][y] = group_nr;
4690   AmoebaCnt[group_nr]++;
4691   AmoebaCnt2[group_nr]++;
4692 }
4693
4694 static void LevelSolved(void)
4695 {
4696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4697       game.players_still_needed > 0)
4698     return;
4699
4700   game.LevelSolved = TRUE;
4701   game.GameOver = TRUE;
4702
4703   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4704                       game_em.lev->score :
4705                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4706                       game_mm.score :
4707                       game.score);
4708   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4709                        MM_HEALTH(game_mm.laser_overload_value) :
4710                        game.health);
4711
4712   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4713   game.LevelSolved_CountingScore = game.score_final;
4714   game.LevelSolved_CountingHealth = game.health_final;
4715 }
4716
4717 void GameWon(void)
4718 {
4719   static int time_count_steps;
4720   static int time, time_final;
4721   static int score, score_final;
4722   static int health, health_final;
4723   static int game_over_delay_1 = 0;
4724   static int game_over_delay_2 = 0;
4725   static int game_over_delay_3 = 0;
4726   int game_over_delay_value_1 = 50;
4727   int game_over_delay_value_2 = 25;
4728   int game_over_delay_value_3 = 50;
4729
4730   if (!game.LevelSolved_GameWon)
4731   {
4732     int i;
4733
4734     // do not start end game actions before the player stops moving (to exit)
4735     if (local_player->active && local_player->MovPos)
4736       return;
4737
4738     game.LevelSolved_GameWon = TRUE;
4739     game.LevelSolved_SaveTape = tape.recording;
4740     game.LevelSolved_SaveScore = !tape.playing;
4741
4742     if (!tape.playing)
4743     {
4744       LevelStats_incSolved(level_nr);
4745
4746       SaveLevelSetup_SeriesInfo();
4747     }
4748
4749     if (tape.auto_play)         // tape might already be stopped here
4750       tape.auto_play_level_solved = TRUE;
4751
4752     TapeStop();
4753
4754     game_over_delay_1 = 0;
4755     game_over_delay_2 = 0;
4756     game_over_delay_3 = game_over_delay_value_3;
4757
4758     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4759     score = score_final = game.score_final;
4760     health = health_final = game.health_final;
4761
4762     if (level.score[SC_TIME_BONUS] > 0)
4763     {
4764       if (TimeLeft > 0)
4765       {
4766         time_final = 0;
4767         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4768       }
4769       else if (game.no_time_limit && TimePlayed < 999)
4770       {
4771         time_final = 999;
4772         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4773       }
4774
4775       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4776
4777       game_over_delay_1 = game_over_delay_value_1;
4778
4779       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4780       {
4781         health_final = 0;
4782         score_final += health * level.score[SC_TIME_BONUS];
4783
4784         game_over_delay_2 = game_over_delay_value_2;
4785       }
4786
4787       game.score_final = score_final;
4788       game.health_final = health_final;
4789     }
4790
4791     if (level_editor_test_game)
4792     {
4793       time = time_final;
4794       score = score_final;
4795
4796       game.LevelSolved_CountingTime = time;
4797       game.LevelSolved_CountingScore = score;
4798
4799       game_panel_controls[GAME_PANEL_TIME].value = time;
4800       game_panel_controls[GAME_PANEL_SCORE].value = score;
4801
4802       DisplayGameControlValues();
4803     }
4804
4805     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4806     {
4807       // check if last player has left the level
4808       if (game.exit_x >= 0 &&
4809           game.exit_y >= 0)
4810       {
4811         int x = game.exit_x;
4812         int y = game.exit_y;
4813         int element = Tile[x][y];
4814
4815         // close exit door after last player
4816         if ((game.all_players_gone &&
4817              (element == EL_EXIT_OPEN ||
4818               element == EL_SP_EXIT_OPEN ||
4819               element == EL_STEEL_EXIT_OPEN)) ||
4820             element == EL_EM_EXIT_OPEN ||
4821             element == EL_EM_STEEL_EXIT_OPEN)
4822         {
4823
4824           Tile[x][y] =
4825             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4826              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4827              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4828              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4829              EL_EM_STEEL_EXIT_CLOSING);
4830
4831           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4832         }
4833
4834         // player disappears
4835         DrawLevelField(x, y);
4836       }
4837
4838       for (i = 0; i < MAX_PLAYERS; i++)
4839       {
4840         struct PlayerInfo *player = &stored_player[i];
4841
4842         if (player->present)
4843         {
4844           RemovePlayer(player);
4845
4846           // player disappears
4847           DrawLevelField(player->jx, player->jy);
4848         }
4849       }
4850     }
4851
4852     PlaySound(SND_GAME_WINNING);
4853   }
4854
4855   if (game_over_delay_1 > 0)
4856   {
4857     game_over_delay_1--;
4858
4859     return;
4860   }
4861
4862   if (time != time_final)
4863   {
4864     int time_to_go = ABS(time_final - time);
4865     int time_count_dir = (time < time_final ? +1 : -1);
4866
4867     if (time_to_go < time_count_steps)
4868       time_count_steps = 1;
4869
4870     time  += time_count_steps * time_count_dir;
4871     score += time_count_steps * level.score[SC_TIME_BONUS];
4872
4873     game.LevelSolved_CountingTime = time;
4874     game.LevelSolved_CountingScore = score;
4875
4876     game_panel_controls[GAME_PANEL_TIME].value = time;
4877     game_panel_controls[GAME_PANEL_SCORE].value = score;
4878
4879     DisplayGameControlValues();
4880
4881     if (time == time_final)
4882       StopSound(SND_GAME_LEVELTIME_BONUS);
4883     else if (setup.sound_loops)
4884       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4885     else
4886       PlaySound(SND_GAME_LEVELTIME_BONUS);
4887
4888     return;
4889   }
4890
4891   if (game_over_delay_2 > 0)
4892   {
4893     game_over_delay_2--;
4894
4895     return;
4896   }
4897
4898   if (health != health_final)
4899   {
4900     int health_count_dir = (health < health_final ? +1 : -1);
4901
4902     health += health_count_dir;
4903     score  += level.score[SC_TIME_BONUS];
4904
4905     game.LevelSolved_CountingHealth = health;
4906     game.LevelSolved_CountingScore = score;
4907
4908     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4909     game_panel_controls[GAME_PANEL_SCORE].value = score;
4910
4911     DisplayGameControlValues();
4912
4913     if (health == health_final)
4914       StopSound(SND_GAME_LEVELTIME_BONUS);
4915     else if (setup.sound_loops)
4916       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4917     else
4918       PlaySound(SND_GAME_LEVELTIME_BONUS);
4919
4920     return;
4921   }
4922
4923   game.panel.active = FALSE;
4924
4925   if (game_over_delay_3 > 0)
4926   {
4927     game_over_delay_3--;
4928
4929     return;
4930   }
4931
4932   GameEnd();
4933 }
4934
4935 void GameEnd(void)
4936 {
4937   // used instead of "level_nr" (needed for network games)
4938   int last_level_nr = levelset.level_nr;
4939   int hi_pos;
4940
4941   game.LevelSolved_GameEnd = TRUE;
4942
4943   if (game.LevelSolved_SaveTape)
4944   {
4945     // make sure that request dialog to save tape does not open door again
4946     if (!global.use_envelope_request)
4947       CloseDoor(DOOR_CLOSE_1);
4948
4949     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4950   }
4951
4952   // if no tape is to be saved, close both doors simultaneously
4953   CloseDoor(DOOR_CLOSE_ALL);
4954
4955   if (level_editor_test_game)
4956   {
4957     SetGameStatus(GAME_MODE_MAIN);
4958
4959     DrawMainMenu();
4960
4961     return;
4962   }
4963
4964   if (!game.LevelSolved_SaveScore)
4965   {
4966     SetGameStatus(GAME_MODE_MAIN);
4967
4968     DrawMainMenu();
4969
4970     return;
4971   }
4972
4973   if (level_nr == leveldir_current->handicap_level)
4974   {
4975     leveldir_current->handicap_level++;
4976
4977     SaveLevelSetup_SeriesInfo();
4978   }
4979
4980   if (setup.increment_levels &&
4981       level_nr < leveldir_current->last_level &&
4982       !network_playing)
4983   {
4984     level_nr++;         // advance to next level
4985     TapeErase();        // start with empty tape
4986
4987     if (setup.auto_play_next_level)
4988     {
4989       LoadLevel(level_nr);
4990
4991       SaveLevelSetup_SeriesInfo();
4992     }
4993   }
4994
4995   hi_pos = NewHiScore(last_level_nr);
4996
4997   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4998   {
4999     SetGameStatus(GAME_MODE_SCORES);
5000
5001     DrawHallOfFame(last_level_nr, hi_pos);
5002   }
5003   else if (setup.auto_play_next_level && setup.increment_levels &&
5004            last_level_nr < leveldir_current->last_level &&
5005            !network_playing)
5006   {
5007     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5008   }
5009   else
5010   {
5011     SetGameStatus(GAME_MODE_MAIN);
5012
5013     DrawMainMenu();
5014   }
5015 }
5016
5017 int NewHiScore(int level_nr)
5018 {
5019   int k, l;
5020   int position = -1;
5021   boolean one_score_entry_per_name = !program.many_scores_per_name;
5022
5023   LoadScore(level_nr);
5024
5025   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5026       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5027     return -1;
5028
5029   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5030   {
5031     if (game.score_final > highscore[k].Score)
5032     {
5033       // player has made it to the hall of fame
5034
5035       if (k < MAX_SCORE_ENTRIES - 1)
5036       {
5037         int m = MAX_SCORE_ENTRIES - 1;
5038
5039         if (one_score_entry_per_name)
5040         {
5041           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5042             if (strEqual(setup.player_name, highscore[l].Name))
5043               m = l;
5044
5045           if (m == k)   // player's new highscore overwrites his old one
5046             goto put_into_list;
5047         }
5048
5049         for (l = m; l > k; l--)
5050         {
5051           strcpy(highscore[l].Name, highscore[l - 1].Name);
5052           highscore[l].Score = highscore[l - 1].Score;
5053         }
5054       }
5055
5056       put_into_list:
5057
5058       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5059       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5060       highscore[k].Score = game.score_final;
5061       position = k;
5062
5063       break;
5064     }
5065     else if (one_score_entry_per_name &&
5066              !strncmp(setup.player_name, highscore[k].Name,
5067                       MAX_PLAYER_NAME_LEN))
5068       break;    // player already there with a higher score
5069   }
5070
5071   if (position >= 0) 
5072     SaveScore(level_nr);
5073
5074   return position;
5075 }
5076
5077 static int getElementMoveStepsizeExt(int x, int y, int direction)
5078 {
5079   int element = Tile[x][y];
5080   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5081   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5082   int horiz_move = (dx != 0);
5083   int sign = (horiz_move ? dx : dy);
5084   int step = sign * element_info[element].move_stepsize;
5085
5086   // special values for move stepsize for spring and things on conveyor belt
5087   if (horiz_move)
5088   {
5089     if (CAN_FALL(element) &&
5090         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5091       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5092     else if (element == EL_SPRING)
5093       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5094   }
5095
5096   return step;
5097 }
5098
5099 static int getElementMoveStepsize(int x, int y)
5100 {
5101   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5102 }
5103
5104 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5105 {
5106   if (player->GfxAction != action || player->GfxDir != dir)
5107   {
5108     player->GfxAction = action;
5109     player->GfxDir = dir;
5110     player->Frame = 0;
5111     player->StepFrame = 0;
5112   }
5113 }
5114
5115 static void ResetGfxFrame(int x, int y)
5116 {
5117   // profiling showed that "autotest" spends 10~20% of its time in this function
5118   if (DrawingDeactivatedField())
5119     return;
5120
5121   int element = Tile[x][y];
5122   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5123
5124   if (graphic_info[graphic].anim_global_sync)
5125     GfxFrame[x][y] = FrameCounter;
5126   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5127     GfxFrame[x][y] = CustomValue[x][y];
5128   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5129     GfxFrame[x][y] = element_info[element].collect_score;
5130   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5131     GfxFrame[x][y] = ChangeDelay[x][y];
5132 }
5133
5134 static void ResetGfxAnimation(int x, int y)
5135 {
5136   GfxAction[x][y] = ACTION_DEFAULT;
5137   GfxDir[x][y] = MovDir[x][y];
5138   GfxFrame[x][y] = 0;
5139
5140   ResetGfxFrame(x, y);
5141 }
5142
5143 static void ResetRandomAnimationValue(int x, int y)
5144 {
5145   GfxRandom[x][y] = INIT_GFX_RANDOM();
5146 }
5147
5148 static void InitMovingField(int x, int y, int direction)
5149 {
5150   int element = Tile[x][y];
5151   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5152   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5153   int newx = x + dx;
5154   int newy = y + dy;
5155   boolean is_moving_before, is_moving_after;
5156
5157   // check if element was/is moving or being moved before/after mode change
5158   is_moving_before = (WasJustMoving[x][y] != 0);
5159   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5160
5161   // reset animation only for moving elements which change direction of moving
5162   // or which just started or stopped moving
5163   // (else CEs with property "can move" / "not moving" are reset each frame)
5164   if (is_moving_before != is_moving_after ||
5165       direction != MovDir[x][y])
5166     ResetGfxAnimation(x, y);
5167
5168   MovDir[x][y] = direction;
5169   GfxDir[x][y] = direction;
5170
5171   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5172                      direction == MV_DOWN && CAN_FALL(element) ?
5173                      ACTION_FALLING : ACTION_MOVING);
5174
5175   // this is needed for CEs with property "can move" / "not moving"
5176
5177   if (is_moving_after)
5178   {
5179     if (Tile[newx][newy] == EL_EMPTY)
5180       Tile[newx][newy] = EL_BLOCKED;
5181
5182     MovDir[newx][newy] = MovDir[x][y];
5183
5184     CustomValue[newx][newy] = CustomValue[x][y];
5185
5186     GfxFrame[newx][newy] = GfxFrame[x][y];
5187     GfxRandom[newx][newy] = GfxRandom[x][y];
5188     GfxAction[newx][newy] = GfxAction[x][y];
5189     GfxDir[newx][newy] = GfxDir[x][y];
5190   }
5191 }
5192
5193 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5194 {
5195   int direction = MovDir[x][y];
5196   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5197   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5198
5199   *goes_to_x = newx;
5200   *goes_to_y = newy;
5201 }
5202
5203 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5204 {
5205   int oldx = x, oldy = y;
5206   int direction = MovDir[x][y];
5207
5208   if (direction == MV_LEFT)
5209     oldx++;
5210   else if (direction == MV_RIGHT)
5211     oldx--;
5212   else if (direction == MV_UP)
5213     oldy++;
5214   else if (direction == MV_DOWN)
5215     oldy--;
5216
5217   *comes_from_x = oldx;
5218   *comes_from_y = oldy;
5219 }
5220
5221 static int MovingOrBlocked2Element(int x, int y)
5222 {
5223   int element = Tile[x][y];
5224
5225   if (element == EL_BLOCKED)
5226   {
5227     int oldx, oldy;
5228
5229     Blocked2Moving(x, y, &oldx, &oldy);
5230     return Tile[oldx][oldy];
5231   }
5232   else
5233     return element;
5234 }
5235
5236 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5237 {
5238   // like MovingOrBlocked2Element(), but if element is moving
5239   // and (x,y) is the field the moving element is just leaving,
5240   // return EL_BLOCKED instead of the element value
5241   int element = Tile[x][y];
5242
5243   if (IS_MOVING(x, y))
5244   {
5245     if (element == EL_BLOCKED)
5246     {
5247       int oldx, oldy;
5248
5249       Blocked2Moving(x, y, &oldx, &oldy);
5250       return Tile[oldx][oldy];
5251     }
5252     else
5253       return EL_BLOCKED;
5254   }
5255   else
5256     return element;
5257 }
5258
5259 static void RemoveField(int x, int y)
5260 {
5261   Tile[x][y] = EL_EMPTY;
5262
5263   MovPos[x][y] = 0;
5264   MovDir[x][y] = 0;
5265   MovDelay[x][y] = 0;
5266
5267   CustomValue[x][y] = 0;
5268
5269   AmoebaNr[x][y] = 0;
5270   ChangeDelay[x][y] = 0;
5271   ChangePage[x][y] = -1;
5272   Pushed[x][y] = FALSE;
5273
5274   GfxElement[x][y] = EL_UNDEFINED;
5275   GfxAction[x][y] = ACTION_DEFAULT;
5276   GfxDir[x][y] = MV_NONE;
5277 }
5278
5279 static void RemoveMovingField(int x, int y)
5280 {
5281   int oldx = x, oldy = y, newx = x, newy = y;
5282   int element = Tile[x][y];
5283   int next_element = EL_UNDEFINED;
5284
5285   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5286     return;
5287
5288   if (IS_MOVING(x, y))
5289   {
5290     Moving2Blocked(x, y, &newx, &newy);
5291
5292     if (Tile[newx][newy] != EL_BLOCKED)
5293     {
5294       // element is moving, but target field is not free (blocked), but
5295       // already occupied by something different (example: acid pool);
5296       // in this case, only remove the moving field, but not the target
5297
5298       RemoveField(oldx, oldy);
5299
5300       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5301
5302       TEST_DrawLevelField(oldx, oldy);
5303
5304       return;
5305     }
5306   }
5307   else if (element == EL_BLOCKED)
5308   {
5309     Blocked2Moving(x, y, &oldx, &oldy);
5310     if (!IS_MOVING(oldx, oldy))
5311       return;
5312   }
5313
5314   if (element == EL_BLOCKED &&
5315       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5316        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5317        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5318        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5319        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5320        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5321     next_element = get_next_element(Tile[oldx][oldy]);
5322
5323   RemoveField(oldx, oldy);
5324   RemoveField(newx, newy);
5325
5326   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5327
5328   if (next_element != EL_UNDEFINED)
5329     Tile[oldx][oldy] = next_element;
5330
5331   TEST_DrawLevelField(oldx, oldy);
5332   TEST_DrawLevelField(newx, newy);
5333 }
5334
5335 void DrawDynamite(int x, int y)
5336 {
5337   int sx = SCREENX(x), sy = SCREENY(y);
5338   int graphic = el2img(Tile[x][y]);
5339   int frame;
5340
5341   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5342     return;
5343
5344   if (IS_WALKABLE_INSIDE(Back[x][y]))
5345     return;
5346
5347   if (Back[x][y])
5348     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5349   else if (Store[x][y])
5350     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5351
5352   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5353
5354   if (Back[x][y] || Store[x][y])
5355     DrawGraphicThruMask(sx, sy, graphic, frame);
5356   else
5357     DrawGraphic(sx, sy, graphic, frame);
5358 }
5359
5360 static void CheckDynamite(int x, int y)
5361 {
5362   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5363   {
5364     MovDelay[x][y]--;
5365
5366     if (MovDelay[x][y] != 0)
5367     {
5368       DrawDynamite(x, y);
5369       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5370
5371       return;
5372     }
5373   }
5374
5375   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5376
5377   Bang(x, y);
5378 }
5379
5380 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5381 {
5382   boolean num_checked_players = 0;
5383   int i;
5384
5385   for (i = 0; i < MAX_PLAYERS; i++)
5386   {
5387     if (stored_player[i].active)
5388     {
5389       int sx = stored_player[i].jx;
5390       int sy = stored_player[i].jy;
5391
5392       if (num_checked_players == 0)
5393       {
5394         *sx1 = *sx2 = sx;
5395         *sy1 = *sy2 = sy;
5396       }
5397       else
5398       {
5399         *sx1 = MIN(*sx1, sx);
5400         *sy1 = MIN(*sy1, sy);
5401         *sx2 = MAX(*sx2, sx);
5402         *sy2 = MAX(*sy2, sy);
5403       }
5404
5405       num_checked_players++;
5406     }
5407   }
5408 }
5409
5410 static boolean checkIfAllPlayersFitToScreen_RND(void)
5411 {
5412   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5413
5414   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5415
5416   return (sx2 - sx1 < SCR_FIELDX &&
5417           sy2 - sy1 < SCR_FIELDY);
5418 }
5419
5420 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5421 {
5422   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5423
5424   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5425
5426   *sx = (sx1 + sx2) / 2;
5427   *sy = (sy1 + sy2) / 2;
5428 }
5429
5430 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5431                                boolean center_screen, boolean quick_relocation)
5432 {
5433   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5434   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5435   boolean no_delay = (tape.warp_forward);
5436   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5437   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5438   int new_scroll_x, new_scroll_y;
5439
5440   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5441   {
5442     // case 1: quick relocation inside visible screen (without scrolling)
5443
5444     RedrawPlayfield();
5445
5446     return;
5447   }
5448
5449   if (!level.shifted_relocation || center_screen)
5450   {
5451     // relocation _with_ centering of screen
5452
5453     new_scroll_x = SCROLL_POSITION_X(x);
5454     new_scroll_y = SCROLL_POSITION_Y(y);
5455   }
5456   else
5457   {
5458     // relocation _without_ centering of screen
5459
5460     int center_scroll_x = SCROLL_POSITION_X(old_x);
5461     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5462     int offset_x = x + (scroll_x - center_scroll_x);
5463     int offset_y = y + (scroll_y - center_scroll_y);
5464
5465     // for new screen position, apply previous offset to center position
5466     new_scroll_x = SCROLL_POSITION_X(offset_x);
5467     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5468   }
5469
5470   if (quick_relocation)
5471   {
5472     // case 2: quick relocation (redraw without visible scrolling)
5473
5474     scroll_x = new_scroll_x;
5475     scroll_y = new_scroll_y;
5476
5477     RedrawPlayfield();
5478
5479     return;
5480   }
5481
5482   // case 3: visible relocation (with scrolling to new position)
5483
5484   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5485
5486   SetVideoFrameDelay(wait_delay_value);
5487
5488   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5489   {
5490     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5491     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5492
5493     if (dx == 0 && dy == 0)             // no scrolling needed at all
5494       break;
5495
5496     scroll_x -= dx;
5497     scroll_y -= dy;
5498
5499     // set values for horizontal/vertical screen scrolling (half tile size)
5500     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5501     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5502     int pos_x = dx * TILEX / 2;
5503     int pos_y = dy * TILEY / 2;
5504     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5505     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5506
5507     ScrollLevel(dx, dy);
5508     DrawAllPlayers();
5509
5510     // scroll in two steps of half tile size to make things smoother
5511     BlitScreenToBitmapExt_RND(window, fx, fy);
5512
5513     // scroll second step to align at full tile size
5514     BlitScreenToBitmap(window);
5515   }
5516
5517   DrawAllPlayers();
5518   BackToFront();
5519
5520   SetVideoFrameDelay(frame_delay_value_old);
5521 }
5522
5523 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5524 {
5525   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5526   int player_nr = GET_PLAYER_NR(el_player);
5527   struct PlayerInfo *player = &stored_player[player_nr];
5528   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5529   boolean no_delay = (tape.warp_forward);
5530   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5531   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5532   int old_jx = player->jx;
5533   int old_jy = player->jy;
5534   int old_element = Tile[old_jx][old_jy];
5535   int element = Tile[jx][jy];
5536   boolean player_relocated = (old_jx != jx || old_jy != jy);
5537
5538   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5539   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5540   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5541   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5542   int leave_side_horiz = move_dir_horiz;
5543   int leave_side_vert  = move_dir_vert;
5544   int enter_side = enter_side_horiz | enter_side_vert;
5545   int leave_side = leave_side_horiz | leave_side_vert;
5546
5547   if (player->buried)           // do not reanimate dead player
5548     return;
5549
5550   if (!player_relocated)        // no need to relocate the player
5551     return;
5552
5553   if (IS_PLAYER(jx, jy))        // player already placed at new position
5554   {
5555     RemoveField(jx, jy);        // temporarily remove newly placed player
5556     DrawLevelField(jx, jy);
5557   }
5558
5559   if (player->present)
5560   {
5561     while (player->MovPos)
5562     {
5563       ScrollPlayer(player, SCROLL_GO_ON);
5564       ScrollScreen(NULL, SCROLL_GO_ON);
5565
5566       AdvanceFrameAndPlayerCounters(player->index_nr);
5567
5568       DrawPlayer(player);
5569
5570       BackToFront_WithFrameDelay(wait_delay_value);
5571     }
5572
5573     DrawPlayer(player);         // needed here only to cleanup last field
5574     DrawLevelField(player->jx, player->jy);     // remove player graphic
5575
5576     player->is_moving = FALSE;
5577   }
5578
5579   if (IS_CUSTOM_ELEMENT(old_element))
5580     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5581                                CE_LEFT_BY_PLAYER,
5582                                player->index_bit, leave_side);
5583
5584   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5585                                       CE_PLAYER_LEAVES_X,
5586                                       player->index_bit, leave_side);
5587
5588   Tile[jx][jy] = el_player;
5589   InitPlayerField(jx, jy, el_player, TRUE);
5590
5591   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5592      possible that the relocation target field did not contain a player element,
5593      but a walkable element, to which the new player was relocated -- in this
5594      case, restore that (already initialized!) element on the player field */
5595   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5596   {
5597     Tile[jx][jy] = element;     // restore previously existing element
5598   }
5599
5600   // only visually relocate centered player
5601   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5602                      FALSE, level.instant_relocation);
5603
5604   TestIfPlayerTouchesBadThing(jx, jy);
5605   TestIfPlayerTouchesCustomElement(jx, jy);
5606
5607   if (IS_CUSTOM_ELEMENT(element))
5608     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5609                                player->index_bit, enter_side);
5610
5611   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5612                                       player->index_bit, enter_side);
5613
5614   if (player->is_switching)
5615   {
5616     /* ensure that relocation while still switching an element does not cause
5617        a new element to be treated as also switched directly after relocation
5618        (this is important for teleporter switches that teleport the player to
5619        a place where another teleporter switch is in the same direction, which
5620        would then incorrectly be treated as immediately switched before the
5621        direction key that caused the switch was released) */
5622
5623     player->switch_x += jx - old_jx;
5624     player->switch_y += jy - old_jy;
5625   }
5626 }
5627
5628 static void Explode(int ex, int ey, int phase, int mode)
5629 {
5630   int x, y;
5631   int last_phase;
5632   int border_element;
5633
5634   // !!! eliminate this variable !!!
5635   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5636
5637   if (game.explosions_delayed)
5638   {
5639     ExplodeField[ex][ey] = mode;
5640     return;
5641   }
5642
5643   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5644   {
5645     int center_element = Tile[ex][ey];
5646     int artwork_element, explosion_element;     // set these values later
5647
5648     // remove things displayed in background while burning dynamite
5649     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5650       Back[ex][ey] = 0;
5651
5652     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5653     {
5654       // put moving element to center field (and let it explode there)
5655       center_element = MovingOrBlocked2Element(ex, ey);
5656       RemoveMovingField(ex, ey);
5657       Tile[ex][ey] = center_element;
5658     }
5659
5660     // now "center_element" is finally determined -- set related values now
5661     artwork_element = center_element;           // for custom player artwork
5662     explosion_element = center_element;         // for custom player artwork
5663
5664     if (IS_PLAYER(ex, ey))
5665     {
5666       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5667
5668       artwork_element = stored_player[player_nr].artwork_element;
5669
5670       if (level.use_explosion_element[player_nr])
5671       {
5672         explosion_element = level.explosion_element[player_nr];
5673         artwork_element = explosion_element;
5674       }
5675     }
5676
5677     if (mode == EX_TYPE_NORMAL ||
5678         mode == EX_TYPE_CENTER ||
5679         mode == EX_TYPE_CROSS)
5680       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5681
5682     last_phase = element_info[explosion_element].explosion_delay + 1;
5683
5684     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5685     {
5686       int xx = x - ex + 1;
5687       int yy = y - ey + 1;
5688       int element;
5689
5690       if (!IN_LEV_FIELD(x, y) ||
5691           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5692           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5693         continue;
5694
5695       element = Tile[x][y];
5696
5697       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5698       {
5699         element = MovingOrBlocked2Element(x, y);
5700
5701         if (!IS_EXPLOSION_PROOF(element))
5702           RemoveMovingField(x, y);
5703       }
5704
5705       // indestructible elements can only explode in center (but not flames)
5706       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5707                                            mode == EX_TYPE_BORDER)) ||
5708           element == EL_FLAMES)
5709         continue;
5710
5711       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5712          behaviour, for example when touching a yamyam that explodes to rocks
5713          with active deadly shield, a rock is created under the player !!! */
5714       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5715 #if 0
5716       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5717           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5718            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5719 #else
5720       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5721 #endif
5722       {
5723         if (IS_ACTIVE_BOMB(element))
5724         {
5725           // re-activate things under the bomb like gate or penguin
5726           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5727           Back[x][y] = 0;
5728         }
5729
5730         continue;
5731       }
5732
5733       // save walkable background elements while explosion on same tile
5734       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5735           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5736         Back[x][y] = element;
5737
5738       // ignite explodable elements reached by other explosion
5739       if (element == EL_EXPLOSION)
5740         element = Store2[x][y];
5741
5742       if (AmoebaNr[x][y] &&
5743           (element == EL_AMOEBA_FULL ||
5744            element == EL_BD_AMOEBA ||
5745            element == EL_AMOEBA_GROWING))
5746       {
5747         AmoebaCnt[AmoebaNr[x][y]]--;
5748         AmoebaCnt2[AmoebaNr[x][y]]--;
5749       }
5750
5751       RemoveField(x, y);
5752
5753       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5754       {
5755         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5756
5757         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5758
5759         if (PLAYERINFO(ex, ey)->use_murphy)
5760           Store[x][y] = EL_EMPTY;
5761       }
5762
5763       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5764       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5765       else if (ELEM_IS_PLAYER(center_element))
5766         Store[x][y] = EL_EMPTY;
5767       else if (center_element == EL_YAMYAM)
5768         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5769       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5770         Store[x][y] = element_info[center_element].content.e[xx][yy];
5771 #if 1
5772       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5773       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5774       // otherwise) -- FIX THIS !!!
5775       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5776         Store[x][y] = element_info[element].content.e[1][1];
5777 #else
5778       else if (!CAN_EXPLODE(element))
5779         Store[x][y] = element_info[element].content.e[1][1];
5780 #endif
5781       else
5782         Store[x][y] = EL_EMPTY;
5783
5784       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5785           center_element == EL_AMOEBA_TO_DIAMOND)
5786         Store2[x][y] = element;
5787
5788       Tile[x][y] = EL_EXPLOSION;
5789       GfxElement[x][y] = artwork_element;
5790
5791       ExplodePhase[x][y] = 1;
5792       ExplodeDelay[x][y] = last_phase;
5793
5794       Stop[x][y] = TRUE;
5795     }
5796
5797     if (center_element == EL_YAMYAM)
5798       game.yamyam_content_nr =
5799         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5800
5801     return;
5802   }
5803
5804   if (Stop[ex][ey])
5805     return;
5806
5807   x = ex;
5808   y = ey;
5809
5810   if (phase == 1)
5811     GfxFrame[x][y] = 0;         // restart explosion animation
5812
5813   last_phase = ExplodeDelay[x][y];
5814
5815   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5816
5817   // this can happen if the player leaves an explosion just in time
5818   if (GfxElement[x][y] == EL_UNDEFINED)
5819     GfxElement[x][y] = EL_EMPTY;
5820
5821   border_element = Store2[x][y];
5822   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5823     border_element = StorePlayer[x][y];
5824
5825   if (phase == element_info[border_element].ignition_delay ||
5826       phase == last_phase)
5827   {
5828     boolean border_explosion = FALSE;
5829
5830     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5831         !PLAYER_EXPLOSION_PROTECTED(x, y))
5832     {
5833       KillPlayerUnlessExplosionProtected(x, y);
5834       border_explosion = TRUE;
5835     }
5836     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5837     {
5838       Tile[x][y] = Store2[x][y];
5839       Store2[x][y] = 0;
5840       Bang(x, y);
5841       border_explosion = TRUE;
5842     }
5843     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5844     {
5845       AmoebaToDiamond(x, y);
5846       Store2[x][y] = 0;
5847       border_explosion = TRUE;
5848     }
5849
5850     // if an element just explodes due to another explosion (chain-reaction),
5851     // do not immediately end the new explosion when it was the last frame of
5852     // the explosion (as it would be done in the following "if"-statement!)
5853     if (border_explosion && phase == last_phase)
5854       return;
5855   }
5856
5857   if (phase == last_phase)
5858   {
5859     int element;
5860
5861     element = Tile[x][y] = Store[x][y];
5862     Store[x][y] = Store2[x][y] = 0;
5863     GfxElement[x][y] = EL_UNDEFINED;
5864
5865     // player can escape from explosions and might therefore be still alive
5866     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5867         element <= EL_PLAYER_IS_EXPLODING_4)
5868     {
5869       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5870       int explosion_element = EL_PLAYER_1 + player_nr;
5871       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5872       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5873
5874       if (level.use_explosion_element[player_nr])
5875         explosion_element = level.explosion_element[player_nr];
5876
5877       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5878                     element_info[explosion_element].content.e[xx][yy]);
5879     }
5880
5881     // restore probably existing indestructible background element
5882     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5883       element = Tile[x][y] = Back[x][y];
5884     Back[x][y] = 0;
5885
5886     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5887     GfxDir[x][y] = MV_NONE;
5888     ChangeDelay[x][y] = 0;
5889     ChangePage[x][y] = -1;
5890
5891     CustomValue[x][y] = 0;
5892
5893     InitField_WithBug2(x, y, FALSE);
5894
5895     TEST_DrawLevelField(x, y);
5896
5897     TestIfElementTouchesCustomElement(x, y);
5898
5899     if (GFX_CRUMBLED(element))
5900       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5901
5902     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5903       StorePlayer[x][y] = 0;
5904
5905     if (ELEM_IS_PLAYER(element))
5906       RelocatePlayer(x, y, element);
5907   }
5908   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5909   {
5910     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5911     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5912
5913     if (phase == delay)
5914       TEST_DrawLevelFieldCrumbled(x, y);
5915
5916     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5917     {
5918       DrawLevelElement(x, y, Back[x][y]);
5919       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5920     }
5921     else if (IS_WALKABLE_UNDER(Back[x][y]))
5922     {
5923       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5924       DrawLevelElementThruMask(x, y, Back[x][y]);
5925     }
5926     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5927       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5928   }
5929 }
5930
5931 static void DynaExplode(int ex, int ey)
5932 {
5933   int i, j;
5934   int dynabomb_element = Tile[ex][ey];
5935   int dynabomb_size = 1;
5936   boolean dynabomb_xl = FALSE;
5937   struct PlayerInfo *player;
5938   static int xy[4][2] =
5939   {
5940     { 0, -1 },
5941     { -1, 0 },
5942     { +1, 0 },
5943     { 0, +1 }
5944   };
5945
5946   if (IS_ACTIVE_BOMB(dynabomb_element))
5947   {
5948     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5949     dynabomb_size = player->dynabomb_size;
5950     dynabomb_xl = player->dynabomb_xl;
5951     player->dynabombs_left++;
5952   }
5953
5954   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5955
5956   for (i = 0; i < NUM_DIRECTIONS; i++)
5957   {
5958     for (j = 1; j <= dynabomb_size; j++)
5959     {
5960       int x = ex + j * xy[i][0];
5961       int y = ey + j * xy[i][1];
5962       int element;
5963
5964       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5965         break;
5966
5967       element = Tile[x][y];
5968
5969       // do not restart explosions of fields with active bombs
5970       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5971         continue;
5972
5973       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5974
5975       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5976           !IS_DIGGABLE(element) && !dynabomb_xl)
5977         break;
5978     }
5979   }
5980 }
5981
5982 void Bang(int x, int y)
5983 {
5984   int element = MovingOrBlocked2Element(x, y);
5985   int explosion_type = EX_TYPE_NORMAL;
5986
5987   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5988   {
5989     struct PlayerInfo *player = PLAYERINFO(x, y);
5990
5991     element = Tile[x][y] = player->initial_element;
5992
5993     if (level.use_explosion_element[player->index_nr])
5994     {
5995       int explosion_element = level.explosion_element[player->index_nr];
5996
5997       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5998         explosion_type = EX_TYPE_CROSS;
5999       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6000         explosion_type = EX_TYPE_CENTER;
6001     }
6002   }
6003
6004   switch (element)
6005   {
6006     case EL_BUG:
6007     case EL_SPACESHIP:
6008     case EL_BD_BUTTERFLY:
6009     case EL_BD_FIREFLY:
6010     case EL_YAMYAM:
6011     case EL_DARK_YAMYAM:
6012     case EL_ROBOT:
6013     case EL_PACMAN:
6014     case EL_MOLE:
6015       RaiseScoreElement(element);
6016       break;
6017
6018     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6019     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6020     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6021     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6022     case EL_DYNABOMB_INCREASE_NUMBER:
6023     case EL_DYNABOMB_INCREASE_SIZE:
6024     case EL_DYNABOMB_INCREASE_POWER:
6025       explosion_type = EX_TYPE_DYNA;
6026       break;
6027
6028     case EL_DC_LANDMINE:
6029       explosion_type = EX_TYPE_CENTER;
6030       break;
6031
6032     case EL_PENGUIN:
6033     case EL_LAMP:
6034     case EL_LAMP_ACTIVE:
6035     case EL_AMOEBA_TO_DIAMOND:
6036       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6037         explosion_type = EX_TYPE_CENTER;
6038       break;
6039
6040     default:
6041       if (element_info[element].explosion_type == EXPLODES_CROSS)
6042         explosion_type = EX_TYPE_CROSS;
6043       else if (element_info[element].explosion_type == EXPLODES_1X1)
6044         explosion_type = EX_TYPE_CENTER;
6045       break;
6046   }
6047
6048   if (explosion_type == EX_TYPE_DYNA)
6049     DynaExplode(x, y);
6050   else
6051     Explode(x, y, EX_PHASE_START, explosion_type);
6052
6053   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6054 }
6055
6056 static void SplashAcid(int x, int y)
6057 {
6058   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6059       (!IN_LEV_FIELD(x - 1, y - 2) ||
6060        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6061     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6062
6063   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6064       (!IN_LEV_FIELD(x + 1, y - 2) ||
6065        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6066     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6067
6068   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6069 }
6070
6071 static void InitBeltMovement(void)
6072 {
6073   static int belt_base_element[4] =
6074   {
6075     EL_CONVEYOR_BELT_1_LEFT,
6076     EL_CONVEYOR_BELT_2_LEFT,
6077     EL_CONVEYOR_BELT_3_LEFT,
6078     EL_CONVEYOR_BELT_4_LEFT
6079   };
6080   static int belt_base_active_element[4] =
6081   {
6082     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6083     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6084     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6085     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6086   };
6087
6088   int x, y, i, j;
6089
6090   // set frame order for belt animation graphic according to belt direction
6091   for (i = 0; i < NUM_BELTS; i++)
6092   {
6093     int belt_nr = i;
6094
6095     for (j = 0; j < NUM_BELT_PARTS; j++)
6096     {
6097       int element = belt_base_active_element[belt_nr] + j;
6098       int graphic_1 = el2img(element);
6099       int graphic_2 = el2panelimg(element);
6100
6101       if (game.belt_dir[i] == MV_LEFT)
6102       {
6103         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6104         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6105       }
6106       else
6107       {
6108         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6109         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6110       }
6111     }
6112   }
6113
6114   SCAN_PLAYFIELD(x, y)
6115   {
6116     int element = Tile[x][y];
6117
6118     for (i = 0; i < NUM_BELTS; i++)
6119     {
6120       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6121       {
6122         int e_belt_nr = getBeltNrFromBeltElement(element);
6123         int belt_nr = i;
6124
6125         if (e_belt_nr == belt_nr)
6126         {
6127           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6128
6129           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6130         }
6131       }
6132     }
6133   }
6134 }
6135
6136 static void ToggleBeltSwitch(int x, int y)
6137 {
6138   static int belt_base_element[4] =
6139   {
6140     EL_CONVEYOR_BELT_1_LEFT,
6141     EL_CONVEYOR_BELT_2_LEFT,
6142     EL_CONVEYOR_BELT_3_LEFT,
6143     EL_CONVEYOR_BELT_4_LEFT
6144   };
6145   static int belt_base_active_element[4] =
6146   {
6147     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6148     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6149     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6150     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6151   };
6152   static int belt_base_switch_element[4] =
6153   {
6154     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6155     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6156     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6157     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6158   };
6159   static int belt_move_dir[4] =
6160   {
6161     MV_LEFT,
6162     MV_NONE,
6163     MV_RIGHT,
6164     MV_NONE,
6165   };
6166
6167   int element = Tile[x][y];
6168   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6169   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6170   int belt_dir = belt_move_dir[belt_dir_nr];
6171   int xx, yy, i;
6172
6173   if (!IS_BELT_SWITCH(element))
6174     return;
6175
6176   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6177   game.belt_dir[belt_nr] = belt_dir;
6178
6179   if (belt_dir_nr == 3)
6180     belt_dir_nr = 1;
6181
6182   // set frame order for belt animation graphic according to belt direction
6183   for (i = 0; i < NUM_BELT_PARTS; i++)
6184   {
6185     int element = belt_base_active_element[belt_nr] + i;
6186     int graphic_1 = el2img(element);
6187     int graphic_2 = el2panelimg(element);
6188
6189     if (belt_dir == MV_LEFT)
6190     {
6191       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6192       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6193     }
6194     else
6195     {
6196       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6197       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6198     }
6199   }
6200
6201   SCAN_PLAYFIELD(xx, yy)
6202   {
6203     int element = Tile[xx][yy];
6204
6205     if (IS_BELT_SWITCH(element))
6206     {
6207       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6208
6209       if (e_belt_nr == belt_nr)
6210       {
6211         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6212         TEST_DrawLevelField(xx, yy);
6213       }
6214     }
6215     else if (IS_BELT(element) && belt_dir != MV_NONE)
6216     {
6217       int e_belt_nr = getBeltNrFromBeltElement(element);
6218
6219       if (e_belt_nr == belt_nr)
6220       {
6221         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6222
6223         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6224         TEST_DrawLevelField(xx, yy);
6225       }
6226     }
6227     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6228     {
6229       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6230
6231       if (e_belt_nr == belt_nr)
6232       {
6233         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6234
6235         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6236         TEST_DrawLevelField(xx, yy);
6237       }
6238     }
6239   }
6240 }
6241
6242 static void ToggleSwitchgateSwitch(int x, int y)
6243 {
6244   int xx, yy;
6245
6246   game.switchgate_pos = !game.switchgate_pos;
6247
6248   SCAN_PLAYFIELD(xx, yy)
6249   {
6250     int element = Tile[xx][yy];
6251
6252     if (element == EL_SWITCHGATE_SWITCH_UP)
6253     {
6254       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6255       TEST_DrawLevelField(xx, yy);
6256     }
6257     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6258     {
6259       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6260       TEST_DrawLevelField(xx, yy);
6261     }
6262     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6263     {
6264       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6265       TEST_DrawLevelField(xx, yy);
6266     }
6267     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6268     {
6269       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6270       TEST_DrawLevelField(xx, yy);
6271     }
6272     else if (element == EL_SWITCHGATE_OPEN ||
6273              element == EL_SWITCHGATE_OPENING)
6274     {
6275       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6276
6277       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6278     }
6279     else if (element == EL_SWITCHGATE_CLOSED ||
6280              element == EL_SWITCHGATE_CLOSING)
6281     {
6282       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6283
6284       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6285     }
6286   }
6287 }
6288
6289 static int getInvisibleActiveFromInvisibleElement(int element)
6290 {
6291   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6292           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6293           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6294           element);
6295 }
6296
6297 static int getInvisibleFromInvisibleActiveElement(int element)
6298 {
6299   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6300           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6301           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6302           element);
6303 }
6304
6305 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6306 {
6307   int x, y;
6308
6309   SCAN_PLAYFIELD(x, y)
6310   {
6311     int element = Tile[x][y];
6312
6313     if (element == EL_LIGHT_SWITCH &&
6314         game.light_time_left > 0)
6315     {
6316       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6317       TEST_DrawLevelField(x, y);
6318     }
6319     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6320              game.light_time_left == 0)
6321     {
6322       Tile[x][y] = EL_LIGHT_SWITCH;
6323       TEST_DrawLevelField(x, y);
6324     }
6325     else if (element == EL_EMC_DRIPPER &&
6326              game.light_time_left > 0)
6327     {
6328       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6329       TEST_DrawLevelField(x, y);
6330     }
6331     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6332              game.light_time_left == 0)
6333     {
6334       Tile[x][y] = EL_EMC_DRIPPER;
6335       TEST_DrawLevelField(x, y);
6336     }
6337     else if (element == EL_INVISIBLE_STEELWALL ||
6338              element == EL_INVISIBLE_WALL ||
6339              element == EL_INVISIBLE_SAND)
6340     {
6341       if (game.light_time_left > 0)
6342         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6343
6344       TEST_DrawLevelField(x, y);
6345
6346       // uncrumble neighbour fields, if needed
6347       if (element == EL_INVISIBLE_SAND)
6348         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6349     }
6350     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6351              element == EL_INVISIBLE_WALL_ACTIVE ||
6352              element == EL_INVISIBLE_SAND_ACTIVE)
6353     {
6354       if (game.light_time_left == 0)
6355         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6356
6357       TEST_DrawLevelField(x, y);
6358
6359       // re-crumble neighbour fields, if needed
6360       if (element == EL_INVISIBLE_SAND)
6361         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6362     }
6363   }
6364 }
6365
6366 static void RedrawAllInvisibleElementsForLenses(void)
6367 {
6368   int x, y;
6369
6370   SCAN_PLAYFIELD(x, y)
6371   {
6372     int element = Tile[x][y];
6373
6374     if (element == EL_EMC_DRIPPER &&
6375         game.lenses_time_left > 0)
6376     {
6377       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6378       TEST_DrawLevelField(x, y);
6379     }
6380     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6381              game.lenses_time_left == 0)
6382     {
6383       Tile[x][y] = EL_EMC_DRIPPER;
6384       TEST_DrawLevelField(x, y);
6385     }
6386     else if (element == EL_INVISIBLE_STEELWALL ||
6387              element == EL_INVISIBLE_WALL ||
6388              element == EL_INVISIBLE_SAND)
6389     {
6390       if (game.lenses_time_left > 0)
6391         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6392
6393       TEST_DrawLevelField(x, y);
6394
6395       // uncrumble neighbour fields, if needed
6396       if (element == EL_INVISIBLE_SAND)
6397         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6398     }
6399     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6400              element == EL_INVISIBLE_WALL_ACTIVE ||
6401              element == EL_INVISIBLE_SAND_ACTIVE)
6402     {
6403       if (game.lenses_time_left == 0)
6404         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6405
6406       TEST_DrawLevelField(x, y);
6407
6408       // re-crumble neighbour fields, if needed
6409       if (element == EL_INVISIBLE_SAND)
6410         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6411     }
6412   }
6413 }
6414
6415 static void RedrawAllInvisibleElementsForMagnifier(void)
6416 {
6417   int x, y;
6418
6419   SCAN_PLAYFIELD(x, y)
6420   {
6421     int element = Tile[x][y];
6422
6423     if (element == EL_EMC_FAKE_GRASS &&
6424         game.magnify_time_left > 0)
6425     {
6426       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6427       TEST_DrawLevelField(x, y);
6428     }
6429     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6430              game.magnify_time_left == 0)
6431     {
6432       Tile[x][y] = EL_EMC_FAKE_GRASS;
6433       TEST_DrawLevelField(x, y);
6434     }
6435     else if (IS_GATE_GRAY(element) &&
6436              game.magnify_time_left > 0)
6437     {
6438       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6439                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6440                     IS_EM_GATE_GRAY(element) ?
6441                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6442                     IS_EMC_GATE_GRAY(element) ?
6443                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6444                     IS_DC_GATE_GRAY(element) ?
6445                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6446                     element);
6447       TEST_DrawLevelField(x, y);
6448     }
6449     else if (IS_GATE_GRAY_ACTIVE(element) &&
6450              game.magnify_time_left == 0)
6451     {
6452       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6453                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6454                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6455                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6456                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6457                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6458                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6459                     EL_DC_GATE_WHITE_GRAY :
6460                     element);
6461       TEST_DrawLevelField(x, y);
6462     }
6463   }
6464 }
6465
6466 static void ToggleLightSwitch(int x, int y)
6467 {
6468   int element = Tile[x][y];
6469
6470   game.light_time_left =
6471     (element == EL_LIGHT_SWITCH ?
6472      level.time_light * FRAMES_PER_SECOND : 0);
6473
6474   RedrawAllLightSwitchesAndInvisibleElements();
6475 }
6476
6477 static void ActivateTimegateSwitch(int x, int y)
6478 {
6479   int xx, yy;
6480
6481   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6482
6483   SCAN_PLAYFIELD(xx, yy)
6484   {
6485     int element = Tile[xx][yy];
6486
6487     if (element == EL_TIMEGATE_CLOSED ||
6488         element == EL_TIMEGATE_CLOSING)
6489     {
6490       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6491       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6492     }
6493
6494     /*
6495     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6496     {
6497       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6498       TEST_DrawLevelField(xx, yy);
6499     }
6500     */
6501
6502   }
6503
6504   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6505                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6506 }
6507
6508 static void Impact(int x, int y)
6509 {
6510   boolean last_line = (y == lev_fieldy - 1);
6511   boolean object_hit = FALSE;
6512   boolean impact = (last_line || object_hit);
6513   int element = Tile[x][y];
6514   int smashed = EL_STEELWALL;
6515
6516   if (!last_line)       // check if element below was hit
6517   {
6518     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6519       return;
6520
6521     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6522                                          MovDir[x][y + 1] != MV_DOWN ||
6523                                          MovPos[x][y + 1] <= TILEY / 2));
6524
6525     // do not smash moving elements that left the smashed field in time
6526     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6527         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6528       object_hit = FALSE;
6529
6530 #if USE_QUICKSAND_IMPACT_BUGFIX
6531     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6532     {
6533       RemoveMovingField(x, y + 1);
6534       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6535       Tile[x][y + 2] = EL_ROCK;
6536       TEST_DrawLevelField(x, y + 2);
6537
6538       object_hit = TRUE;
6539     }
6540
6541     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6542     {
6543       RemoveMovingField(x, y + 1);
6544       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6545       Tile[x][y + 2] = EL_ROCK;
6546       TEST_DrawLevelField(x, y + 2);
6547
6548       object_hit = TRUE;
6549     }
6550 #endif
6551
6552     if (object_hit)
6553       smashed = MovingOrBlocked2Element(x, y + 1);
6554
6555     impact = (last_line || object_hit);
6556   }
6557
6558   if (!last_line && smashed == EL_ACID) // element falls into acid
6559   {
6560     SplashAcid(x, y + 1);
6561     return;
6562   }
6563
6564   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6565   // only reset graphic animation if graphic really changes after impact
6566   if (impact &&
6567       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6568   {
6569     ResetGfxAnimation(x, y);
6570     TEST_DrawLevelField(x, y);
6571   }
6572
6573   if (impact && CAN_EXPLODE_IMPACT(element))
6574   {
6575     Bang(x, y);
6576     return;
6577   }
6578   else if (impact && element == EL_PEARL &&
6579            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6580   {
6581     ResetGfxAnimation(x, y);
6582
6583     Tile[x][y] = EL_PEARL_BREAKING;
6584     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6585     return;
6586   }
6587   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6588   {
6589     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6590
6591     return;
6592   }
6593
6594   if (impact && element == EL_AMOEBA_DROP)
6595   {
6596     if (object_hit && IS_PLAYER(x, y + 1))
6597       KillPlayerUnlessEnemyProtected(x, y + 1);
6598     else if (object_hit && smashed == EL_PENGUIN)
6599       Bang(x, y + 1);
6600     else
6601     {
6602       Tile[x][y] = EL_AMOEBA_GROWING;
6603       Store[x][y] = EL_AMOEBA_WET;
6604
6605       ResetRandomAnimationValue(x, y);
6606     }
6607     return;
6608   }
6609
6610   if (object_hit)               // check which object was hit
6611   {
6612     if ((CAN_PASS_MAGIC_WALL(element) && 
6613          (smashed == EL_MAGIC_WALL ||
6614           smashed == EL_BD_MAGIC_WALL)) ||
6615         (CAN_PASS_DC_MAGIC_WALL(element) &&
6616          smashed == EL_DC_MAGIC_WALL))
6617     {
6618       int xx, yy;
6619       int activated_magic_wall =
6620         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6621          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6622          EL_DC_MAGIC_WALL_ACTIVE);
6623
6624       // activate magic wall / mill
6625       SCAN_PLAYFIELD(xx, yy)
6626       {
6627         if (Tile[xx][yy] == smashed)
6628           Tile[xx][yy] = activated_magic_wall;
6629       }
6630
6631       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6632       game.magic_wall_active = TRUE;
6633
6634       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6635                             SND_MAGIC_WALL_ACTIVATING :
6636                             smashed == EL_BD_MAGIC_WALL ?
6637                             SND_BD_MAGIC_WALL_ACTIVATING :
6638                             SND_DC_MAGIC_WALL_ACTIVATING));
6639     }
6640
6641     if (IS_PLAYER(x, y + 1))
6642     {
6643       if (CAN_SMASH_PLAYER(element))
6644       {
6645         KillPlayerUnlessEnemyProtected(x, y + 1);
6646         return;
6647       }
6648     }
6649     else if (smashed == EL_PENGUIN)
6650     {
6651       if (CAN_SMASH_PLAYER(element))
6652       {
6653         Bang(x, y + 1);
6654         return;
6655       }
6656     }
6657     else if (element == EL_BD_DIAMOND)
6658     {
6659       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6660       {
6661         Bang(x, y + 1);
6662         return;
6663       }
6664     }
6665     else if (((element == EL_SP_INFOTRON ||
6666                element == EL_SP_ZONK) &&
6667               (smashed == EL_SP_SNIKSNAK ||
6668                smashed == EL_SP_ELECTRON ||
6669                smashed == EL_SP_DISK_ORANGE)) ||
6670              (element == EL_SP_INFOTRON &&
6671               smashed == EL_SP_DISK_YELLOW))
6672     {
6673       Bang(x, y + 1);
6674       return;
6675     }
6676     else if (CAN_SMASH_EVERYTHING(element))
6677     {
6678       if (IS_CLASSIC_ENEMY(smashed) ||
6679           CAN_EXPLODE_SMASHED(smashed))
6680       {
6681         Bang(x, y + 1);
6682         return;
6683       }
6684       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6685       {
6686         if (smashed == EL_LAMP ||
6687             smashed == EL_LAMP_ACTIVE)
6688         {
6689           Bang(x, y + 1);
6690           return;
6691         }
6692         else if (smashed == EL_NUT)
6693         {
6694           Tile[x][y + 1] = EL_NUT_BREAKING;
6695           PlayLevelSound(x, y, SND_NUT_BREAKING);
6696           RaiseScoreElement(EL_NUT);
6697           return;
6698         }
6699         else if (smashed == EL_PEARL)
6700         {
6701           ResetGfxAnimation(x, y);
6702
6703           Tile[x][y + 1] = EL_PEARL_BREAKING;
6704           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6705           return;
6706         }
6707         else if (smashed == EL_DIAMOND)
6708         {
6709           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6710           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6711           return;
6712         }
6713         else if (IS_BELT_SWITCH(smashed))
6714         {
6715           ToggleBeltSwitch(x, y + 1);
6716         }
6717         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6718                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6719                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6720                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6721         {
6722           ToggleSwitchgateSwitch(x, y + 1);
6723         }
6724         else if (smashed == EL_LIGHT_SWITCH ||
6725                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6726         {
6727           ToggleLightSwitch(x, y + 1);
6728         }
6729         else
6730         {
6731           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6732
6733           CheckElementChangeBySide(x, y + 1, smashed, element,
6734                                    CE_SWITCHED, CH_SIDE_TOP);
6735           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6736                                             CH_SIDE_TOP);
6737         }
6738       }
6739       else
6740       {
6741         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6742       }
6743     }
6744   }
6745
6746   // play sound of magic wall / mill
6747   if (!last_line &&
6748       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6749        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6750        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6751   {
6752     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6753       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6754     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6755       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6756     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6757       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6758
6759     return;
6760   }
6761
6762   // play sound of object that hits the ground
6763   if (last_line || object_hit)
6764     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6765 }
6766
6767 static void TurnRoundExt(int x, int y)
6768 {
6769   static struct
6770   {
6771     int dx, dy;
6772   } move_xy[] =
6773   {
6774     {  0,  0 },
6775     { -1,  0 },
6776     { +1,  0 },
6777     {  0,  0 },
6778     {  0, -1 },
6779     {  0,  0 }, { 0, 0 }, { 0, 0 },
6780     {  0, +1 }
6781   };
6782   static struct
6783   {
6784     int left, right, back;
6785   } turn[] =
6786   {
6787     { 0,        0,              0        },
6788     { MV_DOWN,  MV_UP,          MV_RIGHT },
6789     { MV_UP,    MV_DOWN,        MV_LEFT  },
6790     { 0,        0,              0        },
6791     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6792     { 0,        0,              0        },
6793     { 0,        0,              0        },
6794     { 0,        0,              0        },
6795     { MV_RIGHT, MV_LEFT,        MV_UP    }
6796   };
6797
6798   int element = Tile[x][y];
6799   int move_pattern = element_info[element].move_pattern;
6800
6801   int old_move_dir = MovDir[x][y];
6802   int left_dir  = turn[old_move_dir].left;
6803   int right_dir = turn[old_move_dir].right;
6804   int back_dir  = turn[old_move_dir].back;
6805
6806   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6807   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6808   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6809   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6810
6811   int left_x  = x + left_dx,  left_y  = y + left_dy;
6812   int right_x = x + right_dx, right_y = y + right_dy;
6813   int move_x  = x + move_dx,  move_y  = y + move_dy;
6814
6815   int xx, yy;
6816
6817   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6818   {
6819     TestIfBadThingTouchesOtherBadThing(x, y);
6820
6821     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6822       MovDir[x][y] = right_dir;
6823     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6824       MovDir[x][y] = left_dir;
6825
6826     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6827       MovDelay[x][y] = 9;
6828     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6829       MovDelay[x][y] = 1;
6830   }
6831   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6832   {
6833     TestIfBadThingTouchesOtherBadThing(x, y);
6834
6835     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6836       MovDir[x][y] = left_dir;
6837     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6838       MovDir[x][y] = right_dir;
6839
6840     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6841       MovDelay[x][y] = 9;
6842     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6843       MovDelay[x][y] = 1;
6844   }
6845   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6846   {
6847     TestIfBadThingTouchesOtherBadThing(x, y);
6848
6849     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6850       MovDir[x][y] = left_dir;
6851     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6852       MovDir[x][y] = right_dir;
6853
6854     if (MovDir[x][y] != old_move_dir)
6855       MovDelay[x][y] = 9;
6856   }
6857   else if (element == EL_YAMYAM)
6858   {
6859     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6860     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6861
6862     if (can_turn_left && can_turn_right)
6863       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6864     else if (can_turn_left)
6865       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6866     else if (can_turn_right)
6867       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6868     else
6869       MovDir[x][y] = back_dir;
6870
6871     MovDelay[x][y] = 16 + 16 * RND(3);
6872   }
6873   else if (element == EL_DARK_YAMYAM)
6874   {
6875     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6876                                                          left_x, left_y);
6877     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6878                                                          right_x, right_y);
6879
6880     if (can_turn_left && can_turn_right)
6881       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6882     else if (can_turn_left)
6883       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6884     else if (can_turn_right)
6885       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6886     else
6887       MovDir[x][y] = back_dir;
6888
6889     MovDelay[x][y] = 16 + 16 * RND(3);
6890   }
6891   else if (element == EL_PACMAN)
6892   {
6893     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6894     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6895
6896     if (can_turn_left && can_turn_right)
6897       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6898     else if (can_turn_left)
6899       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6900     else if (can_turn_right)
6901       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6902     else
6903       MovDir[x][y] = back_dir;
6904
6905     MovDelay[x][y] = 6 + RND(40);
6906   }
6907   else if (element == EL_PIG)
6908   {
6909     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6910     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6911     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6912     boolean should_turn_left, should_turn_right, should_move_on;
6913     int rnd_value = 24;
6914     int rnd = RND(rnd_value);
6915
6916     should_turn_left = (can_turn_left &&
6917                         (!can_move_on ||
6918                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6919                                                    y + back_dy + left_dy)));
6920     should_turn_right = (can_turn_right &&
6921                          (!can_move_on ||
6922                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6923                                                     y + back_dy + right_dy)));
6924     should_move_on = (can_move_on &&
6925                       (!can_turn_left ||
6926                        !can_turn_right ||
6927                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6928                                                  y + move_dy + left_dy) ||
6929                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6930                                                  y + move_dy + right_dy)));
6931
6932     if (should_turn_left || should_turn_right || should_move_on)
6933     {
6934       if (should_turn_left && should_turn_right && should_move_on)
6935         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6936                         rnd < 2 * rnd_value / 3 ? right_dir :
6937                         old_move_dir);
6938       else if (should_turn_left && should_turn_right)
6939         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6940       else if (should_turn_left && should_move_on)
6941         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6942       else if (should_turn_right && should_move_on)
6943         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6944       else if (should_turn_left)
6945         MovDir[x][y] = left_dir;
6946       else if (should_turn_right)
6947         MovDir[x][y] = right_dir;
6948       else if (should_move_on)
6949         MovDir[x][y] = old_move_dir;
6950     }
6951     else if (can_move_on && rnd > rnd_value / 8)
6952       MovDir[x][y] = old_move_dir;
6953     else if (can_turn_left && can_turn_right)
6954       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6955     else if (can_turn_left && rnd > rnd_value / 8)
6956       MovDir[x][y] = left_dir;
6957     else if (can_turn_right && rnd > rnd_value/8)
6958       MovDir[x][y] = right_dir;
6959     else
6960       MovDir[x][y] = back_dir;
6961
6962     xx = x + move_xy[MovDir[x][y]].dx;
6963     yy = y + move_xy[MovDir[x][y]].dy;
6964
6965     if (!IN_LEV_FIELD(xx, yy) ||
6966         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6967       MovDir[x][y] = old_move_dir;
6968
6969     MovDelay[x][y] = 0;
6970   }
6971   else if (element == EL_DRAGON)
6972   {
6973     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6974     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6975     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6976     int rnd_value = 24;
6977     int rnd = RND(rnd_value);
6978
6979     if (can_move_on && rnd > rnd_value / 8)
6980       MovDir[x][y] = old_move_dir;
6981     else if (can_turn_left && can_turn_right)
6982       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6983     else if (can_turn_left && rnd > rnd_value / 8)
6984       MovDir[x][y] = left_dir;
6985     else if (can_turn_right && rnd > rnd_value / 8)
6986       MovDir[x][y] = right_dir;
6987     else
6988       MovDir[x][y] = back_dir;
6989
6990     xx = x + move_xy[MovDir[x][y]].dx;
6991     yy = y + move_xy[MovDir[x][y]].dy;
6992
6993     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6994       MovDir[x][y] = old_move_dir;
6995
6996     MovDelay[x][y] = 0;
6997   }
6998   else if (element == EL_MOLE)
6999   {
7000     boolean can_move_on =
7001       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7002                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7003                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7004     if (!can_move_on)
7005     {
7006       boolean can_turn_left =
7007         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7008                               IS_AMOEBOID(Tile[left_x][left_y])));
7009
7010       boolean can_turn_right =
7011         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7012                               IS_AMOEBOID(Tile[right_x][right_y])));
7013
7014       if (can_turn_left && can_turn_right)
7015         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7016       else if (can_turn_left)
7017         MovDir[x][y] = left_dir;
7018       else
7019         MovDir[x][y] = right_dir;
7020     }
7021
7022     if (MovDir[x][y] != old_move_dir)
7023       MovDelay[x][y] = 9;
7024   }
7025   else if (element == EL_BALLOON)
7026   {
7027     MovDir[x][y] = game.wind_direction;
7028     MovDelay[x][y] = 0;
7029   }
7030   else if (element == EL_SPRING)
7031   {
7032     if (MovDir[x][y] & MV_HORIZONTAL)
7033     {
7034       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7035           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7036       {
7037         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7038         ResetGfxAnimation(move_x, move_y);
7039         TEST_DrawLevelField(move_x, move_y);
7040
7041         MovDir[x][y] = back_dir;
7042       }
7043       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7044                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7045         MovDir[x][y] = MV_NONE;
7046     }
7047
7048     MovDelay[x][y] = 0;
7049   }
7050   else if (element == EL_ROBOT ||
7051            element == EL_SATELLITE ||
7052            element == EL_PENGUIN ||
7053            element == EL_EMC_ANDROID)
7054   {
7055     int attr_x = -1, attr_y = -1;
7056
7057     if (game.all_players_gone)
7058     {
7059       attr_x = game.exit_x;
7060       attr_y = game.exit_y;
7061     }
7062     else
7063     {
7064       int i;
7065
7066       for (i = 0; i < MAX_PLAYERS; i++)
7067       {
7068         struct PlayerInfo *player = &stored_player[i];
7069         int jx = player->jx, jy = player->jy;
7070
7071         if (!player->active)
7072           continue;
7073
7074         if (attr_x == -1 ||
7075             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7076         {
7077           attr_x = jx;
7078           attr_y = jy;
7079         }
7080       }
7081     }
7082
7083     if (element == EL_ROBOT &&
7084         game.robot_wheel_x >= 0 &&
7085         game.robot_wheel_y >= 0 &&
7086         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7087          game.engine_version < VERSION_IDENT(3,1,0,0)))
7088     {
7089       attr_x = game.robot_wheel_x;
7090       attr_y = game.robot_wheel_y;
7091     }
7092
7093     if (element == EL_PENGUIN)
7094     {
7095       int i;
7096       static int xy[4][2] =
7097       {
7098         { 0, -1 },
7099         { -1, 0 },
7100         { +1, 0 },
7101         { 0, +1 }
7102       };
7103
7104       for (i = 0; i < NUM_DIRECTIONS; i++)
7105       {
7106         int ex = x + xy[i][0];
7107         int ey = y + xy[i][1];
7108
7109         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7110                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7111                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7112                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7113         {
7114           attr_x = ex;
7115           attr_y = ey;
7116           break;
7117         }
7118       }
7119     }
7120
7121     MovDir[x][y] = MV_NONE;
7122     if (attr_x < x)
7123       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7124     else if (attr_x > x)
7125       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7126     if (attr_y < y)
7127       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7128     else if (attr_y > y)
7129       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7130
7131     if (element == EL_ROBOT)
7132     {
7133       int newx, newy;
7134
7135       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7136         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7137       Moving2Blocked(x, y, &newx, &newy);
7138
7139       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7140         MovDelay[x][y] = 8 + 8 * !RND(3);
7141       else
7142         MovDelay[x][y] = 16;
7143     }
7144     else if (element == EL_PENGUIN)
7145     {
7146       int newx, newy;
7147
7148       MovDelay[x][y] = 1;
7149
7150       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7151       {
7152         boolean first_horiz = RND(2);
7153         int new_move_dir = MovDir[x][y];
7154
7155         MovDir[x][y] =
7156           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7157         Moving2Blocked(x, y, &newx, &newy);
7158
7159         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7160           return;
7161
7162         MovDir[x][y] =
7163           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7164         Moving2Blocked(x, y, &newx, &newy);
7165
7166         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7167           return;
7168
7169         MovDir[x][y] = old_move_dir;
7170         return;
7171       }
7172     }
7173     else if (element == EL_SATELLITE)
7174     {
7175       int newx, newy;
7176
7177       MovDelay[x][y] = 1;
7178
7179       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7180       {
7181         boolean first_horiz = RND(2);
7182         int new_move_dir = MovDir[x][y];
7183
7184         MovDir[x][y] =
7185           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7186         Moving2Blocked(x, y, &newx, &newy);
7187
7188         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7189           return;
7190
7191         MovDir[x][y] =
7192           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7193         Moving2Blocked(x, y, &newx, &newy);
7194
7195         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7196           return;
7197
7198         MovDir[x][y] = old_move_dir;
7199         return;
7200       }
7201     }
7202     else if (element == EL_EMC_ANDROID)
7203     {
7204       static int check_pos[16] =
7205       {
7206         -1,             //  0 => (invalid)
7207         7,              //  1 => MV_LEFT
7208         3,              //  2 => MV_RIGHT
7209         -1,             //  3 => (invalid)
7210         1,              //  4 =>            MV_UP
7211         0,              //  5 => MV_LEFT  | MV_UP
7212         2,              //  6 => MV_RIGHT | MV_UP
7213         -1,             //  7 => (invalid)
7214         5,              //  8 =>            MV_DOWN
7215         6,              //  9 => MV_LEFT  | MV_DOWN
7216         4,              // 10 => MV_RIGHT | MV_DOWN
7217         -1,             // 11 => (invalid)
7218         -1,             // 12 => (invalid)
7219         -1,             // 13 => (invalid)
7220         -1,             // 14 => (invalid)
7221         -1,             // 15 => (invalid)
7222       };
7223       static struct
7224       {
7225         int dx, dy;
7226         int dir;
7227       } check_xy[8] =
7228       {
7229         { -1, -1,       MV_LEFT  | MV_UP   },
7230         {  0, -1,                  MV_UP   },
7231         { +1, -1,       MV_RIGHT | MV_UP   },
7232         { +1,  0,       MV_RIGHT           },
7233         { +1, +1,       MV_RIGHT | MV_DOWN },
7234         {  0, +1,                  MV_DOWN },
7235         { -1, +1,       MV_LEFT  | MV_DOWN },
7236         { -1,  0,       MV_LEFT            },
7237       };
7238       int start_pos, check_order;
7239       boolean can_clone = FALSE;
7240       int i;
7241
7242       // check if there is any free field around current position
7243       for (i = 0; i < 8; i++)
7244       {
7245         int newx = x + check_xy[i].dx;
7246         int newy = y + check_xy[i].dy;
7247
7248         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7249         {
7250           can_clone = TRUE;
7251
7252           break;
7253         }
7254       }
7255
7256       if (can_clone)            // randomly find an element to clone
7257       {
7258         can_clone = FALSE;
7259
7260         start_pos = check_pos[RND(8)];
7261         check_order = (RND(2) ? -1 : +1);
7262
7263         for (i = 0; i < 8; i++)
7264         {
7265           int pos_raw = start_pos + i * check_order;
7266           int pos = (pos_raw + 8) % 8;
7267           int newx = x + check_xy[pos].dx;
7268           int newy = y + check_xy[pos].dy;
7269
7270           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7271           {
7272             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7273             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7274
7275             Store[x][y] = Tile[newx][newy];
7276
7277             can_clone = TRUE;
7278
7279             break;
7280           }
7281         }
7282       }
7283
7284       if (can_clone)            // randomly find a direction to move
7285       {
7286         can_clone = FALSE;
7287
7288         start_pos = check_pos[RND(8)];
7289         check_order = (RND(2) ? -1 : +1);
7290
7291         for (i = 0; i < 8; i++)
7292         {
7293           int pos_raw = start_pos + i * check_order;
7294           int pos = (pos_raw + 8) % 8;
7295           int newx = x + check_xy[pos].dx;
7296           int newy = y + check_xy[pos].dy;
7297           int new_move_dir = check_xy[pos].dir;
7298
7299           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7300           {
7301             MovDir[x][y] = new_move_dir;
7302             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7303
7304             can_clone = TRUE;
7305
7306             break;
7307           }
7308         }
7309       }
7310
7311       if (can_clone)            // cloning and moving successful
7312         return;
7313
7314       // cannot clone -- try to move towards player
7315
7316       start_pos = check_pos[MovDir[x][y] & 0x0f];
7317       check_order = (RND(2) ? -1 : +1);
7318
7319       for (i = 0; i < 3; i++)
7320       {
7321         // first check start_pos, then previous/next or (next/previous) pos
7322         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7323         int pos = (pos_raw + 8) % 8;
7324         int newx = x + check_xy[pos].dx;
7325         int newy = y + check_xy[pos].dy;
7326         int new_move_dir = check_xy[pos].dir;
7327
7328         if (IS_PLAYER(newx, newy))
7329           break;
7330
7331         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7332         {
7333           MovDir[x][y] = new_move_dir;
7334           MovDelay[x][y] = level.android_move_time * 8 + 1;
7335
7336           break;
7337         }
7338       }
7339     }
7340   }
7341   else if (move_pattern == MV_TURNING_LEFT ||
7342            move_pattern == MV_TURNING_RIGHT ||
7343            move_pattern == MV_TURNING_LEFT_RIGHT ||
7344            move_pattern == MV_TURNING_RIGHT_LEFT ||
7345            move_pattern == MV_TURNING_RANDOM ||
7346            move_pattern == MV_ALL_DIRECTIONS)
7347   {
7348     boolean can_turn_left =
7349       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7350     boolean can_turn_right =
7351       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7352
7353     if (element_info[element].move_stepsize == 0)       // "not moving"
7354       return;
7355
7356     if (move_pattern == MV_TURNING_LEFT)
7357       MovDir[x][y] = left_dir;
7358     else if (move_pattern == MV_TURNING_RIGHT)
7359       MovDir[x][y] = right_dir;
7360     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7361       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7362     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7363       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7364     else if (move_pattern == MV_TURNING_RANDOM)
7365       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7366                       can_turn_right && !can_turn_left ? right_dir :
7367                       RND(2) ? left_dir : right_dir);
7368     else if (can_turn_left && can_turn_right)
7369       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7370     else if (can_turn_left)
7371       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7372     else if (can_turn_right)
7373       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7374     else
7375       MovDir[x][y] = back_dir;
7376
7377     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7378   }
7379   else if (move_pattern == MV_HORIZONTAL ||
7380            move_pattern == MV_VERTICAL)
7381   {
7382     if (move_pattern & old_move_dir)
7383       MovDir[x][y] = back_dir;
7384     else if (move_pattern == MV_HORIZONTAL)
7385       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7386     else if (move_pattern == MV_VERTICAL)
7387       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7388
7389     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390   }
7391   else if (move_pattern & MV_ANY_DIRECTION)
7392   {
7393     MovDir[x][y] = move_pattern;
7394     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7395   }
7396   else if (move_pattern & MV_WIND_DIRECTION)
7397   {
7398     MovDir[x][y] = game.wind_direction;
7399     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400   }
7401   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7402   {
7403     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7404       MovDir[x][y] = left_dir;
7405     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7406       MovDir[x][y] = right_dir;
7407
7408     if (MovDir[x][y] != old_move_dir)
7409       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7410   }
7411   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7412   {
7413     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7414       MovDir[x][y] = right_dir;
7415     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = left_dir;
7417
7418     if (MovDir[x][y] != old_move_dir)
7419       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7420   }
7421   else if (move_pattern == MV_TOWARDS_PLAYER ||
7422            move_pattern == MV_AWAY_FROM_PLAYER)
7423   {
7424     int attr_x = -1, attr_y = -1;
7425     int newx, newy;
7426     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7427
7428     if (game.all_players_gone)
7429     {
7430       attr_x = game.exit_x;
7431       attr_y = game.exit_y;
7432     }
7433     else
7434     {
7435       int i;
7436
7437       for (i = 0; i < MAX_PLAYERS; i++)
7438       {
7439         struct PlayerInfo *player = &stored_player[i];
7440         int jx = player->jx, jy = player->jy;
7441
7442         if (!player->active)
7443           continue;
7444
7445         if (attr_x == -1 ||
7446             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7447         {
7448           attr_x = jx;
7449           attr_y = jy;
7450         }
7451       }
7452     }
7453
7454     MovDir[x][y] = MV_NONE;
7455     if (attr_x < x)
7456       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7457     else if (attr_x > x)
7458       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7459     if (attr_y < y)
7460       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7461     else if (attr_y > y)
7462       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7463
7464     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7465
7466     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7467     {
7468       boolean first_horiz = RND(2);
7469       int new_move_dir = MovDir[x][y];
7470
7471       if (element_info[element].move_stepsize == 0)     // "not moving"
7472       {
7473         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7474         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7475
7476         return;
7477       }
7478
7479       MovDir[x][y] =
7480         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7481       Moving2Blocked(x, y, &newx, &newy);
7482
7483       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7484         return;
7485
7486       MovDir[x][y] =
7487         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7488       Moving2Blocked(x, y, &newx, &newy);
7489
7490       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7491         return;
7492
7493       MovDir[x][y] = old_move_dir;
7494     }
7495   }
7496   else if (move_pattern == MV_WHEN_PUSHED ||
7497            move_pattern == MV_WHEN_DROPPED)
7498   {
7499     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7500       MovDir[x][y] = MV_NONE;
7501
7502     MovDelay[x][y] = 0;
7503   }
7504   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7505   {
7506     static int test_xy[7][2] =
7507     {
7508       { 0, -1 },
7509       { -1, 0 },
7510       { +1, 0 },
7511       { 0, +1 },
7512       { 0, -1 },
7513       { -1, 0 },
7514       { +1, 0 },
7515     };
7516     static int test_dir[7] =
7517     {
7518       MV_UP,
7519       MV_LEFT,
7520       MV_RIGHT,
7521       MV_DOWN,
7522       MV_UP,
7523       MV_LEFT,
7524       MV_RIGHT,
7525     };
7526     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7527     int move_preference = -1000000;     // start with very low preference
7528     int new_move_dir = MV_NONE;
7529     int start_test = RND(4);
7530     int i;
7531
7532     for (i = 0; i < NUM_DIRECTIONS; i++)
7533     {
7534       int move_dir = test_dir[start_test + i];
7535       int move_dir_preference;
7536
7537       xx = x + test_xy[start_test + i][0];
7538       yy = y + test_xy[start_test + i][1];
7539
7540       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7541           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7542       {
7543         new_move_dir = move_dir;
7544
7545         break;
7546       }
7547
7548       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7549         continue;
7550
7551       move_dir_preference = -1 * RunnerVisit[xx][yy];
7552       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7553         move_dir_preference = PlayerVisit[xx][yy];
7554
7555       if (move_dir_preference > move_preference)
7556       {
7557         // prefer field that has not been visited for the longest time
7558         move_preference = move_dir_preference;
7559         new_move_dir = move_dir;
7560       }
7561       else if (move_dir_preference == move_preference &&
7562                move_dir == old_move_dir)
7563       {
7564         // prefer last direction when all directions are preferred equally
7565         move_preference = move_dir_preference;
7566         new_move_dir = move_dir;
7567       }
7568     }
7569
7570     MovDir[x][y] = new_move_dir;
7571     if (old_move_dir != new_move_dir)
7572       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7573   }
7574 }
7575
7576 static void TurnRound(int x, int y)
7577 {
7578   int direction = MovDir[x][y];
7579
7580   TurnRoundExt(x, y);
7581
7582   GfxDir[x][y] = MovDir[x][y];
7583
7584   if (direction != MovDir[x][y])
7585     GfxFrame[x][y] = 0;
7586
7587   if (MovDelay[x][y])
7588     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7589
7590   ResetGfxFrame(x, y);
7591 }
7592
7593 static boolean JustBeingPushed(int x, int y)
7594 {
7595   int i;
7596
7597   for (i = 0; i < MAX_PLAYERS; i++)
7598   {
7599     struct PlayerInfo *player = &stored_player[i];
7600
7601     if (player->active && player->is_pushing && player->MovPos)
7602     {
7603       int next_jx = player->jx + (player->jx - player->last_jx);
7604       int next_jy = player->jy + (player->jy - player->last_jy);
7605
7606       if (x == next_jx && y == next_jy)
7607         return TRUE;
7608     }
7609   }
7610
7611   return FALSE;
7612 }
7613
7614 static void StartMoving(int x, int y)
7615 {
7616   boolean started_moving = FALSE;       // some elements can fall _and_ move
7617   int element = Tile[x][y];
7618
7619   if (Stop[x][y])
7620     return;
7621
7622   if (MovDelay[x][y] == 0)
7623     GfxAction[x][y] = ACTION_DEFAULT;
7624
7625   if (CAN_FALL(element) && y < lev_fieldy - 1)
7626   {
7627     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7628         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7629       if (JustBeingPushed(x, y))
7630         return;
7631
7632     if (element == EL_QUICKSAND_FULL)
7633     {
7634       if (IS_FREE(x, y + 1))
7635       {
7636         InitMovingField(x, y, MV_DOWN);
7637         started_moving = TRUE;
7638
7639         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7640 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7641         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7642           Store[x][y] = EL_ROCK;
7643 #else
7644         Store[x][y] = EL_ROCK;
7645 #endif
7646
7647         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7648       }
7649       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7650       {
7651         if (!MovDelay[x][y])
7652         {
7653           MovDelay[x][y] = TILEY + 1;
7654
7655           ResetGfxAnimation(x, y);
7656           ResetGfxAnimation(x, y + 1);
7657         }
7658
7659         if (MovDelay[x][y])
7660         {
7661           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7662           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7663
7664           MovDelay[x][y]--;
7665           if (MovDelay[x][y])
7666             return;
7667         }
7668
7669         Tile[x][y] = EL_QUICKSAND_EMPTY;
7670         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7671         Store[x][y + 1] = Store[x][y];
7672         Store[x][y] = 0;
7673
7674         PlayLevelSoundAction(x, y, ACTION_FILLING);
7675       }
7676       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7677       {
7678         if (!MovDelay[x][y])
7679         {
7680           MovDelay[x][y] = TILEY + 1;
7681
7682           ResetGfxAnimation(x, y);
7683           ResetGfxAnimation(x, y + 1);
7684         }
7685
7686         if (MovDelay[x][y])
7687         {
7688           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7689           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7690
7691           MovDelay[x][y]--;
7692           if (MovDelay[x][y])
7693             return;
7694         }
7695
7696         Tile[x][y] = EL_QUICKSAND_EMPTY;
7697         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7698         Store[x][y + 1] = Store[x][y];
7699         Store[x][y] = 0;
7700
7701         PlayLevelSoundAction(x, y, ACTION_FILLING);
7702       }
7703     }
7704     else if (element == EL_QUICKSAND_FAST_FULL)
7705     {
7706       if (IS_FREE(x, y + 1))
7707       {
7708         InitMovingField(x, y, MV_DOWN);
7709         started_moving = TRUE;
7710
7711         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7712 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7713         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7714           Store[x][y] = EL_ROCK;
7715 #else
7716         Store[x][y] = EL_ROCK;
7717 #endif
7718
7719         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7720       }
7721       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7722       {
7723         if (!MovDelay[x][y])
7724         {
7725           MovDelay[x][y] = TILEY + 1;
7726
7727           ResetGfxAnimation(x, y);
7728           ResetGfxAnimation(x, y + 1);
7729         }
7730
7731         if (MovDelay[x][y])
7732         {
7733           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7734           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7735
7736           MovDelay[x][y]--;
7737           if (MovDelay[x][y])
7738             return;
7739         }
7740
7741         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7742         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7743         Store[x][y + 1] = Store[x][y];
7744         Store[x][y] = 0;
7745
7746         PlayLevelSoundAction(x, y, ACTION_FILLING);
7747       }
7748       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7749       {
7750         if (!MovDelay[x][y])
7751         {
7752           MovDelay[x][y] = TILEY + 1;
7753
7754           ResetGfxAnimation(x, y);
7755           ResetGfxAnimation(x, y + 1);
7756         }
7757
7758         if (MovDelay[x][y])
7759         {
7760           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7761           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7762
7763           MovDelay[x][y]--;
7764           if (MovDelay[x][y])
7765             return;
7766         }
7767
7768         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7769         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7770         Store[x][y + 1] = Store[x][y];
7771         Store[x][y] = 0;
7772
7773         PlayLevelSoundAction(x, y, ACTION_FILLING);
7774       }
7775     }
7776     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7777              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7778     {
7779       InitMovingField(x, y, MV_DOWN);
7780       started_moving = TRUE;
7781
7782       Tile[x][y] = EL_QUICKSAND_FILLING;
7783       Store[x][y] = element;
7784
7785       PlayLevelSoundAction(x, y, ACTION_FILLING);
7786     }
7787     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7788              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7789     {
7790       InitMovingField(x, y, MV_DOWN);
7791       started_moving = TRUE;
7792
7793       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7794       Store[x][y] = element;
7795
7796       PlayLevelSoundAction(x, y, ACTION_FILLING);
7797     }
7798     else if (element == EL_MAGIC_WALL_FULL)
7799     {
7800       if (IS_FREE(x, y + 1))
7801       {
7802         InitMovingField(x, y, MV_DOWN);
7803         started_moving = TRUE;
7804
7805         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7806         Store[x][y] = EL_CHANGED(Store[x][y]);
7807       }
7808       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7809       {
7810         if (!MovDelay[x][y])
7811           MovDelay[x][y] = TILEY / 4 + 1;
7812
7813         if (MovDelay[x][y])
7814         {
7815           MovDelay[x][y]--;
7816           if (MovDelay[x][y])
7817             return;
7818         }
7819
7820         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7821         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7822         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7823         Store[x][y] = 0;
7824       }
7825     }
7826     else if (element == EL_BD_MAGIC_WALL_FULL)
7827     {
7828       if (IS_FREE(x, y + 1))
7829       {
7830         InitMovingField(x, y, MV_DOWN);
7831         started_moving = TRUE;
7832
7833         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7834         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7835       }
7836       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7837       {
7838         if (!MovDelay[x][y])
7839           MovDelay[x][y] = TILEY / 4 + 1;
7840
7841         if (MovDelay[x][y])
7842         {
7843           MovDelay[x][y]--;
7844           if (MovDelay[x][y])
7845             return;
7846         }
7847
7848         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7849         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7850         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7851         Store[x][y] = 0;
7852       }
7853     }
7854     else if (element == EL_DC_MAGIC_WALL_FULL)
7855     {
7856       if (IS_FREE(x, y + 1))
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860
7861         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7862         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7863       }
7864       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7865       {
7866         if (!MovDelay[x][y])
7867           MovDelay[x][y] = TILEY / 4 + 1;
7868
7869         if (MovDelay[x][y])
7870         {
7871           MovDelay[x][y]--;
7872           if (MovDelay[x][y])
7873             return;
7874         }
7875
7876         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7877         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7878         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7879         Store[x][y] = 0;
7880       }
7881     }
7882     else if ((CAN_PASS_MAGIC_WALL(element) &&
7883               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7884                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7885              (CAN_PASS_DC_MAGIC_WALL(element) &&
7886               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7887
7888     {
7889       InitMovingField(x, y, MV_DOWN);
7890       started_moving = TRUE;
7891
7892       Tile[x][y] =
7893         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7894          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7895          EL_DC_MAGIC_WALL_FILLING);
7896       Store[x][y] = element;
7897     }
7898     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7899     {
7900       SplashAcid(x, y + 1);
7901
7902       InitMovingField(x, y, MV_DOWN);
7903       started_moving = TRUE;
7904
7905       Store[x][y] = EL_ACID;
7906     }
7907     else if (
7908              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7909               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7910              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7911               CAN_FALL(element) && WasJustFalling[x][y] &&
7912               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7913
7914              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7915               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7916               (Tile[x][y + 1] == EL_BLOCKED)))
7917     {
7918       /* this is needed for a special case not covered by calling "Impact()"
7919          from "ContinueMoving()": if an element moves to a tile directly below
7920          another element which was just falling on that tile (which was empty
7921          in the previous frame), the falling element above would just stop
7922          instead of smashing the element below (in previous version, the above
7923          element was just checked for "moving" instead of "falling", resulting
7924          in incorrect smashes caused by horizontal movement of the above
7925          element; also, the case of the player being the element to smash was
7926          simply not covered here... :-/ ) */
7927
7928       CheckCollision[x][y] = 0;
7929       CheckImpact[x][y] = 0;
7930
7931       Impact(x, y);
7932     }
7933     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7934     {
7935       if (MovDir[x][y] == MV_NONE)
7936       {
7937         InitMovingField(x, y, MV_DOWN);
7938         started_moving = TRUE;
7939       }
7940     }
7941     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7942     {
7943       if (WasJustFalling[x][y]) // prevent animation from being restarted
7944         MovDir[x][y] = MV_DOWN;
7945
7946       InitMovingField(x, y, MV_DOWN);
7947       started_moving = TRUE;
7948     }
7949     else if (element == EL_AMOEBA_DROP)
7950     {
7951       Tile[x][y] = EL_AMOEBA_GROWING;
7952       Store[x][y] = EL_AMOEBA_WET;
7953     }
7954     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7955               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7956              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7957              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7958     {
7959       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7960                                 (IS_FREE(x - 1, y + 1) ||
7961                                  Tile[x - 1][y + 1] == EL_ACID));
7962       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7963                                 (IS_FREE(x + 1, y + 1) ||
7964                                  Tile[x + 1][y + 1] == EL_ACID));
7965       boolean can_fall_any  = (can_fall_left || can_fall_right);
7966       boolean can_fall_both = (can_fall_left && can_fall_right);
7967       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7968
7969       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7970       {
7971         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7972           can_fall_right = FALSE;
7973         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7974           can_fall_left = FALSE;
7975         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7976           can_fall_right = FALSE;
7977         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7978           can_fall_left = FALSE;
7979
7980         can_fall_any  = (can_fall_left || can_fall_right);
7981         can_fall_both = FALSE;
7982       }
7983
7984       if (can_fall_both)
7985       {
7986         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7987           can_fall_right = FALSE;       // slip down on left side
7988         else
7989           can_fall_left = !(can_fall_right = RND(2));
7990
7991         can_fall_both = FALSE;
7992       }
7993
7994       if (can_fall_any)
7995       {
7996         // if not determined otherwise, prefer left side for slipping down
7997         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7998         started_moving = TRUE;
7999       }
8000     }
8001     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8002     {
8003       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8004       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8005       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8006       int belt_dir = game.belt_dir[belt_nr];
8007
8008       if ((belt_dir == MV_LEFT  && left_is_free) ||
8009           (belt_dir == MV_RIGHT && right_is_free))
8010       {
8011         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8012
8013         InitMovingField(x, y, belt_dir);
8014         started_moving = TRUE;
8015
8016         Pushed[x][y] = TRUE;
8017         Pushed[nextx][y] = TRUE;
8018
8019         GfxAction[x][y] = ACTION_DEFAULT;
8020       }
8021       else
8022       {
8023         MovDir[x][y] = 0;       // if element was moving, stop it
8024       }
8025     }
8026   }
8027
8028   // not "else if" because of elements that can fall and move (EL_SPRING)
8029   if (CAN_MOVE(element) && !started_moving)
8030   {
8031     int move_pattern = element_info[element].move_pattern;
8032     int newx, newy;
8033
8034     Moving2Blocked(x, y, &newx, &newy);
8035
8036     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8037       return;
8038
8039     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8040         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8041     {
8042       WasJustMoving[x][y] = 0;
8043       CheckCollision[x][y] = 0;
8044
8045       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8046
8047       if (Tile[x][y] != element)        // element has changed
8048         return;
8049     }
8050
8051     if (!MovDelay[x][y])        // start new movement phase
8052     {
8053       // all objects that can change their move direction after each step
8054       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8055
8056       if (element != EL_YAMYAM &&
8057           element != EL_DARK_YAMYAM &&
8058           element != EL_PACMAN &&
8059           !(move_pattern & MV_ANY_DIRECTION) &&
8060           move_pattern != MV_TURNING_LEFT &&
8061           move_pattern != MV_TURNING_RIGHT &&
8062           move_pattern != MV_TURNING_LEFT_RIGHT &&
8063           move_pattern != MV_TURNING_RIGHT_LEFT &&
8064           move_pattern != MV_TURNING_RANDOM)
8065       {
8066         TurnRound(x, y);
8067
8068         if (MovDelay[x][y] && (element == EL_BUG ||
8069                                element == EL_SPACESHIP ||
8070                                element == EL_SP_SNIKSNAK ||
8071                                element == EL_SP_ELECTRON ||
8072                                element == EL_MOLE))
8073           TEST_DrawLevelField(x, y);
8074       }
8075     }
8076
8077     if (MovDelay[x][y])         // wait some time before next movement
8078     {
8079       MovDelay[x][y]--;
8080
8081       if (element == EL_ROBOT ||
8082           element == EL_YAMYAM ||
8083           element == EL_DARK_YAMYAM)
8084       {
8085         DrawLevelElementAnimationIfNeeded(x, y, element);
8086         PlayLevelSoundAction(x, y, ACTION_WAITING);
8087       }
8088       else if (element == EL_SP_ELECTRON)
8089         DrawLevelElementAnimationIfNeeded(x, y, element);
8090       else if (element == EL_DRAGON)
8091       {
8092         int i;
8093         int dir = MovDir[x][y];
8094         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8095         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8096         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8097                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8098                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8099                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8100         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8101
8102         GfxAction[x][y] = ACTION_ATTACKING;
8103
8104         if (IS_PLAYER(x, y))
8105           DrawPlayerField(x, y);
8106         else
8107           TEST_DrawLevelField(x, y);
8108
8109         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8110
8111         for (i = 1; i <= 3; i++)
8112         {
8113           int xx = x + i * dx;
8114           int yy = y + i * dy;
8115           int sx = SCREENX(xx);
8116           int sy = SCREENY(yy);
8117           int flame_graphic = graphic + (i - 1);
8118
8119           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8120             break;
8121
8122           if (MovDelay[x][y])
8123           {
8124             int flamed = MovingOrBlocked2Element(xx, yy);
8125
8126             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8127               Bang(xx, yy);
8128             else
8129               RemoveMovingField(xx, yy);
8130
8131             ChangeDelay[xx][yy] = 0;
8132
8133             Tile[xx][yy] = EL_FLAMES;
8134
8135             if (IN_SCR_FIELD(sx, sy))
8136             {
8137               TEST_DrawLevelFieldCrumbled(xx, yy);
8138               DrawGraphic(sx, sy, flame_graphic, frame);
8139             }
8140           }
8141           else
8142           {
8143             if (Tile[xx][yy] == EL_FLAMES)
8144               Tile[xx][yy] = EL_EMPTY;
8145             TEST_DrawLevelField(xx, yy);
8146           }
8147         }
8148       }
8149
8150       if (MovDelay[x][y])       // element still has to wait some time
8151       {
8152         PlayLevelSoundAction(x, y, ACTION_WAITING);
8153
8154         return;
8155       }
8156     }
8157
8158     // now make next step
8159
8160     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8161
8162     if (DONT_COLLIDE_WITH(element) &&
8163         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8164         !PLAYER_ENEMY_PROTECTED(newx, newy))
8165     {
8166       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8167
8168       return;
8169     }
8170
8171     else if (CAN_MOVE_INTO_ACID(element) &&
8172              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8173              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8174              (MovDir[x][y] == MV_DOWN ||
8175               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8176     {
8177       SplashAcid(newx, newy);
8178       Store[x][y] = EL_ACID;
8179     }
8180     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8181     {
8182       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8183           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8184           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8185           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8186       {
8187         RemoveField(x, y);
8188         TEST_DrawLevelField(x, y);
8189
8190         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8191         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8192           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8193
8194         game.friends_still_needed--;
8195         if (!game.friends_still_needed &&
8196             !game.GameOver &&
8197             game.all_players_gone)
8198           LevelSolved();
8199
8200         return;
8201       }
8202       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8203       {
8204         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8205           TEST_DrawLevelField(newx, newy);
8206         else
8207           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8208       }
8209       else if (!IS_FREE(newx, newy))
8210       {
8211         GfxAction[x][y] = ACTION_WAITING;
8212
8213         if (IS_PLAYER(x, y))
8214           DrawPlayerField(x, y);
8215         else
8216           TEST_DrawLevelField(x, y);
8217
8218         return;
8219       }
8220     }
8221     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8222     {
8223       if (IS_FOOD_PIG(Tile[newx][newy]))
8224       {
8225         if (IS_MOVING(newx, newy))
8226           RemoveMovingField(newx, newy);
8227         else
8228         {
8229           Tile[newx][newy] = EL_EMPTY;
8230           TEST_DrawLevelField(newx, newy);
8231         }
8232
8233         PlayLevelSound(x, y, SND_PIG_DIGGING);
8234       }
8235       else if (!IS_FREE(newx, newy))
8236       {
8237         if (IS_PLAYER(x, y))
8238           DrawPlayerField(x, y);
8239         else
8240           TEST_DrawLevelField(x, y);
8241
8242         return;
8243       }
8244     }
8245     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8246     {
8247       if (Store[x][y] != EL_EMPTY)
8248       {
8249         boolean can_clone = FALSE;
8250         int xx, yy;
8251
8252         // check if element to clone is still there
8253         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8254         {
8255           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8256           {
8257             can_clone = TRUE;
8258
8259             break;
8260           }
8261         }
8262
8263         // cannot clone or target field not free anymore -- do not clone
8264         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8265           Store[x][y] = EL_EMPTY;
8266       }
8267
8268       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8269       {
8270         if (IS_MV_DIAGONAL(MovDir[x][y]))
8271         {
8272           int diagonal_move_dir = MovDir[x][y];
8273           int stored = Store[x][y];
8274           int change_delay = 8;
8275           int graphic;
8276
8277           // android is moving diagonally
8278
8279           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8280
8281           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8282           GfxElement[x][y] = EL_EMC_ANDROID;
8283           GfxAction[x][y] = ACTION_SHRINKING;
8284           GfxDir[x][y] = diagonal_move_dir;
8285           ChangeDelay[x][y] = change_delay;
8286
8287           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8288                                    GfxDir[x][y]);
8289
8290           DrawLevelGraphicAnimation(x, y, graphic);
8291           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8292
8293           if (Tile[newx][newy] == EL_ACID)
8294           {
8295             SplashAcid(newx, newy);
8296
8297             return;
8298           }
8299
8300           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8301
8302           Store[newx][newy] = EL_EMC_ANDROID;
8303           GfxElement[newx][newy] = EL_EMC_ANDROID;
8304           GfxAction[newx][newy] = ACTION_GROWING;
8305           GfxDir[newx][newy] = diagonal_move_dir;
8306           ChangeDelay[newx][newy] = change_delay;
8307
8308           graphic = el_act_dir2img(GfxElement[newx][newy],
8309                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8310
8311           DrawLevelGraphicAnimation(newx, newy, graphic);
8312           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8313
8314           return;
8315         }
8316         else
8317         {
8318           Tile[newx][newy] = EL_EMPTY;
8319           TEST_DrawLevelField(newx, newy);
8320
8321           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8322         }
8323       }
8324       else if (!IS_FREE(newx, newy))
8325       {
8326         return;
8327       }
8328     }
8329     else if (IS_CUSTOM_ELEMENT(element) &&
8330              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8331     {
8332       if (!DigFieldByCE(newx, newy, element))
8333         return;
8334
8335       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8336       {
8337         RunnerVisit[x][y] = FrameCounter;
8338         PlayerVisit[x][y] /= 8;         // expire player visit path
8339       }
8340     }
8341     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8342     {
8343       if (!IS_FREE(newx, newy))
8344       {
8345         if (IS_PLAYER(x, y))
8346           DrawPlayerField(x, y);
8347         else
8348           TEST_DrawLevelField(x, y);
8349
8350         return;
8351       }
8352       else
8353       {
8354         boolean wanna_flame = !RND(10);
8355         int dx = newx - x, dy = newy - y;
8356         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8357         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8358         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8359                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8360         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8361                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8362
8363         if ((wanna_flame ||
8364              IS_CLASSIC_ENEMY(element1) ||
8365              IS_CLASSIC_ENEMY(element2)) &&
8366             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8367             element1 != EL_FLAMES && element2 != EL_FLAMES)
8368         {
8369           ResetGfxAnimation(x, y);
8370           GfxAction[x][y] = ACTION_ATTACKING;
8371
8372           if (IS_PLAYER(x, y))
8373             DrawPlayerField(x, y);
8374           else
8375             TEST_DrawLevelField(x, y);
8376
8377           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8378
8379           MovDelay[x][y] = 50;
8380
8381           Tile[newx][newy] = EL_FLAMES;
8382           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8383             Tile[newx1][newy1] = EL_FLAMES;
8384           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8385             Tile[newx2][newy2] = EL_FLAMES;
8386
8387           return;
8388         }
8389       }
8390     }
8391     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8392              Tile[newx][newy] == EL_DIAMOND)
8393     {
8394       if (IS_MOVING(newx, newy))
8395         RemoveMovingField(newx, newy);
8396       else
8397       {
8398         Tile[newx][newy] = EL_EMPTY;
8399         TEST_DrawLevelField(newx, newy);
8400       }
8401
8402       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8403     }
8404     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8405              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8406     {
8407       if (AmoebaNr[newx][newy])
8408       {
8409         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8410         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8411             Tile[newx][newy] == EL_BD_AMOEBA)
8412           AmoebaCnt[AmoebaNr[newx][newy]]--;
8413       }
8414
8415       if (IS_MOVING(newx, newy))
8416       {
8417         RemoveMovingField(newx, newy);
8418       }
8419       else
8420       {
8421         Tile[newx][newy] = EL_EMPTY;
8422         TEST_DrawLevelField(newx, newy);
8423       }
8424
8425       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8426     }
8427     else if ((element == EL_PACMAN || element == EL_MOLE)
8428              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8429     {
8430       if (AmoebaNr[newx][newy])
8431       {
8432         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8433         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8434             Tile[newx][newy] == EL_BD_AMOEBA)
8435           AmoebaCnt[AmoebaNr[newx][newy]]--;
8436       }
8437
8438       if (element == EL_MOLE)
8439       {
8440         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8441         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8442
8443         ResetGfxAnimation(x, y);
8444         GfxAction[x][y] = ACTION_DIGGING;
8445         TEST_DrawLevelField(x, y);
8446
8447         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8448
8449         return;                         // wait for shrinking amoeba
8450       }
8451       else      // element == EL_PACMAN
8452       {
8453         Tile[newx][newy] = EL_EMPTY;
8454         TEST_DrawLevelField(newx, newy);
8455         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8456       }
8457     }
8458     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8459              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8460               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8461     {
8462       // wait for shrinking amoeba to completely disappear
8463       return;
8464     }
8465     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8466     {
8467       // object was running against a wall
8468
8469       TurnRound(x, y);
8470
8471       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8472         DrawLevelElementAnimation(x, y, element);
8473
8474       if (DONT_TOUCH(element))
8475         TestIfBadThingTouchesPlayer(x, y);
8476
8477       return;
8478     }
8479
8480     InitMovingField(x, y, MovDir[x][y]);
8481
8482     PlayLevelSoundAction(x, y, ACTION_MOVING);
8483   }
8484
8485   if (MovDir[x][y])
8486     ContinueMoving(x, y);
8487 }
8488
8489 void ContinueMoving(int x, int y)
8490 {
8491   int element = Tile[x][y];
8492   struct ElementInfo *ei = &element_info[element];
8493   int direction = MovDir[x][y];
8494   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8495   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8496   int newx = x + dx, newy = y + dy;
8497   int stored = Store[x][y];
8498   int stored_new = Store[newx][newy];
8499   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8500   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8501   boolean last_line = (newy == lev_fieldy - 1);
8502
8503   MovPos[x][y] += getElementMoveStepsize(x, y);
8504
8505   if (pushed_by_player) // special case: moving object pushed by player
8506     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8507
8508   if (ABS(MovPos[x][y]) < TILEX)
8509   {
8510     TEST_DrawLevelField(x, y);
8511
8512     return;     // element is still moving
8513   }
8514
8515   // element reached destination field
8516
8517   Tile[x][y] = EL_EMPTY;
8518   Tile[newx][newy] = element;
8519   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8520
8521   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8522   {
8523     element = Tile[newx][newy] = EL_ACID;
8524   }
8525   else if (element == EL_MOLE)
8526   {
8527     Tile[x][y] = EL_SAND;
8528
8529     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8530   }
8531   else if (element == EL_QUICKSAND_FILLING)
8532   {
8533     element = Tile[newx][newy] = get_next_element(element);
8534     Store[newx][newy] = Store[x][y];
8535   }
8536   else if (element == EL_QUICKSAND_EMPTYING)
8537   {
8538     Tile[x][y] = get_next_element(element);
8539     element = Tile[newx][newy] = Store[x][y];
8540   }
8541   else if (element == EL_QUICKSAND_FAST_FILLING)
8542   {
8543     element = Tile[newx][newy] = get_next_element(element);
8544     Store[newx][newy] = Store[x][y];
8545   }
8546   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8547   {
8548     Tile[x][y] = get_next_element(element);
8549     element = Tile[newx][newy] = Store[x][y];
8550   }
8551   else if (element == EL_MAGIC_WALL_FILLING)
8552   {
8553     element = Tile[newx][newy] = get_next_element(element);
8554     if (!game.magic_wall_active)
8555       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8556     Store[newx][newy] = Store[x][y];
8557   }
8558   else if (element == EL_MAGIC_WALL_EMPTYING)
8559   {
8560     Tile[x][y] = get_next_element(element);
8561     if (!game.magic_wall_active)
8562       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8563     element = Tile[newx][newy] = Store[x][y];
8564
8565     InitField(newx, newy, FALSE);
8566   }
8567   else if (element == EL_BD_MAGIC_WALL_FILLING)
8568   {
8569     element = Tile[newx][newy] = get_next_element(element);
8570     if (!game.magic_wall_active)
8571       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8572     Store[newx][newy] = Store[x][y];
8573   }
8574   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8575   {
8576     Tile[x][y] = get_next_element(element);
8577     if (!game.magic_wall_active)
8578       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8579     element = Tile[newx][newy] = Store[x][y];
8580
8581     InitField(newx, newy, FALSE);
8582   }
8583   else if (element == EL_DC_MAGIC_WALL_FILLING)
8584   {
8585     element = Tile[newx][newy] = get_next_element(element);
8586     if (!game.magic_wall_active)
8587       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8588     Store[newx][newy] = Store[x][y];
8589   }
8590   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8591   {
8592     Tile[x][y] = get_next_element(element);
8593     if (!game.magic_wall_active)
8594       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8595     element = Tile[newx][newy] = Store[x][y];
8596
8597     InitField(newx, newy, FALSE);
8598   }
8599   else if (element == EL_AMOEBA_DROPPING)
8600   {
8601     Tile[x][y] = get_next_element(element);
8602     element = Tile[newx][newy] = Store[x][y];
8603   }
8604   else if (element == EL_SOKOBAN_OBJECT)
8605   {
8606     if (Back[x][y])
8607       Tile[x][y] = Back[x][y];
8608
8609     if (Back[newx][newy])
8610       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8611
8612     Back[x][y] = Back[newx][newy] = 0;
8613   }
8614
8615   Store[x][y] = EL_EMPTY;
8616   MovPos[x][y] = 0;
8617   MovDir[x][y] = 0;
8618   MovDelay[x][y] = 0;
8619
8620   MovDelay[newx][newy] = 0;
8621
8622   if (CAN_CHANGE_OR_HAS_ACTION(element))
8623   {
8624     // copy element change control values to new field
8625     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8626     ChangePage[newx][newy]  = ChangePage[x][y];
8627     ChangeCount[newx][newy] = ChangeCount[x][y];
8628     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8629   }
8630
8631   CustomValue[newx][newy] = CustomValue[x][y];
8632
8633   ChangeDelay[x][y] = 0;
8634   ChangePage[x][y] = -1;
8635   ChangeCount[x][y] = 0;
8636   ChangeEvent[x][y] = -1;
8637
8638   CustomValue[x][y] = 0;
8639
8640   // copy animation control values to new field
8641   GfxFrame[newx][newy]  = GfxFrame[x][y];
8642   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8643   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8644   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8645
8646   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8647
8648   // some elements can leave other elements behind after moving
8649   if (ei->move_leave_element != EL_EMPTY &&
8650       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8651       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8652   {
8653     int move_leave_element = ei->move_leave_element;
8654
8655     // this makes it possible to leave the removed element again
8656     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8657       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8658
8659     Tile[x][y] = move_leave_element;
8660
8661     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8662       MovDir[x][y] = direction;
8663
8664     InitField(x, y, FALSE);
8665
8666     if (GFX_CRUMBLED(Tile[x][y]))
8667       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8668
8669     if (ELEM_IS_PLAYER(move_leave_element))
8670       RelocatePlayer(x, y, move_leave_element);
8671   }
8672
8673   // do this after checking for left-behind element
8674   ResetGfxAnimation(x, y);      // reset animation values for old field
8675
8676   if (!CAN_MOVE(element) ||
8677       (CAN_FALL(element) && direction == MV_DOWN &&
8678        (element == EL_SPRING ||
8679         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8680         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8681     GfxDir[x][y] = MovDir[newx][newy] = 0;
8682
8683   TEST_DrawLevelField(x, y);
8684   TEST_DrawLevelField(newx, newy);
8685
8686   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8687
8688   // prevent pushed element from moving on in pushed direction
8689   if (pushed_by_player && CAN_MOVE(element) &&
8690       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8691       !(element_info[element].move_pattern & direction))
8692     TurnRound(newx, newy);
8693
8694   // prevent elements on conveyor belt from moving on in last direction
8695   if (pushed_by_conveyor && CAN_FALL(element) &&
8696       direction & MV_HORIZONTAL)
8697     MovDir[newx][newy] = 0;
8698
8699   if (!pushed_by_player)
8700   {
8701     int nextx = newx + dx, nexty = newy + dy;
8702     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8703
8704     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8705
8706     if (CAN_FALL(element) && direction == MV_DOWN)
8707       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8708
8709     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8710       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8711
8712     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8713       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8714   }
8715
8716   if (DONT_TOUCH(element))      // object may be nasty to player or others
8717   {
8718     TestIfBadThingTouchesPlayer(newx, newy);
8719     TestIfBadThingTouchesFriend(newx, newy);
8720
8721     if (!IS_CUSTOM_ELEMENT(element))
8722       TestIfBadThingTouchesOtherBadThing(newx, newy);
8723   }
8724   else if (element == EL_PENGUIN)
8725     TestIfFriendTouchesBadThing(newx, newy);
8726
8727   if (DONT_GET_HIT_BY(element))
8728   {
8729     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8730   }
8731
8732   // give the player one last chance (one more frame) to move away
8733   if (CAN_FALL(element) && direction == MV_DOWN &&
8734       (last_line || (!IS_FREE(x, newy + 1) &&
8735                      (!IS_PLAYER(x, newy + 1) ||
8736                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8737     Impact(x, newy);
8738
8739   if (pushed_by_player && !game.use_change_when_pushing_bug)
8740   {
8741     int push_side = MV_DIR_OPPOSITE(direction);
8742     struct PlayerInfo *player = PLAYERINFO(x, y);
8743
8744     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8745                                player->index_bit, push_side);
8746     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8747                                         player->index_bit, push_side);
8748   }
8749
8750   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8751     MovDelay[newx][newy] = 1;
8752
8753   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8754
8755   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8756   TestIfElementHitsCustomElement(newx, newy, direction);
8757   TestIfPlayerTouchesCustomElement(newx, newy);
8758   TestIfElementTouchesCustomElement(newx, newy);
8759
8760   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8761       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8762     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8763                              MV_DIR_OPPOSITE(direction));
8764 }
8765
8766 int AmoebaNeighbourNr(int ax, int ay)
8767 {
8768   int i;
8769   int element = Tile[ax][ay];
8770   int group_nr = 0;
8771   static int xy[4][2] =
8772   {
8773     { 0, -1 },
8774     { -1, 0 },
8775     { +1, 0 },
8776     { 0, +1 }
8777   };
8778
8779   for (i = 0; i < NUM_DIRECTIONS; i++)
8780   {
8781     int x = ax + xy[i][0];
8782     int y = ay + xy[i][1];
8783
8784     if (!IN_LEV_FIELD(x, y))
8785       continue;
8786
8787     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8788       group_nr = AmoebaNr[x][y];
8789   }
8790
8791   return group_nr;
8792 }
8793
8794 static void AmoebaMerge(int ax, int ay)
8795 {
8796   int i, x, y, xx, yy;
8797   int new_group_nr = AmoebaNr[ax][ay];
8798   static int xy[4][2] =
8799   {
8800     { 0, -1 },
8801     { -1, 0 },
8802     { +1, 0 },
8803     { 0, +1 }
8804   };
8805
8806   if (new_group_nr == 0)
8807     return;
8808
8809   for (i = 0; i < NUM_DIRECTIONS; i++)
8810   {
8811     x = ax + xy[i][0];
8812     y = ay + xy[i][1];
8813
8814     if (!IN_LEV_FIELD(x, y))
8815       continue;
8816
8817     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8818          Tile[x][y] == EL_BD_AMOEBA ||
8819          Tile[x][y] == EL_AMOEBA_DEAD) &&
8820         AmoebaNr[x][y] != new_group_nr)
8821     {
8822       int old_group_nr = AmoebaNr[x][y];
8823
8824       if (old_group_nr == 0)
8825         return;
8826
8827       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8828       AmoebaCnt[old_group_nr] = 0;
8829       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8830       AmoebaCnt2[old_group_nr] = 0;
8831
8832       SCAN_PLAYFIELD(xx, yy)
8833       {
8834         if (AmoebaNr[xx][yy] == old_group_nr)
8835           AmoebaNr[xx][yy] = new_group_nr;
8836       }
8837     }
8838   }
8839 }
8840
8841 void AmoebaToDiamond(int ax, int ay)
8842 {
8843   int i, x, y;
8844
8845   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8846   {
8847     int group_nr = AmoebaNr[ax][ay];
8848
8849 #ifdef DEBUG
8850     if (group_nr == 0)
8851     {
8852       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8853       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8854
8855       return;
8856     }
8857 #endif
8858
8859     SCAN_PLAYFIELD(x, y)
8860     {
8861       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8862       {
8863         AmoebaNr[x][y] = 0;
8864         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8865       }
8866     }
8867
8868     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8869                             SND_AMOEBA_TURNING_TO_GEM :
8870                             SND_AMOEBA_TURNING_TO_ROCK));
8871     Bang(ax, ay);
8872   }
8873   else
8874   {
8875     static int xy[4][2] =
8876     {
8877       { 0, -1 },
8878       { -1, 0 },
8879       { +1, 0 },
8880       { 0, +1 }
8881     };
8882
8883     for (i = 0; i < NUM_DIRECTIONS; i++)
8884     {
8885       x = ax + xy[i][0];
8886       y = ay + xy[i][1];
8887
8888       if (!IN_LEV_FIELD(x, y))
8889         continue;
8890
8891       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8892       {
8893         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8894                               SND_AMOEBA_TURNING_TO_GEM :
8895                               SND_AMOEBA_TURNING_TO_ROCK));
8896         Bang(x, y);
8897       }
8898     }
8899   }
8900 }
8901
8902 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8903 {
8904   int x, y;
8905   int group_nr = AmoebaNr[ax][ay];
8906   boolean done = FALSE;
8907
8908 #ifdef DEBUG
8909   if (group_nr == 0)
8910   {
8911     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8912     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8913
8914     return;
8915   }
8916 #endif
8917
8918   SCAN_PLAYFIELD(x, y)
8919   {
8920     if (AmoebaNr[x][y] == group_nr &&
8921         (Tile[x][y] == EL_AMOEBA_DEAD ||
8922          Tile[x][y] == EL_BD_AMOEBA ||
8923          Tile[x][y] == EL_AMOEBA_GROWING))
8924     {
8925       AmoebaNr[x][y] = 0;
8926       Tile[x][y] = new_element;
8927       InitField(x, y, FALSE);
8928       TEST_DrawLevelField(x, y);
8929       done = TRUE;
8930     }
8931   }
8932
8933   if (done)
8934     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8935                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8936                             SND_BD_AMOEBA_TURNING_TO_GEM));
8937 }
8938
8939 static void AmoebaGrowing(int x, int y)
8940 {
8941   static unsigned int sound_delay = 0;
8942   static unsigned int sound_delay_value = 0;
8943
8944   if (!MovDelay[x][y])          // start new growing cycle
8945   {
8946     MovDelay[x][y] = 7;
8947
8948     if (DelayReached(&sound_delay, sound_delay_value))
8949     {
8950       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8951       sound_delay_value = 30;
8952     }
8953   }
8954
8955   if (MovDelay[x][y])           // wait some time before growing bigger
8956   {
8957     MovDelay[x][y]--;
8958     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8959     {
8960       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8961                                            6 - MovDelay[x][y]);
8962
8963       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8964     }
8965
8966     if (!MovDelay[x][y])
8967     {
8968       Tile[x][y] = Store[x][y];
8969       Store[x][y] = 0;
8970       TEST_DrawLevelField(x, y);
8971     }
8972   }
8973 }
8974
8975 static void AmoebaShrinking(int x, int y)
8976 {
8977   static unsigned int sound_delay = 0;
8978   static unsigned int sound_delay_value = 0;
8979
8980   if (!MovDelay[x][y])          // start new shrinking cycle
8981   {
8982     MovDelay[x][y] = 7;
8983
8984     if (DelayReached(&sound_delay, sound_delay_value))
8985       sound_delay_value = 30;
8986   }
8987
8988   if (MovDelay[x][y])           // wait some time before shrinking
8989   {
8990     MovDelay[x][y]--;
8991     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8992     {
8993       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8994                                            6 - MovDelay[x][y]);
8995
8996       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8997     }
8998
8999     if (!MovDelay[x][y])
9000     {
9001       Tile[x][y] = EL_EMPTY;
9002       TEST_DrawLevelField(x, y);
9003
9004       // don't let mole enter this field in this cycle;
9005       // (give priority to objects falling to this field from above)
9006       Stop[x][y] = TRUE;
9007     }
9008   }
9009 }
9010
9011 static void AmoebaReproduce(int ax, int ay)
9012 {
9013   int i;
9014   int element = Tile[ax][ay];
9015   int graphic = el2img(element);
9016   int newax = ax, neway = ay;
9017   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9018   static int xy[4][2] =
9019   {
9020     { 0, -1 },
9021     { -1, 0 },
9022     { +1, 0 },
9023     { 0, +1 }
9024   };
9025
9026   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9027   {
9028     Tile[ax][ay] = EL_AMOEBA_DEAD;
9029     TEST_DrawLevelField(ax, ay);
9030     return;
9031   }
9032
9033   if (IS_ANIMATED(graphic))
9034     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9035
9036   if (!MovDelay[ax][ay])        // start making new amoeba field
9037     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9038
9039   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9040   {
9041     MovDelay[ax][ay]--;
9042     if (MovDelay[ax][ay])
9043       return;
9044   }
9045
9046   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9047   {
9048     int start = RND(4);
9049     int x = ax + xy[start][0];
9050     int y = ay + xy[start][1];
9051
9052     if (!IN_LEV_FIELD(x, y))
9053       return;
9054
9055     if (IS_FREE(x, y) ||
9056         CAN_GROW_INTO(Tile[x][y]) ||
9057         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9058         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9059     {
9060       newax = x;
9061       neway = y;
9062     }
9063
9064     if (newax == ax && neway == ay)
9065       return;
9066   }
9067   else                          // normal or "filled" (BD style) amoeba
9068   {
9069     int start = RND(4);
9070     boolean waiting_for_player = FALSE;
9071
9072     for (i = 0; i < NUM_DIRECTIONS; i++)
9073     {
9074       int j = (start + i) % 4;
9075       int x = ax + xy[j][0];
9076       int y = ay + xy[j][1];
9077
9078       if (!IN_LEV_FIELD(x, y))
9079         continue;
9080
9081       if (IS_FREE(x, y) ||
9082           CAN_GROW_INTO(Tile[x][y]) ||
9083           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9084           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9085       {
9086         newax = x;
9087         neway = y;
9088         break;
9089       }
9090       else if (IS_PLAYER(x, y))
9091         waiting_for_player = TRUE;
9092     }
9093
9094     if (newax == ax && neway == ay)             // amoeba cannot grow
9095     {
9096       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9097       {
9098         Tile[ax][ay] = EL_AMOEBA_DEAD;
9099         TEST_DrawLevelField(ax, ay);
9100         AmoebaCnt[AmoebaNr[ax][ay]]--;
9101
9102         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9103         {
9104           if (element == EL_AMOEBA_FULL)
9105             AmoebaToDiamond(ax, ay);
9106           else if (element == EL_BD_AMOEBA)
9107             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9108         }
9109       }
9110       return;
9111     }
9112     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9113     {
9114       // amoeba gets larger by growing in some direction
9115
9116       int new_group_nr = AmoebaNr[ax][ay];
9117
9118 #ifdef DEBUG
9119   if (new_group_nr == 0)
9120   {
9121     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9122           newax, neway);
9123     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9124
9125     return;
9126   }
9127 #endif
9128
9129       AmoebaNr[newax][neway] = new_group_nr;
9130       AmoebaCnt[new_group_nr]++;
9131       AmoebaCnt2[new_group_nr]++;
9132
9133       // if amoeba touches other amoeba(s) after growing, unify them
9134       AmoebaMerge(newax, neway);
9135
9136       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9137       {
9138         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9139         return;
9140       }
9141     }
9142   }
9143
9144   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9145       (neway == lev_fieldy - 1 && newax != ax))
9146   {
9147     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9148     Store[newax][neway] = element;
9149   }
9150   else if (neway == ay || element == EL_EMC_DRIPPER)
9151   {
9152     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9153
9154     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9155   }
9156   else
9157   {
9158     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9159     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9160     Store[ax][ay] = EL_AMOEBA_DROP;
9161     ContinueMoving(ax, ay);
9162     return;
9163   }
9164
9165   TEST_DrawLevelField(newax, neway);
9166 }
9167
9168 static void Life(int ax, int ay)
9169 {
9170   int x1, y1, x2, y2;
9171   int life_time = 40;
9172   int element = Tile[ax][ay];
9173   int graphic = el2img(element);
9174   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9175                          level.biomaze);
9176   boolean changed = FALSE;
9177
9178   if (IS_ANIMATED(graphic))
9179     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9180
9181   if (Stop[ax][ay])
9182     return;
9183
9184   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9185     MovDelay[ax][ay] = life_time;
9186
9187   if (MovDelay[ax][ay])         // wait some time before next cycle
9188   {
9189     MovDelay[ax][ay]--;
9190     if (MovDelay[ax][ay])
9191       return;
9192   }
9193
9194   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9195   {
9196     int xx = ax+x1, yy = ay+y1;
9197     int old_element = Tile[xx][yy];
9198     int num_neighbours = 0;
9199
9200     if (!IN_LEV_FIELD(xx, yy))
9201       continue;
9202
9203     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9204     {
9205       int x = xx+x2, y = yy+y2;
9206
9207       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9208         continue;
9209
9210       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9211       boolean is_neighbour = FALSE;
9212
9213       if (level.use_life_bugs)
9214         is_neighbour =
9215           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9216            (IS_FREE(x, y)                             &&  Stop[x][y]));
9217       else
9218         is_neighbour =
9219           (Last[x][y] == element || is_player_cell);
9220
9221       if (is_neighbour)
9222         num_neighbours++;
9223     }
9224
9225     boolean is_free = FALSE;
9226
9227     if (level.use_life_bugs)
9228       is_free = (IS_FREE(xx, yy));
9229     else
9230       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9231
9232     if (xx == ax && yy == ay)           // field in the middle
9233     {
9234       if (num_neighbours < life_parameter[0] ||
9235           num_neighbours > life_parameter[1])
9236       {
9237         Tile[xx][yy] = EL_EMPTY;
9238         if (Tile[xx][yy] != old_element)
9239           TEST_DrawLevelField(xx, yy);
9240         Stop[xx][yy] = TRUE;
9241         changed = TRUE;
9242       }
9243     }
9244     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9245     {                                   // free border field
9246       if (num_neighbours >= life_parameter[2] &&
9247           num_neighbours <= life_parameter[3])
9248       {
9249         Tile[xx][yy] = element;
9250         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9251         if (Tile[xx][yy] != old_element)
9252           TEST_DrawLevelField(xx, yy);
9253         Stop[xx][yy] = TRUE;
9254         changed = TRUE;
9255       }
9256     }
9257   }
9258
9259   if (changed)
9260     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9261                    SND_GAME_OF_LIFE_GROWING);
9262 }
9263
9264 static void InitRobotWheel(int x, int y)
9265 {
9266   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9267 }
9268
9269 static void RunRobotWheel(int x, int y)
9270 {
9271   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9272 }
9273
9274 static void StopRobotWheel(int x, int y)
9275 {
9276   if (game.robot_wheel_x == x &&
9277       game.robot_wheel_y == y)
9278   {
9279     game.robot_wheel_x = -1;
9280     game.robot_wheel_y = -1;
9281     game.robot_wheel_active = FALSE;
9282   }
9283 }
9284
9285 static void InitTimegateWheel(int x, int y)
9286 {
9287   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9288 }
9289
9290 static void RunTimegateWheel(int x, int y)
9291 {
9292   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9293 }
9294
9295 static void InitMagicBallDelay(int x, int y)
9296 {
9297   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9298 }
9299
9300 static void ActivateMagicBall(int bx, int by)
9301 {
9302   int x, y;
9303
9304   if (level.ball_random)
9305   {
9306     int pos_border = RND(8);    // select one of the eight border elements
9307     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9308     int xx = pos_content % 3;
9309     int yy = pos_content / 3;
9310
9311     x = bx - 1 + xx;
9312     y = by - 1 + yy;
9313
9314     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9315       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9316   }
9317   else
9318   {
9319     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9320     {
9321       int xx = x - bx + 1;
9322       int yy = y - by + 1;
9323
9324       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9325         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9326     }
9327   }
9328
9329   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9330 }
9331
9332 static void CheckExit(int x, int y)
9333 {
9334   if (game.gems_still_needed > 0 ||
9335       game.sokoban_fields_still_needed > 0 ||
9336       game.sokoban_objects_still_needed > 0 ||
9337       game.lights_still_needed > 0)
9338   {
9339     int element = Tile[x][y];
9340     int graphic = el2img(element);
9341
9342     if (IS_ANIMATED(graphic))
9343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9344
9345     return;
9346   }
9347
9348   // do not re-open exit door closed after last player
9349   if (game.all_players_gone)
9350     return;
9351
9352   Tile[x][y] = EL_EXIT_OPENING;
9353
9354   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9355 }
9356
9357 static void CheckExitEM(int x, int y)
9358 {
9359   if (game.gems_still_needed > 0 ||
9360       game.sokoban_fields_still_needed > 0 ||
9361       game.sokoban_objects_still_needed > 0 ||
9362       game.lights_still_needed > 0)
9363   {
9364     int element = Tile[x][y];
9365     int graphic = el2img(element);
9366
9367     if (IS_ANIMATED(graphic))
9368       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9369
9370     return;
9371   }
9372
9373   // do not re-open exit door closed after last player
9374   if (game.all_players_gone)
9375     return;
9376
9377   Tile[x][y] = EL_EM_EXIT_OPENING;
9378
9379   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9380 }
9381
9382 static void CheckExitSteel(int x, int y)
9383 {
9384   if (game.gems_still_needed > 0 ||
9385       game.sokoban_fields_still_needed > 0 ||
9386       game.sokoban_objects_still_needed > 0 ||
9387       game.lights_still_needed > 0)
9388   {
9389     int element = Tile[x][y];
9390     int graphic = el2img(element);
9391
9392     if (IS_ANIMATED(graphic))
9393       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9394
9395     return;
9396   }
9397
9398   // do not re-open exit door closed after last player
9399   if (game.all_players_gone)
9400     return;
9401
9402   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9403
9404   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9405 }
9406
9407 static void CheckExitSteelEM(int x, int y)
9408 {
9409   if (game.gems_still_needed > 0 ||
9410       game.sokoban_fields_still_needed > 0 ||
9411       game.sokoban_objects_still_needed > 0 ||
9412       game.lights_still_needed > 0)
9413   {
9414     int element = Tile[x][y];
9415     int graphic = el2img(element);
9416
9417     if (IS_ANIMATED(graphic))
9418       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9419
9420     return;
9421   }
9422
9423   // do not re-open exit door closed after last player
9424   if (game.all_players_gone)
9425     return;
9426
9427   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9428
9429   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9430 }
9431
9432 static void CheckExitSP(int x, int y)
9433 {
9434   if (game.gems_still_needed > 0)
9435   {
9436     int element = Tile[x][y];
9437     int graphic = el2img(element);
9438
9439     if (IS_ANIMATED(graphic))
9440       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9441
9442     return;
9443   }
9444
9445   // do not re-open exit door closed after last player
9446   if (game.all_players_gone)
9447     return;
9448
9449   Tile[x][y] = EL_SP_EXIT_OPENING;
9450
9451   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9452 }
9453
9454 static void CloseAllOpenTimegates(void)
9455 {
9456   int x, y;
9457
9458   SCAN_PLAYFIELD(x, y)
9459   {
9460     int element = Tile[x][y];
9461
9462     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9463     {
9464       Tile[x][y] = EL_TIMEGATE_CLOSING;
9465
9466       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9467     }
9468   }
9469 }
9470
9471 static void DrawTwinkleOnField(int x, int y)
9472 {
9473   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9474     return;
9475
9476   if (Tile[x][y] == EL_BD_DIAMOND)
9477     return;
9478
9479   if (MovDelay[x][y] == 0)      // next animation frame
9480     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9481
9482   if (MovDelay[x][y] != 0)      // wait some time before next frame
9483   {
9484     MovDelay[x][y]--;
9485
9486     DrawLevelElementAnimation(x, y, Tile[x][y]);
9487
9488     if (MovDelay[x][y] != 0)
9489     {
9490       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9491                                            10 - MovDelay[x][y]);
9492
9493       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9494     }
9495   }
9496 }
9497
9498 static void MauerWaechst(int x, int y)
9499 {
9500   int delay = 6;
9501
9502   if (!MovDelay[x][y])          // next animation frame
9503     MovDelay[x][y] = 3 * delay;
9504
9505   if (MovDelay[x][y])           // wait some time before next frame
9506   {
9507     MovDelay[x][y]--;
9508
9509     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9510     {
9511       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9512       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9513
9514       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9515     }
9516
9517     if (!MovDelay[x][y])
9518     {
9519       if (MovDir[x][y] == MV_LEFT)
9520       {
9521         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9522           TEST_DrawLevelField(x - 1, y);
9523       }
9524       else if (MovDir[x][y] == MV_RIGHT)
9525       {
9526         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9527           TEST_DrawLevelField(x + 1, y);
9528       }
9529       else if (MovDir[x][y] == MV_UP)
9530       {
9531         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9532           TEST_DrawLevelField(x, y - 1);
9533       }
9534       else
9535       {
9536         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9537           TEST_DrawLevelField(x, y + 1);
9538       }
9539
9540       Tile[x][y] = Store[x][y];
9541       Store[x][y] = 0;
9542       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9543       TEST_DrawLevelField(x, y);
9544     }
9545   }
9546 }
9547
9548 static void MauerAbleger(int ax, int ay)
9549 {
9550   int element = Tile[ax][ay];
9551   int graphic = el2img(element);
9552   boolean oben_frei = FALSE, unten_frei = FALSE;
9553   boolean links_frei = FALSE, rechts_frei = FALSE;
9554   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9555   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9556   boolean new_wall = FALSE;
9557
9558   if (IS_ANIMATED(graphic))
9559     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9560
9561   if (!MovDelay[ax][ay])        // start building new wall
9562     MovDelay[ax][ay] = 6;
9563
9564   if (MovDelay[ax][ay])         // wait some time before building new wall
9565   {
9566     MovDelay[ax][ay]--;
9567     if (MovDelay[ax][ay])
9568       return;
9569   }
9570
9571   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9572     oben_frei = TRUE;
9573   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9574     unten_frei = TRUE;
9575   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9576     links_frei = TRUE;
9577   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9578     rechts_frei = TRUE;
9579
9580   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9581       element == EL_EXPANDABLE_WALL_ANY)
9582   {
9583     if (oben_frei)
9584     {
9585       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9586       Store[ax][ay-1] = element;
9587       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9588       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9589         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9590                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9591       new_wall = TRUE;
9592     }
9593     if (unten_frei)
9594     {
9595       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9596       Store[ax][ay+1] = element;
9597       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9598       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9599         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9600                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9601       new_wall = TRUE;
9602     }
9603   }
9604
9605   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9606       element == EL_EXPANDABLE_WALL_ANY ||
9607       element == EL_EXPANDABLE_WALL ||
9608       element == EL_BD_EXPANDABLE_WALL)
9609   {
9610     if (links_frei)
9611     {
9612       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9613       Store[ax-1][ay] = element;
9614       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9615       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9616         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9617                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9618       new_wall = TRUE;
9619     }
9620
9621     if (rechts_frei)
9622     {
9623       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9624       Store[ax+1][ay] = element;
9625       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9626       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9627         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9628                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9629       new_wall = TRUE;
9630     }
9631   }
9632
9633   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9634     TEST_DrawLevelField(ax, ay);
9635
9636   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9637     oben_massiv = TRUE;
9638   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9639     unten_massiv = TRUE;
9640   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9641     links_massiv = TRUE;
9642   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9643     rechts_massiv = TRUE;
9644
9645   if (((oben_massiv && unten_massiv) ||
9646        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9647        element == EL_EXPANDABLE_WALL) &&
9648       ((links_massiv && rechts_massiv) ||
9649        element == EL_EXPANDABLE_WALL_VERTICAL))
9650     Tile[ax][ay] = EL_WALL;
9651
9652   if (new_wall)
9653     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9654 }
9655
9656 static void MauerAblegerStahl(int ax, int ay)
9657 {
9658   int element = Tile[ax][ay];
9659   int graphic = el2img(element);
9660   boolean oben_frei = FALSE, unten_frei = FALSE;
9661   boolean links_frei = FALSE, rechts_frei = FALSE;
9662   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9663   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9664   boolean new_wall = FALSE;
9665
9666   if (IS_ANIMATED(graphic))
9667     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9668
9669   if (!MovDelay[ax][ay])        // start building new wall
9670     MovDelay[ax][ay] = 6;
9671
9672   if (MovDelay[ax][ay])         // wait some time before building new wall
9673   {
9674     MovDelay[ax][ay]--;
9675     if (MovDelay[ax][ay])
9676       return;
9677   }
9678
9679   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9680     oben_frei = TRUE;
9681   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9682     unten_frei = TRUE;
9683   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9684     links_frei = TRUE;
9685   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9686     rechts_frei = TRUE;
9687
9688   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9689       element == EL_EXPANDABLE_STEELWALL_ANY)
9690   {
9691     if (oben_frei)
9692     {
9693       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9694       Store[ax][ay-1] = element;
9695       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9696       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9697         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9698                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9699       new_wall = TRUE;
9700     }
9701     if (unten_frei)
9702     {
9703       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9704       Store[ax][ay+1] = element;
9705       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9706       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9707         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9708                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9709       new_wall = TRUE;
9710     }
9711   }
9712
9713   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9714       element == EL_EXPANDABLE_STEELWALL_ANY)
9715   {
9716     if (links_frei)
9717     {
9718       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9719       Store[ax-1][ay] = element;
9720       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9721       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9722         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9723                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9724       new_wall = TRUE;
9725     }
9726
9727     if (rechts_frei)
9728     {
9729       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9730       Store[ax+1][ay] = element;
9731       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9732       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9733         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9734                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9735       new_wall = TRUE;
9736     }
9737   }
9738
9739   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9740     oben_massiv = TRUE;
9741   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9742     unten_massiv = TRUE;
9743   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9744     links_massiv = TRUE;
9745   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9746     rechts_massiv = TRUE;
9747
9748   if (((oben_massiv && unten_massiv) ||
9749        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9750       ((links_massiv && rechts_massiv) ||
9751        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9752     Tile[ax][ay] = EL_STEELWALL;
9753
9754   if (new_wall)
9755     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9756 }
9757
9758 static void CheckForDragon(int x, int y)
9759 {
9760   int i, j;
9761   boolean dragon_found = FALSE;
9762   static int xy[4][2] =
9763   {
9764     { 0, -1 },
9765     { -1, 0 },
9766     { +1, 0 },
9767     { 0, +1 }
9768   };
9769
9770   for (i = 0; i < NUM_DIRECTIONS; i++)
9771   {
9772     for (j = 0; j < 4; j++)
9773     {
9774       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9775
9776       if (IN_LEV_FIELD(xx, yy) &&
9777           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9778       {
9779         if (Tile[xx][yy] == EL_DRAGON)
9780           dragon_found = TRUE;
9781       }
9782       else
9783         break;
9784     }
9785   }
9786
9787   if (!dragon_found)
9788   {
9789     for (i = 0; i < NUM_DIRECTIONS; i++)
9790     {
9791       for (j = 0; j < 3; j++)
9792       {
9793         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9794   
9795         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9796         {
9797           Tile[xx][yy] = EL_EMPTY;
9798           TEST_DrawLevelField(xx, yy);
9799         }
9800         else
9801           break;
9802       }
9803     }
9804   }
9805 }
9806
9807 static void InitBuggyBase(int x, int y)
9808 {
9809   int element = Tile[x][y];
9810   int activating_delay = FRAMES_PER_SECOND / 4;
9811
9812   ChangeDelay[x][y] =
9813     (element == EL_SP_BUGGY_BASE ?
9814      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9815      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9816      activating_delay :
9817      element == EL_SP_BUGGY_BASE_ACTIVE ?
9818      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9819 }
9820
9821 static void WarnBuggyBase(int x, int y)
9822 {
9823   int i;
9824   static int xy[4][2] =
9825   {
9826     { 0, -1 },
9827     { -1, 0 },
9828     { +1, 0 },
9829     { 0, +1 }
9830   };
9831
9832   for (i = 0; i < NUM_DIRECTIONS; i++)
9833   {
9834     int xx = x + xy[i][0];
9835     int yy = y + xy[i][1];
9836
9837     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9838     {
9839       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9840
9841       break;
9842     }
9843   }
9844 }
9845
9846 static void InitTrap(int x, int y)
9847 {
9848   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9849 }
9850
9851 static void ActivateTrap(int x, int y)
9852 {
9853   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9854 }
9855
9856 static void ChangeActiveTrap(int x, int y)
9857 {
9858   int graphic = IMG_TRAP_ACTIVE;
9859
9860   // if new animation frame was drawn, correct crumbled sand border
9861   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9862     TEST_DrawLevelFieldCrumbled(x, y);
9863 }
9864
9865 static int getSpecialActionElement(int element, int number, int base_element)
9866 {
9867   return (element != EL_EMPTY ? element :
9868           number != -1 ? base_element + number - 1 :
9869           EL_EMPTY);
9870 }
9871
9872 static int getModifiedActionNumber(int value_old, int operator, int operand,
9873                                    int value_min, int value_max)
9874 {
9875   int value_new = (operator == CA_MODE_SET      ? operand :
9876                    operator == CA_MODE_ADD      ? value_old + operand :
9877                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9878                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9879                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9880                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9881                    value_old);
9882
9883   return (value_new < value_min ? value_min :
9884           value_new > value_max ? value_max :
9885           value_new);
9886 }
9887
9888 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9889 {
9890   struct ElementInfo *ei = &element_info[element];
9891   struct ElementChangeInfo *change = &ei->change_page[page];
9892   int target_element = change->target_element;
9893   int action_type = change->action_type;
9894   int action_mode = change->action_mode;
9895   int action_arg = change->action_arg;
9896   int action_element = change->action_element;
9897   int i;
9898
9899   if (!change->has_action)
9900     return;
9901
9902   // ---------- determine action paramater values -----------------------------
9903
9904   int level_time_value =
9905     (level.time > 0 ? TimeLeft :
9906      TimePlayed);
9907
9908   int action_arg_element_raw =
9909     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9910      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9911      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9912      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9913      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9914      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9915      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9916      EL_EMPTY);
9917   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9918
9919   int action_arg_direction =
9920     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9921      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9922      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9923      change->actual_trigger_side :
9924      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9925      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9926      MV_NONE);
9927
9928   int action_arg_number_min =
9929     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9930      CA_ARG_MIN);
9931
9932   int action_arg_number_max =
9933     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9934      action_type == CA_SET_LEVEL_GEMS ? 999 :
9935      action_type == CA_SET_LEVEL_TIME ? 9999 :
9936      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9937      action_type == CA_SET_CE_VALUE ? 9999 :
9938      action_type == CA_SET_CE_SCORE ? 9999 :
9939      CA_ARG_MAX);
9940
9941   int action_arg_number_reset =
9942     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9943      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9944      action_type == CA_SET_LEVEL_TIME ? level.time :
9945      action_type == CA_SET_LEVEL_SCORE ? 0 :
9946      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9947      action_type == CA_SET_CE_SCORE ? 0 :
9948      0);
9949
9950   int action_arg_number =
9951     (action_arg <= CA_ARG_MAX ? action_arg :
9952      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9953      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9954      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9955      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9956      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9957      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9958      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9959      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9960      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9961      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9962      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9963      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9964      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9965      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9966      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9967      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9968      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9969      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9970      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9971      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9972      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9973      -1);
9974
9975   int action_arg_number_old =
9976     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9977      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9978      action_type == CA_SET_LEVEL_SCORE ? game.score :
9979      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9980      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9981      0);
9982
9983   int action_arg_number_new =
9984     getModifiedActionNumber(action_arg_number_old,
9985                             action_mode, action_arg_number,
9986                             action_arg_number_min, action_arg_number_max);
9987
9988   int trigger_player_bits =
9989     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9990      change->actual_trigger_player_bits : change->trigger_player);
9991
9992   int action_arg_player_bits =
9993     (action_arg >= CA_ARG_PLAYER_1 &&
9994      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9995      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9996      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9997      PLAYER_BITS_ANY);
9998
9999   // ---------- execute action  -----------------------------------------------
10000
10001   switch (action_type)
10002   {
10003     case CA_NO_ACTION:
10004     {
10005       return;
10006     }
10007
10008     // ---------- level actions  ----------------------------------------------
10009
10010     case CA_RESTART_LEVEL:
10011     {
10012       game.restart_level = TRUE;
10013
10014       break;
10015     }
10016
10017     case CA_SHOW_ENVELOPE:
10018     {
10019       int element = getSpecialActionElement(action_arg_element,
10020                                             action_arg_number, EL_ENVELOPE_1);
10021
10022       if (IS_ENVELOPE(element))
10023         local_player->show_envelope = element;
10024
10025       break;
10026     }
10027
10028     case CA_SET_LEVEL_TIME:
10029     {
10030       if (level.time > 0)       // only modify limited time value
10031       {
10032         TimeLeft = action_arg_number_new;
10033
10034         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10035
10036         DisplayGameControlValues();
10037
10038         if (!TimeLeft && setup.time_limit)
10039           for (i = 0; i < MAX_PLAYERS; i++)
10040             KillPlayer(&stored_player[i]);
10041       }
10042
10043       break;
10044     }
10045
10046     case CA_SET_LEVEL_SCORE:
10047     {
10048       game.score = action_arg_number_new;
10049
10050       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10051
10052       DisplayGameControlValues();
10053
10054       break;
10055     }
10056
10057     case CA_SET_LEVEL_GEMS:
10058     {
10059       game.gems_still_needed = action_arg_number_new;
10060
10061       game.snapshot.collected_item = TRUE;
10062
10063       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10064
10065       DisplayGameControlValues();
10066
10067       break;
10068     }
10069
10070     case CA_SET_LEVEL_WIND:
10071     {
10072       game.wind_direction = action_arg_direction;
10073
10074       break;
10075     }
10076
10077     case CA_SET_LEVEL_RANDOM_SEED:
10078     {
10079       // ensure that setting a new random seed while playing is predictable
10080       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10081
10082       break;
10083     }
10084
10085     // ---------- player actions  ---------------------------------------------
10086
10087     case CA_MOVE_PLAYER:
10088     case CA_MOVE_PLAYER_NEW:
10089     {
10090       // automatically move to the next field in specified direction
10091       for (i = 0; i < MAX_PLAYERS; i++)
10092         if (trigger_player_bits & (1 << i))
10093           if (action_type == CA_MOVE_PLAYER ||
10094               stored_player[i].MovPos == 0)
10095             stored_player[i].programmed_action = action_arg_direction;
10096
10097       break;
10098     }
10099
10100     case CA_EXIT_PLAYER:
10101     {
10102       for (i = 0; i < MAX_PLAYERS; i++)
10103         if (action_arg_player_bits & (1 << i))
10104           ExitPlayer(&stored_player[i]);
10105
10106       if (game.players_still_needed == 0)
10107         LevelSolved();
10108
10109       break;
10110     }
10111
10112     case CA_KILL_PLAYER:
10113     {
10114       for (i = 0; i < MAX_PLAYERS; i++)
10115         if (action_arg_player_bits & (1 << i))
10116           KillPlayer(&stored_player[i]);
10117
10118       break;
10119     }
10120
10121     case CA_SET_PLAYER_KEYS:
10122     {
10123       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10124       int element = getSpecialActionElement(action_arg_element,
10125                                             action_arg_number, EL_KEY_1);
10126
10127       if (IS_KEY(element))
10128       {
10129         for (i = 0; i < MAX_PLAYERS; i++)
10130         {
10131           if (trigger_player_bits & (1 << i))
10132           {
10133             stored_player[i].key[KEY_NR(element)] = key_state;
10134
10135             DrawGameDoorValues();
10136           }
10137         }
10138       }
10139
10140       break;
10141     }
10142
10143     case CA_SET_PLAYER_SPEED:
10144     {
10145       for (i = 0; i < MAX_PLAYERS; i++)
10146       {
10147         if (trigger_player_bits & (1 << i))
10148         {
10149           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10150
10151           if (action_arg == CA_ARG_SPEED_FASTER &&
10152               stored_player[i].cannot_move)
10153           {
10154             action_arg_number = STEPSIZE_VERY_SLOW;
10155           }
10156           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10157                    action_arg == CA_ARG_SPEED_FASTER)
10158           {
10159             action_arg_number = 2;
10160             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10161                            CA_MODE_MULTIPLY);
10162           }
10163           else if (action_arg == CA_ARG_NUMBER_RESET)
10164           {
10165             action_arg_number = level.initial_player_stepsize[i];
10166           }
10167
10168           move_stepsize =
10169             getModifiedActionNumber(move_stepsize,
10170                                     action_mode,
10171                                     action_arg_number,
10172                                     action_arg_number_min,
10173                                     action_arg_number_max);
10174
10175           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10176         }
10177       }
10178
10179       break;
10180     }
10181
10182     case CA_SET_PLAYER_SHIELD:
10183     {
10184       for (i = 0; i < MAX_PLAYERS; i++)
10185       {
10186         if (trigger_player_bits & (1 << i))
10187         {
10188           if (action_arg == CA_ARG_SHIELD_OFF)
10189           {
10190             stored_player[i].shield_normal_time_left = 0;
10191             stored_player[i].shield_deadly_time_left = 0;
10192           }
10193           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10194           {
10195             stored_player[i].shield_normal_time_left = 999999;
10196           }
10197           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10198           {
10199             stored_player[i].shield_normal_time_left = 999999;
10200             stored_player[i].shield_deadly_time_left = 999999;
10201           }
10202         }
10203       }
10204
10205       break;
10206     }
10207
10208     case CA_SET_PLAYER_GRAVITY:
10209     {
10210       for (i = 0; i < MAX_PLAYERS; i++)
10211       {
10212         if (trigger_player_bits & (1 << i))
10213         {
10214           stored_player[i].gravity =
10215             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10216              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10217              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10218              stored_player[i].gravity);
10219         }
10220       }
10221
10222       break;
10223     }
10224
10225     case CA_SET_PLAYER_ARTWORK:
10226     {
10227       for (i = 0; i < MAX_PLAYERS; i++)
10228       {
10229         if (trigger_player_bits & (1 << i))
10230         {
10231           int artwork_element = action_arg_element;
10232
10233           if (action_arg == CA_ARG_ELEMENT_RESET)
10234             artwork_element =
10235               (level.use_artwork_element[i] ? level.artwork_element[i] :
10236                stored_player[i].element_nr);
10237
10238           if (stored_player[i].artwork_element != artwork_element)
10239             stored_player[i].Frame = 0;
10240
10241           stored_player[i].artwork_element = artwork_element;
10242
10243           SetPlayerWaiting(&stored_player[i], FALSE);
10244
10245           // set number of special actions for bored and sleeping animation
10246           stored_player[i].num_special_action_bored =
10247             get_num_special_action(artwork_element,
10248                                    ACTION_BORING_1, ACTION_BORING_LAST);
10249           stored_player[i].num_special_action_sleeping =
10250             get_num_special_action(artwork_element,
10251                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10252         }
10253       }
10254
10255       break;
10256     }
10257
10258     case CA_SET_PLAYER_INVENTORY:
10259     {
10260       for (i = 0; i < MAX_PLAYERS; i++)
10261       {
10262         struct PlayerInfo *player = &stored_player[i];
10263         int j, k;
10264
10265         if (trigger_player_bits & (1 << i))
10266         {
10267           int inventory_element = action_arg_element;
10268
10269           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10270               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10271               action_arg == CA_ARG_ELEMENT_ACTION)
10272           {
10273             int element = inventory_element;
10274             int collect_count = element_info[element].collect_count_initial;
10275
10276             if (!IS_CUSTOM_ELEMENT(element))
10277               collect_count = 1;
10278
10279             if (collect_count == 0)
10280               player->inventory_infinite_element = element;
10281             else
10282               for (k = 0; k < collect_count; k++)
10283                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10284                   player->inventory_element[player->inventory_size++] =
10285                     element;
10286           }
10287           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10288                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10289                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10290           {
10291             if (player->inventory_infinite_element != EL_UNDEFINED &&
10292                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10293                                      action_arg_element_raw))
10294               player->inventory_infinite_element = EL_UNDEFINED;
10295
10296             for (k = 0, j = 0; j < player->inventory_size; j++)
10297             {
10298               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10299                                         action_arg_element_raw))
10300                 player->inventory_element[k++] = player->inventory_element[j];
10301             }
10302
10303             player->inventory_size = k;
10304           }
10305           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10306           {
10307             if (player->inventory_size > 0)
10308             {
10309               for (j = 0; j < player->inventory_size - 1; j++)
10310                 player->inventory_element[j] = player->inventory_element[j + 1];
10311
10312               player->inventory_size--;
10313             }
10314           }
10315           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10316           {
10317             if (player->inventory_size > 0)
10318               player->inventory_size--;
10319           }
10320           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10321           {
10322             player->inventory_infinite_element = EL_UNDEFINED;
10323             player->inventory_size = 0;
10324           }
10325           else if (action_arg == CA_ARG_INVENTORY_RESET)
10326           {
10327             player->inventory_infinite_element = EL_UNDEFINED;
10328             player->inventory_size = 0;
10329
10330             if (level.use_initial_inventory[i])
10331             {
10332               for (j = 0; j < level.initial_inventory_size[i]; j++)
10333               {
10334                 int element = level.initial_inventory_content[i][j];
10335                 int collect_count = element_info[element].collect_count_initial;
10336
10337                 if (!IS_CUSTOM_ELEMENT(element))
10338                   collect_count = 1;
10339
10340                 if (collect_count == 0)
10341                   player->inventory_infinite_element = element;
10342                 else
10343                   for (k = 0; k < collect_count; k++)
10344                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10345                       player->inventory_element[player->inventory_size++] =
10346                         element;
10347               }
10348             }
10349           }
10350         }
10351       }
10352
10353       break;
10354     }
10355
10356     // ---------- CE actions  -------------------------------------------------
10357
10358     case CA_SET_CE_VALUE:
10359     {
10360       int last_ce_value = CustomValue[x][y];
10361
10362       CustomValue[x][y] = action_arg_number_new;
10363
10364       if (CustomValue[x][y] != last_ce_value)
10365       {
10366         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10367         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10368
10369         if (CustomValue[x][y] == 0)
10370         {
10371           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10372           ChangeCount[x][y] = 0;        // allow at least one more change
10373
10374           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10375           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10376         }
10377       }
10378
10379       break;
10380     }
10381
10382     case CA_SET_CE_SCORE:
10383     {
10384       int last_ce_score = ei->collect_score;
10385
10386       ei->collect_score = action_arg_number_new;
10387
10388       if (ei->collect_score != last_ce_score)
10389       {
10390         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10391         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10392
10393         if (ei->collect_score == 0)
10394         {
10395           int xx, yy;
10396
10397           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10398           ChangeCount[x][y] = 0;        // allow at least one more change
10399
10400           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10401           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10402
10403           /*
10404             This is a very special case that seems to be a mixture between
10405             CheckElementChange() and CheckTriggeredElementChange(): while
10406             the first one only affects single elements that are triggered
10407             directly, the second one affects multiple elements in the playfield
10408             that are triggered indirectly by another element. This is a third
10409             case: Changing the CE score always affects multiple identical CEs,
10410             so every affected CE must be checked, not only the single CE for
10411             which the CE score was changed in the first place (as every instance
10412             of that CE shares the same CE score, and therefore also can change)!
10413           */
10414           SCAN_PLAYFIELD(xx, yy)
10415           {
10416             if (Tile[xx][yy] == element)
10417               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10418                                  CE_SCORE_GETS_ZERO);
10419           }
10420         }
10421       }
10422
10423       break;
10424     }
10425
10426     case CA_SET_CE_ARTWORK:
10427     {
10428       int artwork_element = action_arg_element;
10429       boolean reset_frame = FALSE;
10430       int xx, yy;
10431
10432       if (action_arg == CA_ARG_ELEMENT_RESET)
10433         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10434                            element);
10435
10436       if (ei->gfx_element != artwork_element)
10437         reset_frame = TRUE;
10438
10439       ei->gfx_element = artwork_element;
10440
10441       SCAN_PLAYFIELD(xx, yy)
10442       {
10443         if (Tile[xx][yy] == element)
10444         {
10445           if (reset_frame)
10446           {
10447             ResetGfxAnimation(xx, yy);
10448             ResetRandomAnimationValue(xx, yy);
10449           }
10450
10451           TEST_DrawLevelField(xx, yy);
10452         }
10453       }
10454
10455       break;
10456     }
10457
10458     // ---------- engine actions  ---------------------------------------------
10459
10460     case CA_SET_ENGINE_SCAN_MODE:
10461     {
10462       InitPlayfieldScanMode(action_arg);
10463
10464       break;
10465     }
10466
10467     default:
10468       break;
10469   }
10470 }
10471
10472 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10473 {
10474   int old_element = Tile[x][y];
10475   int new_element = GetElementFromGroupElement(element);
10476   int previous_move_direction = MovDir[x][y];
10477   int last_ce_value = CustomValue[x][y];
10478   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10479   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10480   boolean add_player_onto_element = (new_element_is_player &&
10481                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10482                                      IS_WALKABLE(old_element));
10483
10484   if (!add_player_onto_element)
10485   {
10486     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10487       RemoveMovingField(x, y);
10488     else
10489       RemoveField(x, y);
10490
10491     Tile[x][y] = new_element;
10492
10493     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10494       MovDir[x][y] = previous_move_direction;
10495
10496     if (element_info[new_element].use_last_ce_value)
10497       CustomValue[x][y] = last_ce_value;
10498
10499     InitField_WithBug1(x, y, FALSE);
10500
10501     new_element = Tile[x][y];   // element may have changed
10502
10503     ResetGfxAnimation(x, y);
10504     ResetRandomAnimationValue(x, y);
10505
10506     TEST_DrawLevelField(x, y);
10507
10508     if (GFX_CRUMBLED(new_element))
10509       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10510   }
10511
10512   // check if element under the player changes from accessible to unaccessible
10513   // (needed for special case of dropping element which then changes)
10514   // (must be checked after creating new element for walkable group elements)
10515   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10516       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10517   {
10518     Bang(x, y);
10519
10520     return;
10521   }
10522
10523   // "ChangeCount" not set yet to allow "entered by player" change one time
10524   if (new_element_is_player)
10525     RelocatePlayer(x, y, new_element);
10526
10527   if (is_change)
10528     ChangeCount[x][y]++;        // count number of changes in the same frame
10529
10530   TestIfBadThingTouchesPlayer(x, y);
10531   TestIfPlayerTouchesCustomElement(x, y);
10532   TestIfElementTouchesCustomElement(x, y);
10533 }
10534
10535 static void CreateField(int x, int y, int element)
10536 {
10537   CreateFieldExt(x, y, element, FALSE);
10538 }
10539
10540 static void CreateElementFromChange(int x, int y, int element)
10541 {
10542   element = GET_VALID_RUNTIME_ELEMENT(element);
10543
10544   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10545   {
10546     int old_element = Tile[x][y];
10547
10548     // prevent changed element from moving in same engine frame
10549     // unless both old and new element can either fall or move
10550     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10551         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10552       Stop[x][y] = TRUE;
10553   }
10554
10555   CreateFieldExt(x, y, element, TRUE);
10556 }
10557
10558 static boolean ChangeElement(int x, int y, int element, int page)
10559 {
10560   struct ElementInfo *ei = &element_info[element];
10561   struct ElementChangeInfo *change = &ei->change_page[page];
10562   int ce_value = CustomValue[x][y];
10563   int ce_score = ei->collect_score;
10564   int target_element;
10565   int old_element = Tile[x][y];
10566
10567   // always use default change event to prevent running into a loop
10568   if (ChangeEvent[x][y] == -1)
10569     ChangeEvent[x][y] = CE_DELAY;
10570
10571   if (ChangeEvent[x][y] == CE_DELAY)
10572   {
10573     // reset actual trigger element, trigger player and action element
10574     change->actual_trigger_element = EL_EMPTY;
10575     change->actual_trigger_player = EL_EMPTY;
10576     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10577     change->actual_trigger_side = CH_SIDE_NONE;
10578     change->actual_trigger_ce_value = 0;
10579     change->actual_trigger_ce_score = 0;
10580   }
10581
10582   // do not change elements more than a specified maximum number of changes
10583   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10584     return FALSE;
10585
10586   ChangeCount[x][y]++;          // count number of changes in the same frame
10587
10588   if (change->explode)
10589   {
10590     Bang(x, y);
10591
10592     return TRUE;
10593   }
10594
10595   if (change->use_target_content)
10596   {
10597     boolean complete_replace = TRUE;
10598     boolean can_replace[3][3];
10599     int xx, yy;
10600
10601     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10602     {
10603       boolean is_empty;
10604       boolean is_walkable;
10605       boolean is_diggable;
10606       boolean is_collectible;
10607       boolean is_removable;
10608       boolean is_destructible;
10609       int ex = x + xx - 1;
10610       int ey = y + yy - 1;
10611       int content_element = change->target_content.e[xx][yy];
10612       int e;
10613
10614       can_replace[xx][yy] = TRUE;
10615
10616       if (ex == x && ey == y)   // do not check changing element itself
10617         continue;
10618
10619       if (content_element == EL_EMPTY_SPACE)
10620       {
10621         can_replace[xx][yy] = FALSE;    // do not replace border with space
10622
10623         continue;
10624       }
10625
10626       if (!IN_LEV_FIELD(ex, ey))
10627       {
10628         can_replace[xx][yy] = FALSE;
10629         complete_replace = FALSE;
10630
10631         continue;
10632       }
10633
10634       e = Tile[ex][ey];
10635
10636       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10637         e = MovingOrBlocked2Element(ex, ey);
10638
10639       is_empty = (IS_FREE(ex, ey) ||
10640                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10641
10642       is_walkable     = (is_empty || IS_WALKABLE(e));
10643       is_diggable     = (is_empty || IS_DIGGABLE(e));
10644       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10645       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10646       is_removable    = (is_diggable || is_collectible);
10647
10648       can_replace[xx][yy] =
10649         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10650           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10651           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10652           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10653           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10654           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10655          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10656
10657       if (!can_replace[xx][yy])
10658         complete_replace = FALSE;
10659     }
10660
10661     if (!change->only_if_complete || complete_replace)
10662     {
10663       boolean something_has_changed = FALSE;
10664
10665       if (change->only_if_complete && change->use_random_replace &&
10666           RND(100) < change->random_percentage)
10667         return FALSE;
10668
10669       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10670       {
10671         int ex = x + xx - 1;
10672         int ey = y + yy - 1;
10673         int content_element;
10674
10675         if (can_replace[xx][yy] && (!change->use_random_replace ||
10676                                     RND(100) < change->random_percentage))
10677         {
10678           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10679             RemoveMovingField(ex, ey);
10680
10681           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10682
10683           content_element = change->target_content.e[xx][yy];
10684           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10685                                               ce_value, ce_score);
10686
10687           CreateElementFromChange(ex, ey, target_element);
10688
10689           something_has_changed = TRUE;
10690
10691           // for symmetry reasons, freeze newly created border elements
10692           if (ex != x || ey != y)
10693             Stop[ex][ey] = TRUE;        // no more moving in this frame
10694         }
10695       }
10696
10697       if (something_has_changed)
10698       {
10699         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10700         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10701       }
10702     }
10703   }
10704   else
10705   {
10706     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10707                                         ce_value, ce_score);
10708
10709     if (element == EL_DIAGONAL_GROWING ||
10710         element == EL_DIAGONAL_SHRINKING)
10711     {
10712       target_element = Store[x][y];
10713
10714       Store[x][y] = EL_EMPTY;
10715     }
10716
10717     CreateElementFromChange(x, y, target_element);
10718
10719     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10720     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10721   }
10722
10723   // this uses direct change before indirect change
10724   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10725
10726   return TRUE;
10727 }
10728
10729 static void HandleElementChange(int x, int y, int page)
10730 {
10731   int element = MovingOrBlocked2Element(x, y);
10732   struct ElementInfo *ei = &element_info[element];
10733   struct ElementChangeInfo *change = &ei->change_page[page];
10734   boolean handle_action_before_change = FALSE;
10735
10736 #ifdef DEBUG
10737   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10738       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10739   {
10740     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10741           x, y, element, element_info[element].token_name);
10742     Debug("game:playing:HandleElementChange", "This should never happen!");
10743   }
10744 #endif
10745
10746   // this can happen with classic bombs on walkable, changing elements
10747   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10748   {
10749     return;
10750   }
10751
10752   if (ChangeDelay[x][y] == 0)           // initialize element change
10753   {
10754     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10755
10756     if (change->can_change)
10757     {
10758       // !!! not clear why graphic animation should be reset at all here !!!
10759       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10760       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10761
10762       /*
10763         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10764
10765         When using an animation frame delay of 1 (this only happens with
10766         "sp_zonk.moving.left/right" in the classic graphics), the default
10767         (non-moving) animation shows wrong animation frames (while the
10768         moving animation, like "sp_zonk.moving.left/right", is correct,
10769         so this graphical bug never shows up with the classic graphics).
10770         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10771         be drawn instead of the correct frames 0,1,2,3. This is caused by
10772         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10773         an element change: First when the change delay ("ChangeDelay[][]")
10774         counter has reached zero after decrementing, then a second time in
10775         the next frame (after "GfxFrame[][]" was already incremented) when
10776         "ChangeDelay[][]" is reset to the initial delay value again.
10777
10778         This causes frame 0 to be drawn twice, while the last frame won't
10779         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10780
10781         As some animations may already be cleverly designed around this bug
10782         (at least the "Snake Bite" snake tail animation does this), it cannot
10783         simply be fixed here without breaking such existing animations.
10784         Unfortunately, it cannot easily be detected if a graphics set was
10785         designed "before" or "after" the bug was fixed. As a workaround,
10786         a new graphics set option "game.graphics_engine_version" was added
10787         to be able to specify the game's major release version for which the
10788         graphics set was designed, which can then be used to decide if the
10789         bugfix should be used (version 4 and above) or not (version 3 or
10790         below, or if no version was specified at all, as with old sets).
10791
10792         (The wrong/fixed animation frames can be tested with the test level set
10793         "test_gfxframe" and level "000", which contains a specially prepared
10794         custom element at level position (x/y) == (11/9) which uses the zonk
10795         animation mentioned above. Using "game.graphics_engine_version: 4"
10796         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10797         This can also be seen from the debug output for this test element.)
10798       */
10799
10800       // when a custom element is about to change (for example by change delay),
10801       // do not reset graphic animation when the custom element is moving
10802       if (game.graphics_engine_version < 4 &&
10803           !IS_MOVING(x, y))
10804       {
10805         ResetGfxAnimation(x, y);
10806         ResetRandomAnimationValue(x, y);
10807       }
10808
10809       if (change->pre_change_function)
10810         change->pre_change_function(x, y);
10811     }
10812   }
10813
10814   ChangeDelay[x][y]--;
10815
10816   if (ChangeDelay[x][y] != 0)           // continue element change
10817   {
10818     if (change->can_change)
10819     {
10820       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10821
10822       if (IS_ANIMATED(graphic))
10823         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10824
10825       if (change->change_function)
10826         change->change_function(x, y);
10827     }
10828   }
10829   else                                  // finish element change
10830   {
10831     if (ChangePage[x][y] != -1)         // remember page from delayed change
10832     {
10833       page = ChangePage[x][y];
10834       ChangePage[x][y] = -1;
10835
10836       change = &ei->change_page[page];
10837     }
10838
10839     if (IS_MOVING(x, y))                // never change a running system ;-)
10840     {
10841       ChangeDelay[x][y] = 1;            // try change after next move step
10842       ChangePage[x][y] = page;          // remember page to use for change
10843
10844       return;
10845     }
10846
10847     // special case: set new level random seed before changing element
10848     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10849       handle_action_before_change = TRUE;
10850
10851     if (change->has_action && handle_action_before_change)
10852       ExecuteCustomElementAction(x, y, element, page);
10853
10854     if (change->can_change)
10855     {
10856       if (ChangeElement(x, y, element, page))
10857       {
10858         if (change->post_change_function)
10859           change->post_change_function(x, y);
10860       }
10861     }
10862
10863     if (change->has_action && !handle_action_before_change)
10864       ExecuteCustomElementAction(x, y, element, page);
10865   }
10866 }
10867
10868 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10869                                               int trigger_element,
10870                                               int trigger_event,
10871                                               int trigger_player,
10872                                               int trigger_side,
10873                                               int trigger_page)
10874 {
10875   boolean change_done_any = FALSE;
10876   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10877   int i;
10878
10879   if (!(trigger_events[trigger_element][trigger_event]))
10880     return FALSE;
10881
10882   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10883
10884   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10885   {
10886     int element = EL_CUSTOM_START + i;
10887     boolean change_done = FALSE;
10888     int p;
10889
10890     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10891         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10892       continue;
10893
10894     for (p = 0; p < element_info[element].num_change_pages; p++)
10895     {
10896       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10897
10898       if (change->can_change_or_has_action &&
10899           change->has_event[trigger_event] &&
10900           change->trigger_side & trigger_side &&
10901           change->trigger_player & trigger_player &&
10902           change->trigger_page & trigger_page_bits &&
10903           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10904       {
10905         change->actual_trigger_element = trigger_element;
10906         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10907         change->actual_trigger_player_bits = trigger_player;
10908         change->actual_trigger_side = trigger_side;
10909         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10910         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10911
10912         if ((change->can_change && !change_done) || change->has_action)
10913         {
10914           int x, y;
10915
10916           SCAN_PLAYFIELD(x, y)
10917           {
10918             if (Tile[x][y] == element)
10919             {
10920               if (change->can_change && !change_done)
10921               {
10922                 // if element already changed in this frame, not only prevent
10923                 // another element change (checked in ChangeElement()), but
10924                 // also prevent additional element actions for this element
10925
10926                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10927                     !level.use_action_after_change_bug)
10928                   continue;
10929
10930                 ChangeDelay[x][y] = 1;
10931                 ChangeEvent[x][y] = trigger_event;
10932
10933                 HandleElementChange(x, y, p);
10934               }
10935               else if (change->has_action)
10936               {
10937                 // if element already changed in this frame, not only prevent
10938                 // another element change (checked in ChangeElement()), but
10939                 // also prevent additional element actions for this element
10940
10941                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10942                     !level.use_action_after_change_bug)
10943                   continue;
10944
10945                 ExecuteCustomElementAction(x, y, element, p);
10946                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10947               }
10948             }
10949           }
10950
10951           if (change->can_change)
10952           {
10953             change_done = TRUE;
10954             change_done_any = TRUE;
10955           }
10956         }
10957       }
10958     }
10959   }
10960
10961   RECURSION_LOOP_DETECTION_END();
10962
10963   return change_done_any;
10964 }
10965
10966 static boolean CheckElementChangeExt(int x, int y,
10967                                      int element,
10968                                      int trigger_element,
10969                                      int trigger_event,
10970                                      int trigger_player,
10971                                      int trigger_side)
10972 {
10973   boolean change_done = FALSE;
10974   int p;
10975
10976   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10977       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10978     return FALSE;
10979
10980   if (Tile[x][y] == EL_BLOCKED)
10981   {
10982     Blocked2Moving(x, y, &x, &y);
10983     element = Tile[x][y];
10984   }
10985
10986   // check if element has already changed or is about to change after moving
10987   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10988        Tile[x][y] != element) ||
10989
10990       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10991        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10992         ChangePage[x][y] != -1)))
10993     return FALSE;
10994
10995   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10996
10997   for (p = 0; p < element_info[element].num_change_pages; p++)
10998   {
10999     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11000
11001     /* check trigger element for all events where the element that is checked
11002        for changing interacts with a directly adjacent element -- this is
11003        different to element changes that affect other elements to change on the
11004        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11005     boolean check_trigger_element =
11006       (trigger_event == CE_TOUCHING_X ||
11007        trigger_event == CE_HITTING_X ||
11008        trigger_event == CE_HIT_BY_X ||
11009        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11010
11011     if (change->can_change_or_has_action &&
11012         change->has_event[trigger_event] &&
11013         change->trigger_side & trigger_side &&
11014         change->trigger_player & trigger_player &&
11015         (!check_trigger_element ||
11016          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11017     {
11018       change->actual_trigger_element = trigger_element;
11019       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11020       change->actual_trigger_player_bits = trigger_player;
11021       change->actual_trigger_side = trigger_side;
11022       change->actual_trigger_ce_value = CustomValue[x][y];
11023       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11024
11025       // special case: trigger element not at (x,y) position for some events
11026       if (check_trigger_element)
11027       {
11028         static struct
11029         {
11030           int dx, dy;
11031         } move_xy[] =
11032           {
11033             {  0,  0 },
11034             { -1,  0 },
11035             { +1,  0 },
11036             {  0,  0 },
11037             {  0, -1 },
11038             {  0,  0 }, { 0, 0 }, { 0, 0 },
11039             {  0, +1 }
11040           };
11041
11042         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11043         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11044
11045         change->actual_trigger_ce_value = CustomValue[xx][yy];
11046         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11047       }
11048
11049       if (change->can_change && !change_done)
11050       {
11051         ChangeDelay[x][y] = 1;
11052         ChangeEvent[x][y] = trigger_event;
11053
11054         HandleElementChange(x, y, p);
11055
11056         change_done = TRUE;
11057       }
11058       else if (change->has_action)
11059       {
11060         ExecuteCustomElementAction(x, y, element, p);
11061         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11062       }
11063     }
11064   }
11065
11066   RECURSION_LOOP_DETECTION_END();
11067
11068   return change_done;
11069 }
11070
11071 static void PlayPlayerSound(struct PlayerInfo *player)
11072 {
11073   int jx = player->jx, jy = player->jy;
11074   int sound_element = player->artwork_element;
11075   int last_action = player->last_action_waiting;
11076   int action = player->action_waiting;
11077
11078   if (player->is_waiting)
11079   {
11080     if (action != last_action)
11081       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11082     else
11083       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11084   }
11085   else
11086   {
11087     if (action != last_action)
11088       StopSound(element_info[sound_element].sound[last_action]);
11089
11090     if (last_action == ACTION_SLEEPING)
11091       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11092   }
11093 }
11094
11095 static void PlayAllPlayersSound(void)
11096 {
11097   int i;
11098
11099   for (i = 0; i < MAX_PLAYERS; i++)
11100     if (stored_player[i].active)
11101       PlayPlayerSound(&stored_player[i]);
11102 }
11103
11104 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11105 {
11106   boolean last_waiting = player->is_waiting;
11107   int move_dir = player->MovDir;
11108
11109   player->dir_waiting = move_dir;
11110   player->last_action_waiting = player->action_waiting;
11111
11112   if (is_waiting)
11113   {
11114     if (!last_waiting)          // not waiting -> waiting
11115     {
11116       player->is_waiting = TRUE;
11117
11118       player->frame_counter_bored =
11119         FrameCounter +
11120         game.player_boring_delay_fixed +
11121         GetSimpleRandom(game.player_boring_delay_random);
11122       player->frame_counter_sleeping =
11123         FrameCounter +
11124         game.player_sleeping_delay_fixed +
11125         GetSimpleRandom(game.player_sleeping_delay_random);
11126
11127       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11128     }
11129
11130     if (game.player_sleeping_delay_fixed +
11131         game.player_sleeping_delay_random > 0 &&
11132         player->anim_delay_counter == 0 &&
11133         player->post_delay_counter == 0 &&
11134         FrameCounter >= player->frame_counter_sleeping)
11135       player->is_sleeping = TRUE;
11136     else if (game.player_boring_delay_fixed +
11137              game.player_boring_delay_random > 0 &&
11138              FrameCounter >= player->frame_counter_bored)
11139       player->is_bored = TRUE;
11140
11141     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11142                               player->is_bored ? ACTION_BORING :
11143                               ACTION_WAITING);
11144
11145     if (player->is_sleeping && player->use_murphy)
11146     {
11147       // special case for sleeping Murphy when leaning against non-free tile
11148
11149       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11150           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11151            !IS_MOVING(player->jx - 1, player->jy)))
11152         move_dir = MV_LEFT;
11153       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11154                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11155                 !IS_MOVING(player->jx + 1, player->jy)))
11156         move_dir = MV_RIGHT;
11157       else
11158         player->is_sleeping = FALSE;
11159
11160       player->dir_waiting = move_dir;
11161     }
11162
11163     if (player->is_sleeping)
11164     {
11165       if (player->num_special_action_sleeping > 0)
11166       {
11167         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11168         {
11169           int last_special_action = player->special_action_sleeping;
11170           int num_special_action = player->num_special_action_sleeping;
11171           int special_action =
11172             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11173              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11174              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11175              last_special_action + 1 : ACTION_SLEEPING);
11176           int special_graphic =
11177             el_act_dir2img(player->artwork_element, special_action, move_dir);
11178
11179           player->anim_delay_counter =
11180             graphic_info[special_graphic].anim_delay_fixed +
11181             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11182           player->post_delay_counter =
11183             graphic_info[special_graphic].post_delay_fixed +
11184             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11185
11186           player->special_action_sleeping = special_action;
11187         }
11188
11189         if (player->anim_delay_counter > 0)
11190         {
11191           player->action_waiting = player->special_action_sleeping;
11192           player->anim_delay_counter--;
11193         }
11194         else if (player->post_delay_counter > 0)
11195         {
11196           player->post_delay_counter--;
11197         }
11198       }
11199     }
11200     else if (player->is_bored)
11201     {
11202       if (player->num_special_action_bored > 0)
11203       {
11204         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11205         {
11206           int special_action =
11207             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11208           int special_graphic =
11209             el_act_dir2img(player->artwork_element, special_action, move_dir);
11210
11211           player->anim_delay_counter =
11212             graphic_info[special_graphic].anim_delay_fixed +
11213             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11214           player->post_delay_counter =
11215             graphic_info[special_graphic].post_delay_fixed +
11216             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11217
11218           player->special_action_bored = special_action;
11219         }
11220
11221         if (player->anim_delay_counter > 0)
11222         {
11223           player->action_waiting = player->special_action_bored;
11224           player->anim_delay_counter--;
11225         }
11226         else if (player->post_delay_counter > 0)
11227         {
11228           player->post_delay_counter--;
11229         }
11230       }
11231     }
11232   }
11233   else if (last_waiting)        // waiting -> not waiting
11234   {
11235     player->is_waiting = FALSE;
11236     player->is_bored = FALSE;
11237     player->is_sleeping = FALSE;
11238
11239     player->frame_counter_bored = -1;
11240     player->frame_counter_sleeping = -1;
11241
11242     player->anim_delay_counter = 0;
11243     player->post_delay_counter = 0;
11244
11245     player->dir_waiting = player->MovDir;
11246     player->action_waiting = ACTION_DEFAULT;
11247
11248     player->special_action_bored = ACTION_DEFAULT;
11249     player->special_action_sleeping = ACTION_DEFAULT;
11250   }
11251 }
11252
11253 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11254 {
11255   if ((!player->is_moving  && player->was_moving) ||
11256       (player->MovPos == 0 && player->was_moving) ||
11257       (player->is_snapping && !player->was_snapping) ||
11258       (player->is_dropping && !player->was_dropping))
11259   {
11260     if (!CheckSaveEngineSnapshotToList())
11261       return;
11262
11263     player->was_moving = FALSE;
11264     player->was_snapping = TRUE;
11265     player->was_dropping = TRUE;
11266   }
11267   else
11268   {
11269     if (player->is_moving)
11270       player->was_moving = TRUE;
11271
11272     if (!player->is_snapping)
11273       player->was_snapping = FALSE;
11274
11275     if (!player->is_dropping)
11276       player->was_dropping = FALSE;
11277   }
11278
11279   static struct MouseActionInfo mouse_action_last = { 0 };
11280   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11281   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11282
11283   if (new_released)
11284     CheckSaveEngineSnapshotToList();
11285
11286   mouse_action_last = mouse_action;
11287 }
11288
11289 static void CheckSingleStepMode(struct PlayerInfo *player)
11290 {
11291   if (tape.single_step && tape.recording && !tape.pausing)
11292   {
11293     // as it is called "single step mode", just return to pause mode when the
11294     // player stopped moving after one tile (or never starts moving at all)
11295     // (reverse logic needed here in case single step mode used in team mode)
11296     if (player->is_moving ||
11297         player->is_pushing ||
11298         player->is_dropping_pressed ||
11299         player->effective_mouse_action.button)
11300       game.enter_single_step_mode = FALSE;
11301   }
11302
11303   CheckSaveEngineSnapshot(player);
11304 }
11305
11306 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11307 {
11308   int left      = player_action & JOY_LEFT;
11309   int right     = player_action & JOY_RIGHT;
11310   int up        = player_action & JOY_UP;
11311   int down      = player_action & JOY_DOWN;
11312   int button1   = player_action & JOY_BUTTON_1;
11313   int button2   = player_action & JOY_BUTTON_2;
11314   int dx        = (left ? -1 : right ? 1 : 0);
11315   int dy        = (up   ? -1 : down  ? 1 : 0);
11316
11317   if (!player->active || tape.pausing)
11318     return 0;
11319
11320   if (player_action)
11321   {
11322     if (button1)
11323       SnapField(player, dx, dy);
11324     else
11325     {
11326       if (button2)
11327         DropElement(player);
11328
11329       MovePlayer(player, dx, dy);
11330     }
11331
11332     CheckSingleStepMode(player);
11333
11334     SetPlayerWaiting(player, FALSE);
11335
11336     return player_action;
11337   }
11338   else
11339   {
11340     // no actions for this player (no input at player's configured device)
11341
11342     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11343     SnapField(player, 0, 0);
11344     CheckGravityMovementWhenNotMoving(player);
11345
11346     if (player->MovPos == 0)
11347       SetPlayerWaiting(player, TRUE);
11348
11349     if (player->MovPos == 0)    // needed for tape.playing
11350       player->is_moving = FALSE;
11351
11352     player->is_dropping = FALSE;
11353     player->is_dropping_pressed = FALSE;
11354     player->drop_pressed_delay = 0;
11355
11356     CheckSingleStepMode(player);
11357
11358     return 0;
11359   }
11360 }
11361
11362 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11363                                          byte *tape_action)
11364 {
11365   if (!tape.use_mouse_actions)
11366     return;
11367
11368   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11369   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11370   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11371 }
11372
11373 static void SetTapeActionFromMouseAction(byte *tape_action,
11374                                          struct MouseActionInfo *mouse_action)
11375 {
11376   if (!tape.use_mouse_actions)
11377     return;
11378
11379   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11380   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11381   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11382 }
11383
11384 static void CheckLevelSolved(void)
11385 {
11386   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11387   {
11388     if (game_em.level_solved &&
11389         !game_em.game_over)                             // game won
11390     {
11391       LevelSolved();
11392
11393       game_em.game_over = TRUE;
11394
11395       game.all_players_gone = TRUE;
11396     }
11397
11398     if (game_em.game_over)                              // game lost
11399       game.all_players_gone = TRUE;
11400   }
11401   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11402   {
11403     if (game_sp.level_solved &&
11404         !game_sp.game_over)                             // game won
11405     {
11406       LevelSolved();
11407
11408       game_sp.game_over = TRUE;
11409
11410       game.all_players_gone = TRUE;
11411     }
11412
11413     if (game_sp.game_over)                              // game lost
11414       game.all_players_gone = TRUE;
11415   }
11416   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11417   {
11418     if (game_mm.level_solved &&
11419         !game_mm.game_over)                             // game won
11420     {
11421       LevelSolved();
11422
11423       game_mm.game_over = TRUE;
11424
11425       game.all_players_gone = TRUE;
11426     }
11427
11428     if (game_mm.game_over)                              // game lost
11429       game.all_players_gone = TRUE;
11430   }
11431 }
11432
11433 static void CheckLevelTime(void)
11434 {
11435   int i;
11436
11437   if (TimeFrames >= FRAMES_PER_SECOND)
11438   {
11439     TimeFrames = 0;
11440     TapeTime++;
11441
11442     for (i = 0; i < MAX_PLAYERS; i++)
11443     {
11444       struct PlayerInfo *player = &stored_player[i];
11445
11446       if (SHIELD_ON(player))
11447       {
11448         player->shield_normal_time_left--;
11449
11450         if (player->shield_deadly_time_left > 0)
11451           player->shield_deadly_time_left--;
11452       }
11453     }
11454
11455     if (!game.LevelSolved && !level.use_step_counter)
11456     {
11457       TimePlayed++;
11458
11459       if (TimeLeft > 0)
11460       {
11461         TimeLeft--;
11462
11463         if (TimeLeft <= 10 && setup.time_limit)
11464           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11465
11466         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11467            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11468
11469         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11470
11471         if (!TimeLeft && setup.time_limit)
11472         {
11473           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11474             game_em.lev->killed_out_of_time = TRUE;
11475           else
11476             for (i = 0; i < MAX_PLAYERS; i++)
11477               KillPlayer(&stored_player[i]);
11478         }
11479       }
11480       else if (game.no_time_limit && !game.all_players_gone)
11481       {
11482         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11483       }
11484
11485       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11486     }
11487
11488     if (tape.recording || tape.playing)
11489       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11490   }
11491
11492   if (tape.recording || tape.playing)
11493     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11494
11495   UpdateAndDisplayGameControlValues();
11496 }
11497
11498 void AdvanceFrameAndPlayerCounters(int player_nr)
11499 {
11500   int i;
11501
11502   // advance frame counters (global frame counter and time frame counter)
11503   FrameCounter++;
11504   TimeFrames++;
11505
11506   // advance player counters (counters for move delay, move animation etc.)
11507   for (i = 0; i < MAX_PLAYERS; i++)
11508   {
11509     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11510     int move_delay_value = stored_player[i].move_delay_value;
11511     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11512
11513     if (!advance_player_counters)       // not all players may be affected
11514       continue;
11515
11516     if (move_frames == 0)       // less than one move per game frame
11517     {
11518       int stepsize = TILEX / move_delay_value;
11519       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11520       int count = (stored_player[i].is_moving ?
11521                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11522
11523       if (count % delay == 0)
11524         move_frames = 1;
11525     }
11526
11527     stored_player[i].Frame += move_frames;
11528
11529     if (stored_player[i].MovPos != 0)
11530       stored_player[i].StepFrame += move_frames;
11531
11532     if (stored_player[i].move_delay > 0)
11533       stored_player[i].move_delay--;
11534
11535     // due to bugs in previous versions, counter must count up, not down
11536     if (stored_player[i].push_delay != -1)
11537       stored_player[i].push_delay++;
11538
11539     if (stored_player[i].drop_delay > 0)
11540       stored_player[i].drop_delay--;
11541
11542     if (stored_player[i].is_dropping_pressed)
11543       stored_player[i].drop_pressed_delay++;
11544   }
11545 }
11546
11547 void StartGameActions(boolean init_network_game, boolean record_tape,
11548                       int random_seed)
11549 {
11550   unsigned int new_random_seed = InitRND(random_seed);
11551
11552   if (record_tape)
11553     TapeStartRecording(new_random_seed);
11554
11555   if (init_network_game)
11556   {
11557     SendToServer_LevelFile();
11558     SendToServer_StartPlaying();
11559
11560     return;
11561   }
11562
11563   InitGame();
11564 }
11565
11566 static void GameActionsExt(void)
11567 {
11568 #if 0
11569   static unsigned int game_frame_delay = 0;
11570 #endif
11571   unsigned int game_frame_delay_value;
11572   byte *recorded_player_action;
11573   byte summarized_player_action = 0;
11574   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11575   int i;
11576
11577   // detect endless loops, caused by custom element programming
11578   if (recursion_loop_detected && recursion_loop_depth == 0)
11579   {
11580     char *message = getStringCat3("Internal Error! Element ",
11581                                   EL_NAME(recursion_loop_element),
11582                                   " caused endless loop! Quit the game?");
11583
11584     Warn("element '%s' caused endless loop in game engine",
11585          EL_NAME(recursion_loop_element));
11586
11587     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11588
11589     recursion_loop_detected = FALSE;    // if game should be continued
11590
11591     free(message);
11592
11593     return;
11594   }
11595
11596   if (game.restart_level)
11597     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11598
11599   CheckLevelSolved();
11600
11601   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11602     GameWon();
11603
11604   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11605     TapeStop();
11606
11607   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11608     return;
11609
11610   game_frame_delay_value =
11611     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11612
11613   if (tape.playing && tape.warp_forward && !tape.pausing)
11614     game_frame_delay_value = 0;
11615
11616   SetVideoFrameDelay(game_frame_delay_value);
11617
11618   // (de)activate virtual buttons depending on current game status
11619   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11620   {
11621     if (game.all_players_gone)  // if no players there to be controlled anymore
11622       SetOverlayActive(FALSE);
11623     else if (!tape.playing)     // if game continues after tape stopped playing
11624       SetOverlayActive(TRUE);
11625   }
11626
11627 #if 0
11628 #if 0
11629   // ---------- main game synchronization point ----------
11630
11631   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11632
11633   Debug("game:playing:skip", "skip == %d", skip);
11634
11635 #else
11636   // ---------- main game synchronization point ----------
11637
11638   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11639 #endif
11640 #endif
11641
11642   if (network_playing && !network_player_action_received)
11643   {
11644     // try to get network player actions in time
11645
11646     // last chance to get network player actions without main loop delay
11647     HandleNetworking();
11648
11649     // game was quit by network peer
11650     if (game_status != GAME_MODE_PLAYING)
11651       return;
11652
11653     // check if network player actions still missing and game still running
11654     if (!network_player_action_received && !checkGameEnded())
11655       return;           // failed to get network player actions in time
11656
11657     // do not yet reset "network_player_action_received" (for tape.pausing)
11658   }
11659
11660   if (tape.pausing)
11661     return;
11662
11663   // at this point we know that we really continue executing the game
11664
11665   network_player_action_received = FALSE;
11666
11667   // when playing tape, read previously recorded player input from tape data
11668   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11669
11670   local_player->effective_mouse_action = local_player->mouse_action;
11671
11672   if (recorded_player_action != NULL)
11673     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11674                                  recorded_player_action);
11675
11676   // TapePlayAction() may return NULL when toggling to "pause before death"
11677   if (tape.pausing)
11678     return;
11679
11680   if (tape.set_centered_player)
11681   {
11682     game.centered_player_nr_next = tape.centered_player_nr_next;
11683     game.set_centered_player = TRUE;
11684   }
11685
11686   for (i = 0; i < MAX_PLAYERS; i++)
11687   {
11688     summarized_player_action |= stored_player[i].action;
11689
11690     if (!network_playing && (game.team_mode || tape.playing))
11691       stored_player[i].effective_action = stored_player[i].action;
11692   }
11693
11694   if (network_playing && !checkGameEnded())
11695     SendToServer_MovePlayer(summarized_player_action);
11696
11697   // summarize all actions at local players mapped input device position
11698   // (this allows using different input devices in single player mode)
11699   if (!network.enabled && !game.team_mode)
11700     stored_player[map_player_action[local_player->index_nr]].effective_action =
11701       summarized_player_action;
11702
11703   // summarize all actions at centered player in local team mode
11704   if (tape.recording &&
11705       setup.team_mode && !network.enabled &&
11706       setup.input_on_focus &&
11707       game.centered_player_nr != -1)
11708   {
11709     for (i = 0; i < MAX_PLAYERS; i++)
11710       stored_player[map_player_action[i]].effective_action =
11711         (i == game.centered_player_nr ? summarized_player_action : 0);
11712   }
11713
11714   if (recorded_player_action != NULL)
11715     for (i = 0; i < MAX_PLAYERS; i++)
11716       stored_player[i].effective_action = recorded_player_action[i];
11717
11718   for (i = 0; i < MAX_PLAYERS; i++)
11719   {
11720     tape_action[i] = stored_player[i].effective_action;
11721
11722     /* (this may happen in the RND game engine if a player was not present on
11723        the playfield on level start, but appeared later from a custom element */
11724     if (setup.team_mode &&
11725         tape.recording &&
11726         tape_action[i] &&
11727         !tape.player_participates[i])
11728       tape.player_participates[i] = TRUE;
11729   }
11730
11731   SetTapeActionFromMouseAction(tape_action,
11732                                &local_player->effective_mouse_action);
11733
11734   // only record actions from input devices, but not programmed actions
11735   if (tape.recording)
11736     TapeRecordAction(tape_action);
11737
11738   // remember if game was played (especially after tape stopped playing)
11739   if (!tape.playing && summarized_player_action)
11740     game.GamePlayed = TRUE;
11741
11742 #if USE_NEW_PLAYER_ASSIGNMENTS
11743   // !!! also map player actions in single player mode !!!
11744   // if (game.team_mode)
11745   if (1)
11746   {
11747     byte mapped_action[MAX_PLAYERS];
11748
11749 #if DEBUG_PLAYER_ACTIONS
11750     for (i = 0; i < MAX_PLAYERS; i++)
11751       DebugContinued("", "%d, ", stored_player[i].effective_action);
11752 #endif
11753
11754     for (i = 0; i < MAX_PLAYERS; i++)
11755       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11756
11757     for (i = 0; i < MAX_PLAYERS; i++)
11758       stored_player[i].effective_action = mapped_action[i];
11759
11760 #if DEBUG_PLAYER_ACTIONS
11761     DebugContinued("", "=> ");
11762     for (i = 0; i < MAX_PLAYERS; i++)
11763       DebugContinued("", "%d, ", stored_player[i].effective_action);
11764     DebugContinued("game:playing:player", "\n");
11765 #endif
11766   }
11767 #if DEBUG_PLAYER_ACTIONS
11768   else
11769   {
11770     for (i = 0; i < MAX_PLAYERS; i++)
11771       DebugContinued("", "%d, ", stored_player[i].effective_action);
11772     DebugContinued("game:playing:player", "\n");
11773   }
11774 #endif
11775 #endif
11776
11777   for (i = 0; i < MAX_PLAYERS; i++)
11778   {
11779     // allow engine snapshot in case of changed movement attempt
11780     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11781         (stored_player[i].effective_action & KEY_MOTION))
11782       game.snapshot.changed_action = TRUE;
11783
11784     // allow engine snapshot in case of snapping/dropping attempt
11785     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11786         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11787       game.snapshot.changed_action = TRUE;
11788
11789     game.snapshot.last_action[i] = stored_player[i].effective_action;
11790   }
11791
11792   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11793   {
11794     GameActions_EM_Main();
11795   }
11796   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11797   {
11798     GameActions_SP_Main();
11799   }
11800   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11801   {
11802     GameActions_MM_Main();
11803   }
11804   else
11805   {
11806     GameActions_RND_Main();
11807   }
11808
11809   BlitScreenToBitmap(backbuffer);
11810
11811   CheckLevelSolved();
11812   CheckLevelTime();
11813
11814   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11815
11816   if (global.show_frames_per_second)
11817   {
11818     static unsigned int fps_counter = 0;
11819     static int fps_frames = 0;
11820     unsigned int fps_delay_ms = Counter() - fps_counter;
11821
11822     fps_frames++;
11823
11824     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11825     {
11826       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11827
11828       fps_frames = 0;
11829       fps_counter = Counter();
11830
11831       // always draw FPS to screen after FPS value was updated
11832       redraw_mask |= REDRAW_FPS;
11833     }
11834
11835     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11836     if (GetDrawDeactivationMask() == REDRAW_NONE)
11837       redraw_mask |= REDRAW_FPS;
11838   }
11839 }
11840
11841 static void GameActions_CheckSaveEngineSnapshot(void)
11842 {
11843   if (!game.snapshot.save_snapshot)
11844     return;
11845
11846   // clear flag for saving snapshot _before_ saving snapshot
11847   game.snapshot.save_snapshot = FALSE;
11848
11849   SaveEngineSnapshotToList();
11850 }
11851
11852 void GameActions(void)
11853 {
11854   GameActionsExt();
11855
11856   GameActions_CheckSaveEngineSnapshot();
11857 }
11858
11859 void GameActions_EM_Main(void)
11860 {
11861   byte effective_action[MAX_PLAYERS];
11862   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11863   int i;
11864
11865   for (i = 0; i < MAX_PLAYERS; i++)
11866     effective_action[i] = stored_player[i].effective_action;
11867
11868   GameActions_EM(effective_action, warp_mode);
11869 }
11870
11871 void GameActions_SP_Main(void)
11872 {
11873   byte effective_action[MAX_PLAYERS];
11874   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11875   int i;
11876
11877   for (i = 0; i < MAX_PLAYERS; i++)
11878     effective_action[i] = stored_player[i].effective_action;
11879
11880   GameActions_SP(effective_action, warp_mode);
11881
11882   for (i = 0; i < MAX_PLAYERS; i++)
11883   {
11884     if (stored_player[i].force_dropping)
11885       stored_player[i].action |= KEY_BUTTON_DROP;
11886
11887     stored_player[i].force_dropping = FALSE;
11888   }
11889 }
11890
11891 void GameActions_MM_Main(void)
11892 {
11893   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11894
11895   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11896 }
11897
11898 void GameActions_RND_Main(void)
11899 {
11900   GameActions_RND();
11901 }
11902
11903 void GameActions_RND(void)
11904 {
11905   static struct MouseActionInfo mouse_action_last = { 0 };
11906   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11907   int magic_wall_x = 0, magic_wall_y = 0;
11908   int i, x, y, element, graphic, last_gfx_frame;
11909
11910   InitPlayfieldScanModeVars();
11911
11912   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11913   {
11914     SCAN_PLAYFIELD(x, y)
11915     {
11916       ChangeCount[x][y] = 0;
11917       ChangeEvent[x][y] = -1;
11918     }
11919   }
11920
11921   if (game.set_centered_player)
11922   {
11923     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11924
11925     // switching to "all players" only possible if all players fit to screen
11926     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11927     {
11928       game.centered_player_nr_next = game.centered_player_nr;
11929       game.set_centered_player = FALSE;
11930     }
11931
11932     // do not switch focus to non-existing (or non-active) player
11933     if (game.centered_player_nr_next >= 0 &&
11934         !stored_player[game.centered_player_nr_next].active)
11935     {
11936       game.centered_player_nr_next = game.centered_player_nr;
11937       game.set_centered_player = FALSE;
11938     }
11939   }
11940
11941   if (game.set_centered_player &&
11942       ScreenMovPos == 0)        // screen currently aligned at tile position
11943   {
11944     int sx, sy;
11945
11946     if (game.centered_player_nr_next == -1)
11947     {
11948       setScreenCenteredToAllPlayers(&sx, &sy);
11949     }
11950     else
11951     {
11952       sx = stored_player[game.centered_player_nr_next].jx;
11953       sy = stored_player[game.centered_player_nr_next].jy;
11954     }
11955
11956     game.centered_player_nr = game.centered_player_nr_next;
11957     game.set_centered_player = FALSE;
11958
11959     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11960     DrawGameDoorValues();
11961   }
11962
11963   // check single step mode (set flag and clear again if any player is active)
11964   game.enter_single_step_mode =
11965     (tape.single_step && tape.recording && !tape.pausing);
11966
11967   for (i = 0; i < MAX_PLAYERS; i++)
11968   {
11969     int actual_player_action = stored_player[i].effective_action;
11970
11971 #if 1
11972     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11973        - rnd_equinox_tetrachloride 048
11974        - rnd_equinox_tetrachloride_ii 096
11975        - rnd_emanuel_schmieg 002
11976        - doctor_sloan_ww 001, 020
11977     */
11978     if (stored_player[i].MovPos == 0)
11979       CheckGravityMovement(&stored_player[i]);
11980 #endif
11981
11982     // overwrite programmed action with tape action
11983     if (stored_player[i].programmed_action)
11984       actual_player_action = stored_player[i].programmed_action;
11985
11986     PlayerActions(&stored_player[i], actual_player_action);
11987
11988     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11989   }
11990
11991   // single step pause mode may already have been toggled by "ScrollPlayer()"
11992   if (game.enter_single_step_mode && !tape.pausing)
11993     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11994
11995   ScrollScreen(NULL, SCROLL_GO_ON);
11996
11997   /* for backwards compatibility, the following code emulates a fixed bug that
11998      occured when pushing elements (causing elements that just made their last
11999      pushing step to already (if possible) make their first falling step in the
12000      same game frame, which is bad); this code is also needed to use the famous
12001      "spring push bug" which is used in older levels and might be wanted to be
12002      used also in newer levels, but in this case the buggy pushing code is only
12003      affecting the "spring" element and no other elements */
12004
12005   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12006   {
12007     for (i = 0; i < MAX_PLAYERS; i++)
12008     {
12009       struct PlayerInfo *player = &stored_player[i];
12010       int x = player->jx;
12011       int y = player->jy;
12012
12013       if (player->active && player->is_pushing && player->is_moving &&
12014           IS_MOVING(x, y) &&
12015           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12016            Tile[x][y] == EL_SPRING))
12017       {
12018         ContinueMoving(x, y);
12019
12020         // continue moving after pushing (this is actually a bug)
12021         if (!IS_MOVING(x, y))
12022           Stop[x][y] = FALSE;
12023       }
12024     }
12025   }
12026
12027   SCAN_PLAYFIELD(x, y)
12028   {
12029     Last[x][y] = Tile[x][y];
12030
12031     ChangeCount[x][y] = 0;
12032     ChangeEvent[x][y] = -1;
12033
12034     // this must be handled before main playfield loop
12035     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12036     {
12037       MovDelay[x][y]--;
12038       if (MovDelay[x][y] <= 0)
12039         RemoveField(x, y);
12040     }
12041
12042     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12043     {
12044       MovDelay[x][y]--;
12045       if (MovDelay[x][y] <= 0)
12046       {
12047         int element = Store[x][y];
12048         int move_direction = MovDir[x][y];
12049         int player_index_bit = Store2[x][y];
12050
12051         Store[x][y] = 0;
12052         Store2[x][y] = 0;
12053
12054         RemoveField(x, y);
12055         TEST_DrawLevelField(x, y);
12056
12057         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12058       }
12059     }
12060
12061 #if DEBUG
12062     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12063     {
12064       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12065             x, y);
12066       Debug("game:playing:GameActions_RND", "This should never happen!");
12067
12068       ChangePage[x][y] = -1;
12069     }
12070 #endif
12071
12072     Stop[x][y] = FALSE;
12073     if (WasJustMoving[x][y] > 0)
12074       WasJustMoving[x][y]--;
12075     if (WasJustFalling[x][y] > 0)
12076       WasJustFalling[x][y]--;
12077     if (CheckCollision[x][y] > 0)
12078       CheckCollision[x][y]--;
12079     if (CheckImpact[x][y] > 0)
12080       CheckImpact[x][y]--;
12081
12082     GfxFrame[x][y]++;
12083
12084     /* reset finished pushing action (not done in ContinueMoving() to allow
12085        continuous pushing animation for elements with zero push delay) */
12086     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12087     {
12088       ResetGfxAnimation(x, y);
12089       TEST_DrawLevelField(x, y);
12090     }
12091
12092 #if DEBUG
12093     if (IS_BLOCKED(x, y))
12094     {
12095       int oldx, oldy;
12096
12097       Blocked2Moving(x, y, &oldx, &oldy);
12098       if (!IS_MOVING(oldx, oldy))
12099       {
12100         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12101         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12102         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12103         Debug("game:playing:GameActions_RND", "This should never happen!");
12104       }
12105     }
12106 #endif
12107   }
12108
12109   if (mouse_action.button)
12110   {
12111     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12112
12113     x = mouse_action.lx;
12114     y = mouse_action.ly;
12115     element = Tile[x][y];
12116
12117     if (new_button)
12118     {
12119       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12120       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12121     }
12122
12123     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12124     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12125   }
12126
12127   SCAN_PLAYFIELD(x, y)
12128   {
12129     element = Tile[x][y];
12130     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12131     last_gfx_frame = GfxFrame[x][y];
12132
12133     ResetGfxFrame(x, y);
12134
12135     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12136       DrawLevelGraphicAnimation(x, y, graphic);
12137
12138     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12139         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12140       ResetRandomAnimationValue(x, y);
12141
12142     SetRandomAnimationValue(x, y);
12143
12144     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12145
12146     if (IS_INACTIVE(element))
12147     {
12148       if (IS_ANIMATED(graphic))
12149         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12150
12151       continue;
12152     }
12153
12154     // this may take place after moving, so 'element' may have changed
12155     if (IS_CHANGING(x, y) &&
12156         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12157     {
12158       int page = element_info[element].event_page_nr[CE_DELAY];
12159
12160       HandleElementChange(x, y, page);
12161
12162       element = Tile[x][y];
12163       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12164     }
12165
12166     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12167     {
12168       StartMoving(x, y);
12169
12170       element = Tile[x][y];
12171       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12172
12173       if (IS_ANIMATED(graphic) &&
12174           !IS_MOVING(x, y) &&
12175           !Stop[x][y])
12176         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12177
12178       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12179         TEST_DrawTwinkleOnField(x, y);
12180     }
12181     else if (element == EL_ACID)
12182     {
12183       if (!Stop[x][y])
12184         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12185     }
12186     else if ((element == EL_EXIT_OPEN ||
12187               element == EL_EM_EXIT_OPEN ||
12188               element == EL_SP_EXIT_OPEN ||
12189               element == EL_STEEL_EXIT_OPEN ||
12190               element == EL_EM_STEEL_EXIT_OPEN ||
12191               element == EL_SP_TERMINAL ||
12192               element == EL_SP_TERMINAL_ACTIVE ||
12193               element == EL_EXTRA_TIME ||
12194               element == EL_SHIELD_NORMAL ||
12195               element == EL_SHIELD_DEADLY) &&
12196              IS_ANIMATED(graphic))
12197       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12198     else if (IS_MOVING(x, y))
12199       ContinueMoving(x, y);
12200     else if (IS_ACTIVE_BOMB(element))
12201       CheckDynamite(x, y);
12202     else if (element == EL_AMOEBA_GROWING)
12203       AmoebaGrowing(x, y);
12204     else if (element == EL_AMOEBA_SHRINKING)
12205       AmoebaShrinking(x, y);
12206
12207 #if !USE_NEW_AMOEBA_CODE
12208     else if (IS_AMOEBALIVE(element))
12209       AmoebaReproduce(x, y);
12210 #endif
12211
12212     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12213       Life(x, y);
12214     else if (element == EL_EXIT_CLOSED)
12215       CheckExit(x, y);
12216     else if (element == EL_EM_EXIT_CLOSED)
12217       CheckExitEM(x, y);
12218     else if (element == EL_STEEL_EXIT_CLOSED)
12219       CheckExitSteel(x, y);
12220     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12221       CheckExitSteelEM(x, y);
12222     else if (element == EL_SP_EXIT_CLOSED)
12223       CheckExitSP(x, y);
12224     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12225              element == EL_EXPANDABLE_STEELWALL_GROWING)
12226       MauerWaechst(x, y);
12227     else if (element == EL_EXPANDABLE_WALL ||
12228              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12229              element == EL_EXPANDABLE_WALL_VERTICAL ||
12230              element == EL_EXPANDABLE_WALL_ANY ||
12231              element == EL_BD_EXPANDABLE_WALL)
12232       MauerAbleger(x, y);
12233     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12234              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12235              element == EL_EXPANDABLE_STEELWALL_ANY)
12236       MauerAblegerStahl(x, y);
12237     else if (element == EL_FLAMES)
12238       CheckForDragon(x, y);
12239     else if (element == EL_EXPLOSION)
12240       ; // drawing of correct explosion animation is handled separately
12241     else if (element == EL_ELEMENT_SNAPPING ||
12242              element == EL_DIAGONAL_SHRINKING ||
12243              element == EL_DIAGONAL_GROWING)
12244     {
12245       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12246
12247       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12248     }
12249     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12250       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12251
12252     if (IS_BELT_ACTIVE(element))
12253       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12254
12255     if (game.magic_wall_active)
12256     {
12257       int jx = local_player->jx, jy = local_player->jy;
12258
12259       // play the element sound at the position nearest to the player
12260       if ((element == EL_MAGIC_WALL_FULL ||
12261            element == EL_MAGIC_WALL_ACTIVE ||
12262            element == EL_MAGIC_WALL_EMPTYING ||
12263            element == EL_BD_MAGIC_WALL_FULL ||
12264            element == EL_BD_MAGIC_WALL_ACTIVE ||
12265            element == EL_BD_MAGIC_WALL_EMPTYING ||
12266            element == EL_DC_MAGIC_WALL_FULL ||
12267            element == EL_DC_MAGIC_WALL_ACTIVE ||
12268            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12269           ABS(x - jx) + ABS(y - jy) <
12270           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12271       {
12272         magic_wall_x = x;
12273         magic_wall_y = y;
12274       }
12275     }
12276   }
12277
12278 #if USE_NEW_AMOEBA_CODE
12279   // new experimental amoeba growth stuff
12280   if (!(FrameCounter % 8))
12281   {
12282     static unsigned int random = 1684108901;
12283
12284     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12285     {
12286       x = RND(lev_fieldx);
12287       y = RND(lev_fieldy);
12288       element = Tile[x][y];
12289
12290       if (!IS_PLAYER(x,y) &&
12291           (element == EL_EMPTY ||
12292            CAN_GROW_INTO(element) ||
12293            element == EL_QUICKSAND_EMPTY ||
12294            element == EL_QUICKSAND_FAST_EMPTY ||
12295            element == EL_ACID_SPLASH_LEFT ||
12296            element == EL_ACID_SPLASH_RIGHT))
12297       {
12298         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12299             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12300             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12301             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12302           Tile[x][y] = EL_AMOEBA_DROP;
12303       }
12304
12305       random = random * 129 + 1;
12306     }
12307   }
12308 #endif
12309
12310   game.explosions_delayed = FALSE;
12311
12312   SCAN_PLAYFIELD(x, y)
12313   {
12314     element = Tile[x][y];
12315
12316     if (ExplodeField[x][y])
12317       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12318     else if (element == EL_EXPLOSION)
12319       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12320
12321     ExplodeField[x][y] = EX_TYPE_NONE;
12322   }
12323
12324   game.explosions_delayed = TRUE;
12325
12326   if (game.magic_wall_active)
12327   {
12328     if (!(game.magic_wall_time_left % 4))
12329     {
12330       int element = Tile[magic_wall_x][magic_wall_y];
12331
12332       if (element == EL_BD_MAGIC_WALL_FULL ||
12333           element == EL_BD_MAGIC_WALL_ACTIVE ||
12334           element == EL_BD_MAGIC_WALL_EMPTYING)
12335         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12336       else if (element == EL_DC_MAGIC_WALL_FULL ||
12337                element == EL_DC_MAGIC_WALL_ACTIVE ||
12338                element == EL_DC_MAGIC_WALL_EMPTYING)
12339         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12340       else
12341         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12342     }
12343
12344     if (game.magic_wall_time_left > 0)
12345     {
12346       game.magic_wall_time_left--;
12347
12348       if (!game.magic_wall_time_left)
12349       {
12350         SCAN_PLAYFIELD(x, y)
12351         {
12352           element = Tile[x][y];
12353
12354           if (element == EL_MAGIC_WALL_ACTIVE ||
12355               element == EL_MAGIC_WALL_FULL)
12356           {
12357             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12358             TEST_DrawLevelField(x, y);
12359           }
12360           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12361                    element == EL_BD_MAGIC_WALL_FULL)
12362           {
12363             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12364             TEST_DrawLevelField(x, y);
12365           }
12366           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12367                    element == EL_DC_MAGIC_WALL_FULL)
12368           {
12369             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12370             TEST_DrawLevelField(x, y);
12371           }
12372         }
12373
12374         game.magic_wall_active = FALSE;
12375       }
12376     }
12377   }
12378
12379   if (game.light_time_left > 0)
12380   {
12381     game.light_time_left--;
12382
12383     if (game.light_time_left == 0)
12384       RedrawAllLightSwitchesAndInvisibleElements();
12385   }
12386
12387   if (game.timegate_time_left > 0)
12388   {
12389     game.timegate_time_left--;
12390
12391     if (game.timegate_time_left == 0)
12392       CloseAllOpenTimegates();
12393   }
12394
12395   if (game.lenses_time_left > 0)
12396   {
12397     game.lenses_time_left--;
12398
12399     if (game.lenses_time_left == 0)
12400       RedrawAllInvisibleElementsForLenses();
12401   }
12402
12403   if (game.magnify_time_left > 0)
12404   {
12405     game.magnify_time_left--;
12406
12407     if (game.magnify_time_left == 0)
12408       RedrawAllInvisibleElementsForMagnifier();
12409   }
12410
12411   for (i = 0; i < MAX_PLAYERS; i++)
12412   {
12413     struct PlayerInfo *player = &stored_player[i];
12414
12415     if (SHIELD_ON(player))
12416     {
12417       if (player->shield_deadly_time_left)
12418         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12419       else if (player->shield_normal_time_left)
12420         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12421     }
12422   }
12423
12424 #if USE_DELAYED_GFX_REDRAW
12425   SCAN_PLAYFIELD(x, y)
12426   {
12427     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12428     {
12429       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12430          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12431
12432       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12433         DrawLevelField(x, y);
12434
12435       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12436         DrawLevelFieldCrumbled(x, y);
12437
12438       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12439         DrawLevelFieldCrumbledNeighbours(x, y);
12440
12441       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12442         DrawTwinkleOnField(x, y);
12443     }
12444
12445     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12446   }
12447 #endif
12448
12449   DrawAllPlayers();
12450   PlayAllPlayersSound();
12451
12452   for (i = 0; i < MAX_PLAYERS; i++)
12453   {
12454     struct PlayerInfo *player = &stored_player[i];
12455
12456     if (player->show_envelope != 0 && (!player->active ||
12457                                        player->MovPos == 0))
12458     {
12459       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12460
12461       player->show_envelope = 0;
12462     }
12463   }
12464
12465   // use random number generator in every frame to make it less predictable
12466   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12467     RND(1);
12468
12469   mouse_action_last = mouse_action;
12470 }
12471
12472 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12473 {
12474   int min_x = x, min_y = y, max_x = x, max_y = y;
12475   int scr_fieldx = getScreenFieldSizeX();
12476   int scr_fieldy = getScreenFieldSizeY();
12477   int i;
12478
12479   for (i = 0; i < MAX_PLAYERS; i++)
12480   {
12481     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12482
12483     if (!stored_player[i].active || &stored_player[i] == player)
12484       continue;
12485
12486     min_x = MIN(min_x, jx);
12487     min_y = MIN(min_y, jy);
12488     max_x = MAX(max_x, jx);
12489     max_y = MAX(max_y, jy);
12490   }
12491
12492   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12493 }
12494
12495 static boolean AllPlayersInVisibleScreen(void)
12496 {
12497   int i;
12498
12499   for (i = 0; i < MAX_PLAYERS; i++)
12500   {
12501     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12502
12503     if (!stored_player[i].active)
12504       continue;
12505
12506     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12507       return FALSE;
12508   }
12509
12510   return TRUE;
12511 }
12512
12513 void ScrollLevel(int dx, int dy)
12514 {
12515   int scroll_offset = 2 * TILEX_VAR;
12516   int x, y;
12517
12518   BlitBitmap(drawto_field, drawto_field,
12519              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12520              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12521              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12522              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12523              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12524              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12525
12526   if (dx != 0)
12527   {
12528     x = (dx == 1 ? BX1 : BX2);
12529     for (y = BY1; y <= BY2; y++)
12530       DrawScreenField(x, y);
12531   }
12532
12533   if (dy != 0)
12534   {
12535     y = (dy == 1 ? BY1 : BY2);
12536     for (x = BX1; x <= BX2; x++)
12537       DrawScreenField(x, y);
12538   }
12539
12540   redraw_mask |= REDRAW_FIELD;
12541 }
12542
12543 static boolean canFallDown(struct PlayerInfo *player)
12544 {
12545   int jx = player->jx, jy = player->jy;
12546
12547   return (IN_LEV_FIELD(jx, jy + 1) &&
12548           (IS_FREE(jx, jy + 1) ||
12549            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12550           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12551           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12552 }
12553
12554 static boolean canPassField(int x, int y, int move_dir)
12555 {
12556   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12557   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12558   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12559   int nextx = x + dx;
12560   int nexty = y + dy;
12561   int element = Tile[x][y];
12562
12563   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12564           !CAN_MOVE(element) &&
12565           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12566           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12567           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12568 }
12569
12570 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12571 {
12572   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12573   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12574   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12575   int newx = x + dx;
12576   int newy = y + dy;
12577
12578   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12579           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12580           (IS_DIGGABLE(Tile[newx][newy]) ||
12581            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12582            canPassField(newx, newy, move_dir)));
12583 }
12584
12585 static void CheckGravityMovement(struct PlayerInfo *player)
12586 {
12587   if (player->gravity && !player->programmed_action)
12588   {
12589     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12590     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12591     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12592     int jx = player->jx, jy = player->jy;
12593     boolean player_is_moving_to_valid_field =
12594       (!player_is_snapping &&
12595        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12596         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12597     boolean player_can_fall_down = canFallDown(player);
12598
12599     if (player_can_fall_down &&
12600         !player_is_moving_to_valid_field)
12601       player->programmed_action = MV_DOWN;
12602   }
12603 }
12604
12605 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12606 {
12607   return CheckGravityMovement(player);
12608
12609   if (player->gravity && !player->programmed_action)
12610   {
12611     int jx = player->jx, jy = player->jy;
12612     boolean field_under_player_is_free =
12613       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12614     boolean player_is_standing_on_valid_field =
12615       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12616        (IS_WALKABLE(Tile[jx][jy]) &&
12617         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12618
12619     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12620       player->programmed_action = MV_DOWN;
12621   }
12622 }
12623
12624 /*
12625   MovePlayerOneStep()
12626   -----------------------------------------------------------------------------
12627   dx, dy:               direction (non-diagonal) to try to move the player to
12628   real_dx, real_dy:     direction as read from input device (can be diagonal)
12629 */
12630
12631 boolean MovePlayerOneStep(struct PlayerInfo *player,
12632                           int dx, int dy, int real_dx, int real_dy)
12633 {
12634   int jx = player->jx, jy = player->jy;
12635   int new_jx = jx + dx, new_jy = jy + dy;
12636   int can_move;
12637   boolean player_can_move = !player->cannot_move;
12638
12639   if (!player->active || (!dx && !dy))
12640     return MP_NO_ACTION;
12641
12642   player->MovDir = (dx < 0 ? MV_LEFT :
12643                     dx > 0 ? MV_RIGHT :
12644                     dy < 0 ? MV_UP :
12645                     dy > 0 ? MV_DOWN :  MV_NONE);
12646
12647   if (!IN_LEV_FIELD(new_jx, new_jy))
12648     return MP_NO_ACTION;
12649
12650   if (!player_can_move)
12651   {
12652     if (player->MovPos == 0)
12653     {
12654       player->is_moving = FALSE;
12655       player->is_digging = FALSE;
12656       player->is_collecting = FALSE;
12657       player->is_snapping = FALSE;
12658       player->is_pushing = FALSE;
12659     }
12660   }
12661
12662   if (!network.enabled && game.centered_player_nr == -1 &&
12663       !AllPlayersInSight(player, new_jx, new_jy))
12664     return MP_NO_ACTION;
12665
12666   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12667   if (can_move != MP_MOVING)
12668     return can_move;
12669
12670   // check if DigField() has caused relocation of the player
12671   if (player->jx != jx || player->jy != jy)
12672     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12673
12674   StorePlayer[jx][jy] = 0;
12675   player->last_jx = jx;
12676   player->last_jy = jy;
12677   player->jx = new_jx;
12678   player->jy = new_jy;
12679   StorePlayer[new_jx][new_jy] = player->element_nr;
12680
12681   if (player->move_delay_value_next != -1)
12682   {
12683     player->move_delay_value = player->move_delay_value_next;
12684     player->move_delay_value_next = -1;
12685   }
12686
12687   player->MovPos =
12688     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12689
12690   player->step_counter++;
12691
12692   PlayerVisit[jx][jy] = FrameCounter;
12693
12694   player->is_moving = TRUE;
12695
12696 #if 1
12697   // should better be called in MovePlayer(), but this breaks some tapes
12698   ScrollPlayer(player, SCROLL_INIT);
12699 #endif
12700
12701   return MP_MOVING;
12702 }
12703
12704 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12705 {
12706   int jx = player->jx, jy = player->jy;
12707   int old_jx = jx, old_jy = jy;
12708   int moved = MP_NO_ACTION;
12709
12710   if (!player->active)
12711     return FALSE;
12712
12713   if (!dx && !dy)
12714   {
12715     if (player->MovPos == 0)
12716     {
12717       player->is_moving = FALSE;
12718       player->is_digging = FALSE;
12719       player->is_collecting = FALSE;
12720       player->is_snapping = FALSE;
12721       player->is_pushing = FALSE;
12722     }
12723
12724     return FALSE;
12725   }
12726
12727   if (player->move_delay > 0)
12728     return FALSE;
12729
12730   player->move_delay = -1;              // set to "uninitialized" value
12731
12732   // store if player is automatically moved to next field
12733   player->is_auto_moving = (player->programmed_action != MV_NONE);
12734
12735   // remove the last programmed player action
12736   player->programmed_action = 0;
12737
12738   if (player->MovPos)
12739   {
12740     // should only happen if pre-1.2 tape recordings are played
12741     // this is only for backward compatibility
12742
12743     int original_move_delay_value = player->move_delay_value;
12744
12745 #if DEBUG
12746     Debug("game:playing:MovePlayer",
12747           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12748           tape.counter);
12749 #endif
12750
12751     // scroll remaining steps with finest movement resolution
12752     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12753
12754     while (player->MovPos)
12755     {
12756       ScrollPlayer(player, SCROLL_GO_ON);
12757       ScrollScreen(NULL, SCROLL_GO_ON);
12758
12759       AdvanceFrameAndPlayerCounters(player->index_nr);
12760
12761       DrawAllPlayers();
12762       BackToFront_WithFrameDelay(0);
12763     }
12764
12765     player->move_delay_value = original_move_delay_value;
12766   }
12767
12768   player->is_active = FALSE;
12769
12770   if (player->last_move_dir & MV_HORIZONTAL)
12771   {
12772     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12773       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12774   }
12775   else
12776   {
12777     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12778       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12779   }
12780
12781   if (!moved && !player->is_active)
12782   {
12783     player->is_moving = FALSE;
12784     player->is_digging = FALSE;
12785     player->is_collecting = FALSE;
12786     player->is_snapping = FALSE;
12787     player->is_pushing = FALSE;
12788   }
12789
12790   jx = player->jx;
12791   jy = player->jy;
12792
12793   if (moved & MP_MOVING && !ScreenMovPos &&
12794       (player->index_nr == game.centered_player_nr ||
12795        game.centered_player_nr == -1))
12796   {
12797     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12798
12799     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12800     {
12801       // actual player has left the screen -- scroll in that direction
12802       if (jx != old_jx)         // player has moved horizontally
12803         scroll_x += (jx - old_jx);
12804       else                      // player has moved vertically
12805         scroll_y += (jy - old_jy);
12806     }
12807     else
12808     {
12809       int offset_raw = game.scroll_delay_value;
12810
12811       if (jx != old_jx)         // player has moved horizontally
12812       {
12813         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12814         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12815         int new_scroll_x = jx - MIDPOSX + offset_x;
12816
12817         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12818             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12819           scroll_x = new_scroll_x;
12820
12821         // don't scroll over playfield boundaries
12822         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12823
12824         // don't scroll more than one field at a time
12825         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12826
12827         // don't scroll against the player's moving direction
12828         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12829             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12830           scroll_x = old_scroll_x;
12831       }
12832       else                      // player has moved vertically
12833       {
12834         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12835         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12836         int new_scroll_y = jy - MIDPOSY + offset_y;
12837
12838         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12839             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12840           scroll_y = new_scroll_y;
12841
12842         // don't scroll over playfield boundaries
12843         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12844
12845         // don't scroll more than one field at a time
12846         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12847
12848         // don't scroll against the player's moving direction
12849         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12850             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12851           scroll_y = old_scroll_y;
12852       }
12853     }
12854
12855     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12856     {
12857       if (!network.enabled && game.centered_player_nr == -1 &&
12858           !AllPlayersInVisibleScreen())
12859       {
12860         scroll_x = old_scroll_x;
12861         scroll_y = old_scroll_y;
12862       }
12863       else
12864       {
12865         ScrollScreen(player, SCROLL_INIT);
12866         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12867       }
12868     }
12869   }
12870
12871   player->StepFrame = 0;
12872
12873   if (moved & MP_MOVING)
12874   {
12875     if (old_jx != jx && old_jy == jy)
12876       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12877     else if (old_jx == jx && old_jy != jy)
12878       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12879
12880     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12881
12882     player->last_move_dir = player->MovDir;
12883     player->is_moving = TRUE;
12884     player->is_snapping = FALSE;
12885     player->is_switching = FALSE;
12886     player->is_dropping = FALSE;
12887     player->is_dropping_pressed = FALSE;
12888     player->drop_pressed_delay = 0;
12889
12890 #if 0
12891     // should better be called here than above, but this breaks some tapes
12892     ScrollPlayer(player, SCROLL_INIT);
12893 #endif
12894   }
12895   else
12896   {
12897     CheckGravityMovementWhenNotMoving(player);
12898
12899     player->is_moving = FALSE;
12900
12901     /* at this point, the player is allowed to move, but cannot move right now
12902        (e.g. because of something blocking the way) -- ensure that the player
12903        is also allowed to move in the next frame (in old versions before 3.1.1,
12904        the player was forced to wait again for eight frames before next try) */
12905
12906     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12907       player->move_delay = 0;   // allow direct movement in the next frame
12908   }
12909
12910   if (player->move_delay == -1)         // not yet initialized by DigField()
12911     player->move_delay = player->move_delay_value;
12912
12913   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12914   {
12915     TestIfPlayerTouchesBadThing(jx, jy);
12916     TestIfPlayerTouchesCustomElement(jx, jy);
12917   }
12918
12919   if (!player->active)
12920     RemovePlayer(player);
12921
12922   return moved;
12923 }
12924
12925 void ScrollPlayer(struct PlayerInfo *player, int mode)
12926 {
12927   int jx = player->jx, jy = player->jy;
12928   int last_jx = player->last_jx, last_jy = player->last_jy;
12929   int move_stepsize = TILEX / player->move_delay_value;
12930
12931   if (!player->active)
12932     return;
12933
12934   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12935     return;
12936
12937   if (mode == SCROLL_INIT)
12938   {
12939     player->actual_frame_counter = FrameCounter;
12940     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12941
12942     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12943         Tile[last_jx][last_jy] == EL_EMPTY)
12944     {
12945       int last_field_block_delay = 0;   // start with no blocking at all
12946       int block_delay_adjustment = player->block_delay_adjustment;
12947
12948       // if player blocks last field, add delay for exactly one move
12949       if (player->block_last_field)
12950       {
12951         last_field_block_delay += player->move_delay_value;
12952
12953         // when blocking enabled, prevent moving up despite gravity
12954         if (player->gravity && player->MovDir == MV_UP)
12955           block_delay_adjustment = -1;
12956       }
12957
12958       // add block delay adjustment (also possible when not blocking)
12959       last_field_block_delay += block_delay_adjustment;
12960
12961       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12962       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12963     }
12964
12965     if (player->MovPos != 0)    // player has not yet reached destination
12966       return;
12967   }
12968   else if (!FrameReached(&player->actual_frame_counter, 1))
12969     return;
12970
12971   if (player->MovPos != 0)
12972   {
12973     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12974     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12975
12976     // before DrawPlayer() to draw correct player graphic for this case
12977     if (player->MovPos == 0)
12978       CheckGravityMovement(player);
12979   }
12980
12981   if (player->MovPos == 0)      // player reached destination field
12982   {
12983     if (player->move_delay_reset_counter > 0)
12984     {
12985       player->move_delay_reset_counter--;
12986
12987       if (player->move_delay_reset_counter == 0)
12988       {
12989         // continue with normal speed after quickly moving through gate
12990         HALVE_PLAYER_SPEED(player);
12991
12992         // be able to make the next move without delay
12993         player->move_delay = 0;
12994       }
12995     }
12996
12997     player->last_jx = jx;
12998     player->last_jy = jy;
12999
13000     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13001         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13002         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13003         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13004         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13005         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13006         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13007         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13008     {
13009       ExitPlayer(player);
13010
13011       if (game.players_still_needed == 0 &&
13012           (game.friends_still_needed == 0 ||
13013            IS_SP_ELEMENT(Tile[jx][jy])))
13014         LevelSolved();
13015     }
13016
13017     // this breaks one level: "machine", level 000
13018     {
13019       int move_direction = player->MovDir;
13020       int enter_side = MV_DIR_OPPOSITE(move_direction);
13021       int leave_side = move_direction;
13022       int old_jx = last_jx;
13023       int old_jy = last_jy;
13024       int old_element = Tile[old_jx][old_jy];
13025       int new_element = Tile[jx][jy];
13026
13027       if (IS_CUSTOM_ELEMENT(old_element))
13028         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13029                                    CE_LEFT_BY_PLAYER,
13030                                    player->index_bit, leave_side);
13031
13032       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13033                                           CE_PLAYER_LEAVES_X,
13034                                           player->index_bit, leave_side);
13035
13036       if (IS_CUSTOM_ELEMENT(new_element))
13037         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13038                                    player->index_bit, enter_side);
13039
13040       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13041                                           CE_PLAYER_ENTERS_X,
13042                                           player->index_bit, enter_side);
13043
13044       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13045                                         CE_MOVE_OF_X, move_direction);
13046     }
13047
13048     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13049     {
13050       TestIfPlayerTouchesBadThing(jx, jy);
13051       TestIfPlayerTouchesCustomElement(jx, jy);
13052
13053       /* needed because pushed element has not yet reached its destination,
13054          so it would trigger a change event at its previous field location */
13055       if (!player->is_pushing)
13056         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13057
13058       if (level.finish_dig_collect &&
13059           (player->is_digging || player->is_collecting))
13060       {
13061         int last_element = player->last_removed_element;
13062         int move_direction = player->MovDir;
13063         int enter_side = MV_DIR_OPPOSITE(move_direction);
13064         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13065                             CE_PLAYER_COLLECTS_X);
13066
13067         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13068                                             player->index_bit, enter_side);
13069
13070         player->last_removed_element = EL_UNDEFINED;
13071       }
13072
13073       if (!player->active)
13074         RemovePlayer(player);
13075     }
13076
13077     if (!game.LevelSolved && level.use_step_counter)
13078     {
13079       int i;
13080
13081       TimePlayed++;
13082
13083       if (TimeLeft > 0)
13084       {
13085         TimeLeft--;
13086
13087         if (TimeLeft <= 10 && setup.time_limit)
13088           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13089
13090         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13091
13092         DisplayGameControlValues();
13093
13094         if (!TimeLeft && setup.time_limit)
13095           for (i = 0; i < MAX_PLAYERS; i++)
13096             KillPlayer(&stored_player[i]);
13097       }
13098       else if (game.no_time_limit && !game.all_players_gone)
13099       {
13100         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13101
13102         DisplayGameControlValues();
13103       }
13104     }
13105
13106     if (tape.single_step && tape.recording && !tape.pausing &&
13107         !player->programmed_action)
13108       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13109
13110     if (!player->programmed_action)
13111       CheckSaveEngineSnapshot(player);
13112   }
13113 }
13114
13115 void ScrollScreen(struct PlayerInfo *player, int mode)
13116 {
13117   static unsigned int screen_frame_counter = 0;
13118
13119   if (mode == SCROLL_INIT)
13120   {
13121     // set scrolling step size according to actual player's moving speed
13122     ScrollStepSize = TILEX / player->move_delay_value;
13123
13124     screen_frame_counter = FrameCounter;
13125     ScreenMovDir = player->MovDir;
13126     ScreenMovPos = player->MovPos;
13127     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13128     return;
13129   }
13130   else if (!FrameReached(&screen_frame_counter, 1))
13131     return;
13132
13133   if (ScreenMovPos)
13134   {
13135     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13136     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13137     redraw_mask |= REDRAW_FIELD;
13138   }
13139   else
13140     ScreenMovDir = MV_NONE;
13141 }
13142
13143 void TestIfPlayerTouchesCustomElement(int x, int y)
13144 {
13145   static int xy[4][2] =
13146   {
13147     { 0, -1 },
13148     { -1, 0 },
13149     { +1, 0 },
13150     { 0, +1 }
13151   };
13152   static int trigger_sides[4][2] =
13153   {
13154     // center side       border side
13155     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13156     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13157     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13158     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13159   };
13160   static int touch_dir[4] =
13161   {
13162     MV_LEFT | MV_RIGHT,
13163     MV_UP   | MV_DOWN,
13164     MV_UP   | MV_DOWN,
13165     MV_LEFT | MV_RIGHT
13166   };
13167   int center_element = Tile[x][y];      // should always be non-moving!
13168   int i;
13169
13170   for (i = 0; i < NUM_DIRECTIONS; i++)
13171   {
13172     int xx = x + xy[i][0];
13173     int yy = y + xy[i][1];
13174     int center_side = trigger_sides[i][0];
13175     int border_side = trigger_sides[i][1];
13176     int border_element;
13177
13178     if (!IN_LEV_FIELD(xx, yy))
13179       continue;
13180
13181     if (IS_PLAYER(x, y))                // player found at center element
13182     {
13183       struct PlayerInfo *player = PLAYERINFO(x, y);
13184
13185       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13186         border_element = Tile[xx][yy];          // may be moving!
13187       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13188         border_element = Tile[xx][yy];
13189       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13190         border_element = MovingOrBlocked2Element(xx, yy);
13191       else
13192         continue;               // center and border element do not touch
13193
13194       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13195                                  player->index_bit, border_side);
13196       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13197                                           CE_PLAYER_TOUCHES_X,
13198                                           player->index_bit, border_side);
13199
13200       {
13201         /* use player element that is initially defined in the level playfield,
13202            not the player element that corresponds to the runtime player number
13203            (example: a level that contains EL_PLAYER_3 as the only player would
13204            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13205         int player_element = PLAYERINFO(x, y)->initial_element;
13206
13207         CheckElementChangeBySide(xx, yy, border_element, player_element,
13208                                  CE_TOUCHING_X, border_side);
13209       }
13210     }
13211     else if (IS_PLAYER(xx, yy))         // player found at border element
13212     {
13213       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13214
13215       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13216       {
13217         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13218           continue;             // center and border element do not touch
13219       }
13220
13221       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13222                                  player->index_bit, center_side);
13223       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13224                                           CE_PLAYER_TOUCHES_X,
13225                                           player->index_bit, center_side);
13226
13227       {
13228         /* use player element that is initially defined in the level playfield,
13229            not the player element that corresponds to the runtime player number
13230            (example: a level that contains EL_PLAYER_3 as the only player would
13231            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13232         int player_element = PLAYERINFO(xx, yy)->initial_element;
13233
13234         CheckElementChangeBySide(x, y, center_element, player_element,
13235                                  CE_TOUCHING_X, center_side);
13236       }
13237
13238       break;
13239     }
13240   }
13241 }
13242
13243 void TestIfElementTouchesCustomElement(int x, int y)
13244 {
13245   static int xy[4][2] =
13246   {
13247     { 0, -1 },
13248     { -1, 0 },
13249     { +1, 0 },
13250     { 0, +1 }
13251   };
13252   static int trigger_sides[4][2] =
13253   {
13254     // center side      border side
13255     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13256     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13257     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13258     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13259   };
13260   static int touch_dir[4] =
13261   {
13262     MV_LEFT | MV_RIGHT,
13263     MV_UP   | MV_DOWN,
13264     MV_UP   | MV_DOWN,
13265     MV_LEFT | MV_RIGHT
13266   };
13267   boolean change_center_element = FALSE;
13268   int center_element = Tile[x][y];      // should always be non-moving!
13269   int border_element_old[NUM_DIRECTIONS];
13270   int i;
13271
13272   for (i = 0; i < NUM_DIRECTIONS; i++)
13273   {
13274     int xx = x + xy[i][0];
13275     int yy = y + xy[i][1];
13276     int border_element;
13277
13278     border_element_old[i] = -1;
13279
13280     if (!IN_LEV_FIELD(xx, yy))
13281       continue;
13282
13283     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13284       border_element = Tile[xx][yy];    // may be moving!
13285     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13286       border_element = Tile[xx][yy];
13287     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13288       border_element = MovingOrBlocked2Element(xx, yy);
13289     else
13290       continue;                 // center and border element do not touch
13291
13292     border_element_old[i] = border_element;
13293   }
13294
13295   for (i = 0; i < NUM_DIRECTIONS; i++)
13296   {
13297     int xx = x + xy[i][0];
13298     int yy = y + xy[i][1];
13299     int center_side = trigger_sides[i][0];
13300     int border_element = border_element_old[i];
13301
13302     if (border_element == -1)
13303       continue;
13304
13305     // check for change of border element
13306     CheckElementChangeBySide(xx, yy, border_element, center_element,
13307                              CE_TOUCHING_X, center_side);
13308
13309     // (center element cannot be player, so we dont have to check this here)
13310   }
13311
13312   for (i = 0; i < NUM_DIRECTIONS; i++)
13313   {
13314     int xx = x + xy[i][0];
13315     int yy = y + xy[i][1];
13316     int border_side = trigger_sides[i][1];
13317     int border_element = border_element_old[i];
13318
13319     if (border_element == -1)
13320       continue;
13321
13322     // check for change of center element (but change it only once)
13323     if (!change_center_element)
13324       change_center_element =
13325         CheckElementChangeBySide(x, y, center_element, border_element,
13326                                  CE_TOUCHING_X, border_side);
13327
13328     if (IS_PLAYER(xx, yy))
13329     {
13330       /* use player element that is initially defined in the level playfield,
13331          not the player element that corresponds to the runtime player number
13332          (example: a level that contains EL_PLAYER_3 as the only player would
13333          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13334       int player_element = PLAYERINFO(xx, yy)->initial_element;
13335
13336       CheckElementChangeBySide(x, y, center_element, player_element,
13337                                CE_TOUCHING_X, border_side);
13338     }
13339   }
13340 }
13341
13342 void TestIfElementHitsCustomElement(int x, int y, int direction)
13343 {
13344   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13345   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13346   int hitx = x + dx, hity = y + dy;
13347   int hitting_element = Tile[x][y];
13348   int touched_element;
13349
13350   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13351     return;
13352
13353   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13354                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13355
13356   if (IN_LEV_FIELD(hitx, hity))
13357   {
13358     int opposite_direction = MV_DIR_OPPOSITE(direction);
13359     int hitting_side = direction;
13360     int touched_side = opposite_direction;
13361     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13362                           MovDir[hitx][hity] != direction ||
13363                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13364
13365     object_hit = TRUE;
13366
13367     if (object_hit)
13368     {
13369       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13370                                CE_HITTING_X, touched_side);
13371
13372       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13373                                CE_HIT_BY_X, hitting_side);
13374
13375       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13376                                CE_HIT_BY_SOMETHING, opposite_direction);
13377
13378       if (IS_PLAYER(hitx, hity))
13379       {
13380         /* use player element that is initially defined in the level playfield,
13381            not the player element that corresponds to the runtime player number
13382            (example: a level that contains EL_PLAYER_3 as the only player would
13383            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13384         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13385
13386         CheckElementChangeBySide(x, y, hitting_element, player_element,
13387                                  CE_HITTING_X, touched_side);
13388       }
13389     }
13390   }
13391
13392   // "hitting something" is also true when hitting the playfield border
13393   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13394                            CE_HITTING_SOMETHING, direction);
13395 }
13396
13397 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13398 {
13399   int i, kill_x = -1, kill_y = -1;
13400
13401   int bad_element = -1;
13402   static int test_xy[4][2] =
13403   {
13404     { 0, -1 },
13405     { -1, 0 },
13406     { +1, 0 },
13407     { 0, +1 }
13408   };
13409   static int test_dir[4] =
13410   {
13411     MV_UP,
13412     MV_LEFT,
13413     MV_RIGHT,
13414     MV_DOWN
13415   };
13416
13417   for (i = 0; i < NUM_DIRECTIONS; i++)
13418   {
13419     int test_x, test_y, test_move_dir, test_element;
13420
13421     test_x = good_x + test_xy[i][0];
13422     test_y = good_y + test_xy[i][1];
13423
13424     if (!IN_LEV_FIELD(test_x, test_y))
13425       continue;
13426
13427     test_move_dir =
13428       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13429
13430     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13431
13432     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13433        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13434     */
13435     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13436         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13437     {
13438       kill_x = test_x;
13439       kill_y = test_y;
13440       bad_element = test_element;
13441
13442       break;
13443     }
13444   }
13445
13446   if (kill_x != -1 || kill_y != -1)
13447   {
13448     if (IS_PLAYER(good_x, good_y))
13449     {
13450       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13451
13452       if (player->shield_deadly_time_left > 0 &&
13453           !IS_INDESTRUCTIBLE(bad_element))
13454         Bang(kill_x, kill_y);
13455       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13456         KillPlayer(player);
13457     }
13458     else
13459       Bang(good_x, good_y);
13460   }
13461 }
13462
13463 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13464 {
13465   int i, kill_x = -1, kill_y = -1;
13466   int bad_element = Tile[bad_x][bad_y];
13467   static int test_xy[4][2] =
13468   {
13469     { 0, -1 },
13470     { -1, 0 },
13471     { +1, 0 },
13472     { 0, +1 }
13473   };
13474   static int touch_dir[4] =
13475   {
13476     MV_LEFT | MV_RIGHT,
13477     MV_UP   | MV_DOWN,
13478     MV_UP   | MV_DOWN,
13479     MV_LEFT | MV_RIGHT
13480   };
13481   static int test_dir[4] =
13482   {
13483     MV_UP,
13484     MV_LEFT,
13485     MV_RIGHT,
13486     MV_DOWN
13487   };
13488
13489   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13490     return;
13491
13492   for (i = 0; i < NUM_DIRECTIONS; i++)
13493   {
13494     int test_x, test_y, test_move_dir, test_element;
13495
13496     test_x = bad_x + test_xy[i][0];
13497     test_y = bad_y + test_xy[i][1];
13498
13499     if (!IN_LEV_FIELD(test_x, test_y))
13500       continue;
13501
13502     test_move_dir =
13503       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13504
13505     test_element = Tile[test_x][test_y];
13506
13507     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13508        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13509     */
13510     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13511         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13512     {
13513       // good thing is player or penguin that does not move away
13514       if (IS_PLAYER(test_x, test_y))
13515       {
13516         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13517
13518         if (bad_element == EL_ROBOT && player->is_moving)
13519           continue;     // robot does not kill player if he is moving
13520
13521         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13522         {
13523           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13524             continue;           // center and border element do not touch
13525         }
13526
13527         kill_x = test_x;
13528         kill_y = test_y;
13529
13530         break;
13531       }
13532       else if (test_element == EL_PENGUIN)
13533       {
13534         kill_x = test_x;
13535         kill_y = test_y;
13536
13537         break;
13538       }
13539     }
13540   }
13541
13542   if (kill_x != -1 || kill_y != -1)
13543   {
13544     if (IS_PLAYER(kill_x, kill_y))
13545     {
13546       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13547
13548       if (player->shield_deadly_time_left > 0 &&
13549           !IS_INDESTRUCTIBLE(bad_element))
13550         Bang(bad_x, bad_y);
13551       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13552         KillPlayer(player);
13553     }
13554     else
13555       Bang(kill_x, kill_y);
13556   }
13557 }
13558
13559 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13560 {
13561   int bad_element = Tile[bad_x][bad_y];
13562   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13563   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13564   int test_x = bad_x + dx, test_y = bad_y + dy;
13565   int test_move_dir, test_element;
13566   int kill_x = -1, kill_y = -1;
13567
13568   if (!IN_LEV_FIELD(test_x, test_y))
13569     return;
13570
13571   test_move_dir =
13572     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13573
13574   test_element = Tile[test_x][test_y];
13575
13576   if (test_move_dir != bad_move_dir)
13577   {
13578     // good thing can be player or penguin that does not move away
13579     if (IS_PLAYER(test_x, test_y))
13580     {
13581       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13582
13583       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13584          player as being hit when he is moving towards the bad thing, because
13585          the "get hit by" condition would be lost after the player stops) */
13586       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13587         return;         // player moves away from bad thing
13588
13589       kill_x = test_x;
13590       kill_y = test_y;
13591     }
13592     else if (test_element == EL_PENGUIN)
13593     {
13594       kill_x = test_x;
13595       kill_y = test_y;
13596     }
13597   }
13598
13599   if (kill_x != -1 || kill_y != -1)
13600   {
13601     if (IS_PLAYER(kill_x, kill_y))
13602     {
13603       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13604
13605       if (player->shield_deadly_time_left > 0 &&
13606           !IS_INDESTRUCTIBLE(bad_element))
13607         Bang(bad_x, bad_y);
13608       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13609         KillPlayer(player);
13610     }
13611     else
13612       Bang(kill_x, kill_y);
13613   }
13614 }
13615
13616 void TestIfPlayerTouchesBadThing(int x, int y)
13617 {
13618   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13619 }
13620
13621 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13622 {
13623   TestIfGoodThingHitsBadThing(x, y, move_dir);
13624 }
13625
13626 void TestIfBadThingTouchesPlayer(int x, int y)
13627 {
13628   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13629 }
13630
13631 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13632 {
13633   TestIfBadThingHitsGoodThing(x, y, move_dir);
13634 }
13635
13636 void TestIfFriendTouchesBadThing(int x, int y)
13637 {
13638   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13639 }
13640
13641 void TestIfBadThingTouchesFriend(int x, int y)
13642 {
13643   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13644 }
13645
13646 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13647 {
13648   int i, kill_x = bad_x, kill_y = bad_y;
13649   static int xy[4][2] =
13650   {
13651     { 0, -1 },
13652     { -1, 0 },
13653     { +1, 0 },
13654     { 0, +1 }
13655   };
13656
13657   for (i = 0; i < NUM_DIRECTIONS; i++)
13658   {
13659     int x, y, element;
13660
13661     x = bad_x + xy[i][0];
13662     y = bad_y + xy[i][1];
13663     if (!IN_LEV_FIELD(x, y))
13664       continue;
13665
13666     element = Tile[x][y];
13667     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13668         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13669     {
13670       kill_x = x;
13671       kill_y = y;
13672       break;
13673     }
13674   }
13675
13676   if (kill_x != bad_x || kill_y != bad_y)
13677     Bang(bad_x, bad_y);
13678 }
13679
13680 void KillPlayer(struct PlayerInfo *player)
13681 {
13682   int jx = player->jx, jy = player->jy;
13683
13684   if (!player->active)
13685     return;
13686
13687 #if 0
13688   Debug("game:playing:KillPlayer",
13689         "0: killed == %d, active == %d, reanimated == %d",
13690         player->killed, player->active, player->reanimated);
13691 #endif
13692
13693   /* the following code was introduced to prevent an infinite loop when calling
13694      -> Bang()
13695      -> CheckTriggeredElementChangeExt()
13696      -> ExecuteCustomElementAction()
13697      -> KillPlayer()
13698      -> (infinitely repeating the above sequence of function calls)
13699      which occurs when killing the player while having a CE with the setting
13700      "kill player X when explosion of <player X>"; the solution using a new
13701      field "player->killed" was chosen for backwards compatibility, although
13702      clever use of the fields "player->active" etc. would probably also work */
13703 #if 1
13704   if (player->killed)
13705     return;
13706 #endif
13707
13708   player->killed = TRUE;
13709
13710   // remove accessible field at the player's position
13711   Tile[jx][jy] = EL_EMPTY;
13712
13713   // deactivate shield (else Bang()/Explode() would not work right)
13714   player->shield_normal_time_left = 0;
13715   player->shield_deadly_time_left = 0;
13716
13717 #if 0
13718   Debug("game:playing:KillPlayer",
13719         "1: killed == %d, active == %d, reanimated == %d",
13720         player->killed, player->active, player->reanimated);
13721 #endif
13722
13723   Bang(jx, jy);
13724
13725 #if 0
13726   Debug("game:playing:KillPlayer",
13727         "2: killed == %d, active == %d, reanimated == %d",
13728         player->killed, player->active, player->reanimated);
13729 #endif
13730
13731   if (player->reanimated)       // killed player may have been reanimated
13732     player->killed = player->reanimated = FALSE;
13733   else
13734     BuryPlayer(player);
13735 }
13736
13737 static void KillPlayerUnlessEnemyProtected(int x, int y)
13738 {
13739   if (!PLAYER_ENEMY_PROTECTED(x, y))
13740     KillPlayer(PLAYERINFO(x, y));
13741 }
13742
13743 static void KillPlayerUnlessExplosionProtected(int x, int y)
13744 {
13745   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13746     KillPlayer(PLAYERINFO(x, y));
13747 }
13748
13749 void BuryPlayer(struct PlayerInfo *player)
13750 {
13751   int jx = player->jx, jy = player->jy;
13752
13753   if (!player->active)
13754     return;
13755
13756   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13757   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13758
13759   RemovePlayer(player);
13760
13761   player->buried = TRUE;
13762
13763   if (game.all_players_gone)
13764     game.GameOver = TRUE;
13765 }
13766
13767 void RemovePlayer(struct PlayerInfo *player)
13768 {
13769   int jx = player->jx, jy = player->jy;
13770   int i, found = FALSE;
13771
13772   player->present = FALSE;
13773   player->active = FALSE;
13774
13775   // required for some CE actions (even if the player is not active anymore)
13776   player->MovPos = 0;
13777
13778   if (!ExplodeField[jx][jy])
13779     StorePlayer[jx][jy] = 0;
13780
13781   if (player->is_moving)
13782     TEST_DrawLevelField(player->last_jx, player->last_jy);
13783
13784   for (i = 0; i < MAX_PLAYERS; i++)
13785     if (stored_player[i].active)
13786       found = TRUE;
13787
13788   if (!found)
13789   {
13790     game.all_players_gone = TRUE;
13791     game.GameOver = TRUE;
13792   }
13793
13794   game.exit_x = game.robot_wheel_x = jx;
13795   game.exit_y = game.robot_wheel_y = jy;
13796 }
13797
13798 void ExitPlayer(struct PlayerInfo *player)
13799 {
13800   DrawPlayer(player);   // needed here only to cleanup last field
13801   RemovePlayer(player);
13802
13803   if (game.players_still_needed > 0)
13804     game.players_still_needed--;
13805 }
13806
13807 static void SetFieldForSnapping(int x, int y, int element, int direction,
13808                                 int player_index_bit)
13809 {
13810   struct ElementInfo *ei = &element_info[element];
13811   int direction_bit = MV_DIR_TO_BIT(direction);
13812   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13813   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13814                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13815
13816   Tile[x][y] = EL_ELEMENT_SNAPPING;
13817   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13818   MovDir[x][y] = direction;
13819   Store[x][y] = element;
13820   Store2[x][y] = player_index_bit;
13821
13822   ResetGfxAnimation(x, y);
13823
13824   GfxElement[x][y] = element;
13825   GfxAction[x][y] = action;
13826   GfxDir[x][y] = direction;
13827   GfxFrame[x][y] = -1;
13828 }
13829
13830 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13831                                    int player_index_bit)
13832 {
13833   TestIfElementTouchesCustomElement(x, y);      // for empty space
13834
13835   if (level.finish_dig_collect)
13836   {
13837     int dig_side = MV_DIR_OPPOSITE(direction);
13838
13839     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13840                                         player_index_bit, dig_side);
13841   }
13842 }
13843
13844 /*
13845   =============================================================================
13846   checkDiagonalPushing()
13847   -----------------------------------------------------------------------------
13848   check if diagonal input device direction results in pushing of object
13849   (by checking if the alternative direction is walkable, diggable, ...)
13850   =============================================================================
13851 */
13852
13853 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13854                                     int x, int y, int real_dx, int real_dy)
13855 {
13856   int jx, jy, dx, dy, xx, yy;
13857
13858   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13859     return TRUE;
13860
13861   // diagonal direction: check alternative direction
13862   jx = player->jx;
13863   jy = player->jy;
13864   dx = x - jx;
13865   dy = y - jy;
13866   xx = jx + (dx == 0 ? real_dx : 0);
13867   yy = jy + (dy == 0 ? real_dy : 0);
13868
13869   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13870 }
13871
13872 /*
13873   =============================================================================
13874   DigField()
13875   -----------------------------------------------------------------------------
13876   x, y:                 field next to player (non-diagonal) to try to dig to
13877   real_dx, real_dy:     direction as read from input device (can be diagonal)
13878   =============================================================================
13879 */
13880
13881 static int DigField(struct PlayerInfo *player,
13882                     int oldx, int oldy, int x, int y,
13883                     int real_dx, int real_dy, int mode)
13884 {
13885   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13886   boolean player_was_pushing = player->is_pushing;
13887   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13888   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13889   int jx = oldx, jy = oldy;
13890   int dx = x - jx, dy = y - jy;
13891   int nextx = x + dx, nexty = y + dy;
13892   int move_direction = (dx == -1 ? MV_LEFT  :
13893                         dx == +1 ? MV_RIGHT :
13894                         dy == -1 ? MV_UP    :
13895                         dy == +1 ? MV_DOWN  : MV_NONE);
13896   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13897   int dig_side = MV_DIR_OPPOSITE(move_direction);
13898   int old_element = Tile[jx][jy];
13899   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13900   int collect_count;
13901
13902   if (is_player)                // function can also be called by EL_PENGUIN
13903   {
13904     if (player->MovPos == 0)
13905     {
13906       player->is_digging = FALSE;
13907       player->is_collecting = FALSE;
13908     }
13909
13910     if (player->MovPos == 0)    // last pushing move finished
13911       player->is_pushing = FALSE;
13912
13913     if (mode == DF_NO_PUSH)     // player just stopped pushing
13914     {
13915       player->is_switching = FALSE;
13916       player->push_delay = -1;
13917
13918       return MP_NO_ACTION;
13919     }
13920   }
13921
13922   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13923     old_element = Back[jx][jy];
13924
13925   // in case of element dropped at player position, check background
13926   else if (Back[jx][jy] != EL_EMPTY &&
13927            game.engine_version >= VERSION_IDENT(2,2,0,0))
13928     old_element = Back[jx][jy];
13929
13930   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13931     return MP_NO_ACTION;        // field has no opening in this direction
13932
13933   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13934     return MP_NO_ACTION;        // field has no opening in this direction
13935
13936   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13937   {
13938     SplashAcid(x, y);
13939
13940     Tile[jx][jy] = player->artwork_element;
13941     InitMovingField(jx, jy, MV_DOWN);
13942     Store[jx][jy] = EL_ACID;
13943     ContinueMoving(jx, jy);
13944     BuryPlayer(player);
13945
13946     return MP_DONT_RUN_INTO;
13947   }
13948
13949   if (player_can_move && DONT_RUN_INTO(element))
13950   {
13951     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13952
13953     return MP_DONT_RUN_INTO;
13954   }
13955
13956   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13957     return MP_NO_ACTION;
13958
13959   collect_count = element_info[element].collect_count_initial;
13960
13961   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13962     return MP_NO_ACTION;
13963
13964   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13965     player_can_move = player_can_move_or_snap;
13966
13967   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13968       game.engine_version >= VERSION_IDENT(2,2,0,0))
13969   {
13970     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13971                                player->index_bit, dig_side);
13972     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13973                                         player->index_bit, dig_side);
13974
13975     if (element == EL_DC_LANDMINE)
13976       Bang(x, y);
13977
13978     if (Tile[x][y] != element)          // field changed by snapping
13979       return MP_ACTION;
13980
13981     return MP_NO_ACTION;
13982   }
13983
13984   if (player->gravity && is_player && !player->is_auto_moving &&
13985       canFallDown(player) && move_direction != MV_DOWN &&
13986       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13987     return MP_NO_ACTION;        // player cannot walk here due to gravity
13988
13989   if (player_can_move &&
13990       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13991   {
13992     int sound_element = SND_ELEMENT(element);
13993     int sound_action = ACTION_WALKING;
13994
13995     if (IS_RND_GATE(element))
13996     {
13997       if (!player->key[RND_GATE_NR(element)])
13998         return MP_NO_ACTION;
13999     }
14000     else if (IS_RND_GATE_GRAY(element))
14001     {
14002       if (!player->key[RND_GATE_GRAY_NR(element)])
14003         return MP_NO_ACTION;
14004     }
14005     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14006     {
14007       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14008         return MP_NO_ACTION;
14009     }
14010     else if (element == EL_EXIT_OPEN ||
14011              element == EL_EM_EXIT_OPEN ||
14012              element == EL_EM_EXIT_OPENING ||
14013              element == EL_STEEL_EXIT_OPEN ||
14014              element == EL_EM_STEEL_EXIT_OPEN ||
14015              element == EL_EM_STEEL_EXIT_OPENING ||
14016              element == EL_SP_EXIT_OPEN ||
14017              element == EL_SP_EXIT_OPENING)
14018     {
14019       sound_action = ACTION_PASSING;    // player is passing exit
14020     }
14021     else if (element == EL_EMPTY)
14022     {
14023       sound_action = ACTION_MOVING;             // nothing to walk on
14024     }
14025
14026     // play sound from background or player, whatever is available
14027     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14028       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14029     else
14030       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14031   }
14032   else if (player_can_move &&
14033            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14034   {
14035     if (!ACCESS_FROM(element, opposite_direction))
14036       return MP_NO_ACTION;      // field not accessible from this direction
14037
14038     if (CAN_MOVE(element))      // only fixed elements can be passed!
14039       return MP_NO_ACTION;
14040
14041     if (IS_EM_GATE(element))
14042     {
14043       if (!player->key[EM_GATE_NR(element)])
14044         return MP_NO_ACTION;
14045     }
14046     else if (IS_EM_GATE_GRAY(element))
14047     {
14048       if (!player->key[EM_GATE_GRAY_NR(element)])
14049         return MP_NO_ACTION;
14050     }
14051     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14052     {
14053       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14054         return MP_NO_ACTION;
14055     }
14056     else if (IS_EMC_GATE(element))
14057     {
14058       if (!player->key[EMC_GATE_NR(element)])
14059         return MP_NO_ACTION;
14060     }
14061     else if (IS_EMC_GATE_GRAY(element))
14062     {
14063       if (!player->key[EMC_GATE_GRAY_NR(element)])
14064         return MP_NO_ACTION;
14065     }
14066     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14067     {
14068       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14069         return MP_NO_ACTION;
14070     }
14071     else if (element == EL_DC_GATE_WHITE ||
14072              element == EL_DC_GATE_WHITE_GRAY ||
14073              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14074     {
14075       if (player->num_white_keys == 0)
14076         return MP_NO_ACTION;
14077
14078       player->num_white_keys--;
14079     }
14080     else if (IS_SP_PORT(element))
14081     {
14082       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14083           element == EL_SP_GRAVITY_PORT_RIGHT ||
14084           element == EL_SP_GRAVITY_PORT_UP ||
14085           element == EL_SP_GRAVITY_PORT_DOWN)
14086         player->gravity = !player->gravity;
14087       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14088                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14089                element == EL_SP_GRAVITY_ON_PORT_UP ||
14090                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14091         player->gravity = TRUE;
14092       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14093                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14094                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14095                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14096         player->gravity = FALSE;
14097     }
14098
14099     // automatically move to the next field with double speed
14100     player->programmed_action = move_direction;
14101
14102     if (player->move_delay_reset_counter == 0)
14103     {
14104       player->move_delay_reset_counter = 2;     // two double speed steps
14105
14106       DOUBLE_PLAYER_SPEED(player);
14107     }
14108
14109     PlayLevelSoundAction(x, y, ACTION_PASSING);
14110   }
14111   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14112   {
14113     RemoveField(x, y);
14114
14115     if (mode != DF_SNAP)
14116     {
14117       GfxElement[x][y] = GFX_ELEMENT(element);
14118       player->is_digging = TRUE;
14119     }
14120
14121     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14122
14123     // use old behaviour for old levels (digging)
14124     if (!level.finish_dig_collect)
14125     {
14126       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14127                                           player->index_bit, dig_side);
14128
14129       // if digging triggered player relocation, finish digging tile
14130       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14131         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14132     }
14133
14134     if (mode == DF_SNAP)
14135     {
14136       if (level.block_snap_field)
14137         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14138       else
14139         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14140
14141       // use old behaviour for old levels (snapping)
14142       if (!level.finish_dig_collect)
14143         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14144                                             player->index_bit, dig_side);
14145     }
14146   }
14147   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14148   {
14149     RemoveField(x, y);
14150
14151     if (is_player && mode != DF_SNAP)
14152     {
14153       GfxElement[x][y] = element;
14154       player->is_collecting = TRUE;
14155     }
14156
14157     if (element == EL_SPEED_PILL)
14158     {
14159       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14160     }
14161     else if (element == EL_EXTRA_TIME && level.time > 0)
14162     {
14163       TimeLeft += level.extra_time;
14164
14165       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14166
14167       DisplayGameControlValues();
14168     }
14169     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14170     {
14171       player->shield_normal_time_left += level.shield_normal_time;
14172       if (element == EL_SHIELD_DEADLY)
14173         player->shield_deadly_time_left += level.shield_deadly_time;
14174     }
14175     else if (element == EL_DYNAMITE ||
14176              element == EL_EM_DYNAMITE ||
14177              element == EL_SP_DISK_RED)
14178     {
14179       if (player->inventory_size < MAX_INVENTORY_SIZE)
14180         player->inventory_element[player->inventory_size++] = element;
14181
14182       DrawGameDoorValues();
14183     }
14184     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14185     {
14186       player->dynabomb_count++;
14187       player->dynabombs_left++;
14188     }
14189     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14190     {
14191       player->dynabomb_size++;
14192     }
14193     else if (element == EL_DYNABOMB_INCREASE_POWER)
14194     {
14195       player->dynabomb_xl = TRUE;
14196     }
14197     else if (IS_KEY(element))
14198     {
14199       player->key[KEY_NR(element)] = TRUE;
14200
14201       DrawGameDoorValues();
14202     }
14203     else if (element == EL_DC_KEY_WHITE)
14204     {
14205       player->num_white_keys++;
14206
14207       // display white keys?
14208       // DrawGameDoorValues();
14209     }
14210     else if (IS_ENVELOPE(element))
14211     {
14212       player->show_envelope = element;
14213     }
14214     else if (element == EL_EMC_LENSES)
14215     {
14216       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14217
14218       RedrawAllInvisibleElementsForLenses();
14219     }
14220     else if (element == EL_EMC_MAGNIFIER)
14221     {
14222       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14223
14224       RedrawAllInvisibleElementsForMagnifier();
14225     }
14226     else if (IS_DROPPABLE(element) ||
14227              IS_THROWABLE(element))     // can be collected and dropped
14228     {
14229       int i;
14230
14231       if (collect_count == 0)
14232         player->inventory_infinite_element = element;
14233       else
14234         for (i = 0; i < collect_count; i++)
14235           if (player->inventory_size < MAX_INVENTORY_SIZE)
14236             player->inventory_element[player->inventory_size++] = element;
14237
14238       DrawGameDoorValues();
14239     }
14240     else if (collect_count > 0)
14241     {
14242       game.gems_still_needed -= collect_count;
14243       if (game.gems_still_needed < 0)
14244         game.gems_still_needed = 0;
14245
14246       game.snapshot.collected_item = TRUE;
14247
14248       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14249
14250       DisplayGameControlValues();
14251     }
14252
14253     RaiseScoreElement(element);
14254     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14255
14256     // use old behaviour for old levels (collecting)
14257     if (!level.finish_dig_collect && is_player)
14258     {
14259       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14260                                           player->index_bit, dig_side);
14261
14262       // if collecting triggered player relocation, finish collecting tile
14263       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14264         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14265     }
14266
14267     if (mode == DF_SNAP)
14268     {
14269       if (level.block_snap_field)
14270         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14271       else
14272         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14273
14274       // use old behaviour for old levels (snapping)
14275       if (!level.finish_dig_collect)
14276         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14277                                             player->index_bit, dig_side);
14278     }
14279   }
14280   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14281   {
14282     if (mode == DF_SNAP && element != EL_BD_ROCK)
14283       return MP_NO_ACTION;
14284
14285     if (CAN_FALL(element) && dy)
14286       return MP_NO_ACTION;
14287
14288     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14289         !(element == EL_SPRING && level.use_spring_bug))
14290       return MP_NO_ACTION;
14291
14292     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14293         ((move_direction & MV_VERTICAL &&
14294           ((element_info[element].move_pattern & MV_LEFT &&
14295             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14296            (element_info[element].move_pattern & MV_RIGHT &&
14297             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14298          (move_direction & MV_HORIZONTAL &&
14299           ((element_info[element].move_pattern & MV_UP &&
14300             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14301            (element_info[element].move_pattern & MV_DOWN &&
14302             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14303       return MP_NO_ACTION;
14304
14305     // do not push elements already moving away faster than player
14306     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14307         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14308       return MP_NO_ACTION;
14309
14310     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14311     {
14312       if (player->push_delay_value == -1 || !player_was_pushing)
14313         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14314     }
14315     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14316     {
14317       if (player->push_delay_value == -1)
14318         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14319     }
14320     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14321     {
14322       if (!player->is_pushing)
14323         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14324     }
14325
14326     player->is_pushing = TRUE;
14327     player->is_active = TRUE;
14328
14329     if (!(IN_LEV_FIELD(nextx, nexty) &&
14330           (IS_FREE(nextx, nexty) ||
14331            (IS_SB_ELEMENT(element) &&
14332             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14333            (IS_CUSTOM_ELEMENT(element) &&
14334             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14335       return MP_NO_ACTION;
14336
14337     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14338       return MP_NO_ACTION;
14339
14340     if (player->push_delay == -1)       // new pushing; restart delay
14341       player->push_delay = 0;
14342
14343     if (player->push_delay < player->push_delay_value &&
14344         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14345         element != EL_SPRING && element != EL_BALLOON)
14346     {
14347       // make sure that there is no move delay before next try to push
14348       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14349         player->move_delay = 0;
14350
14351       return MP_NO_ACTION;
14352     }
14353
14354     if (IS_CUSTOM_ELEMENT(element) &&
14355         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14356     {
14357       if (!DigFieldByCE(nextx, nexty, element))
14358         return MP_NO_ACTION;
14359     }
14360
14361     if (IS_SB_ELEMENT(element))
14362     {
14363       boolean sokoban_task_solved = FALSE;
14364
14365       if (element == EL_SOKOBAN_FIELD_FULL)
14366       {
14367         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14368
14369         IncrementSokobanFieldsNeeded();
14370         IncrementSokobanObjectsNeeded();
14371       }
14372
14373       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14374       {
14375         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14376
14377         DecrementSokobanFieldsNeeded();
14378         DecrementSokobanObjectsNeeded();
14379
14380         // sokoban object was pushed from empty field to sokoban field
14381         if (Back[x][y] == EL_EMPTY)
14382           sokoban_task_solved = TRUE;
14383       }
14384
14385       Tile[x][y] = EL_SOKOBAN_OBJECT;
14386
14387       if (Back[x][y] == Back[nextx][nexty])
14388         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14389       else if (Back[x][y] != 0)
14390         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14391                                     ACTION_EMPTYING);
14392       else
14393         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14394                                     ACTION_FILLING);
14395
14396       if (sokoban_task_solved &&
14397           game.sokoban_fields_still_needed == 0 &&
14398           game.sokoban_objects_still_needed == 0 &&
14399           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14400       {
14401         game.players_still_needed = 0;
14402
14403         LevelSolved();
14404
14405         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14406       }
14407     }
14408     else
14409       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14410
14411     InitMovingField(x, y, move_direction);
14412     GfxAction[x][y] = ACTION_PUSHING;
14413
14414     if (mode == DF_SNAP)
14415       ContinueMoving(x, y);
14416     else
14417       MovPos[x][y] = (dx != 0 ? dx : dy);
14418
14419     Pushed[x][y] = TRUE;
14420     Pushed[nextx][nexty] = TRUE;
14421
14422     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14423       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14424     else
14425       player->push_delay_value = -1;    // get new value later
14426
14427     // check for element change _after_ element has been pushed
14428     if (game.use_change_when_pushing_bug)
14429     {
14430       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14431                                  player->index_bit, dig_side);
14432       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14433                                           player->index_bit, dig_side);
14434     }
14435   }
14436   else if (IS_SWITCHABLE(element))
14437   {
14438     if (PLAYER_SWITCHING(player, x, y))
14439     {
14440       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14441                                           player->index_bit, dig_side);
14442
14443       return MP_ACTION;
14444     }
14445
14446     player->is_switching = TRUE;
14447     player->switch_x = x;
14448     player->switch_y = y;
14449
14450     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14451
14452     if (element == EL_ROBOT_WHEEL)
14453     {
14454       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14455
14456       game.robot_wheel_x = x;
14457       game.robot_wheel_y = y;
14458       game.robot_wheel_active = TRUE;
14459
14460       TEST_DrawLevelField(x, y);
14461     }
14462     else if (element == EL_SP_TERMINAL)
14463     {
14464       int xx, yy;
14465
14466       SCAN_PLAYFIELD(xx, yy)
14467       {
14468         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14469         {
14470           Bang(xx, yy);
14471         }
14472         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14473         {
14474           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14475
14476           ResetGfxAnimation(xx, yy);
14477           TEST_DrawLevelField(xx, yy);
14478         }
14479       }
14480     }
14481     else if (IS_BELT_SWITCH(element))
14482     {
14483       ToggleBeltSwitch(x, y);
14484     }
14485     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14486              element == EL_SWITCHGATE_SWITCH_DOWN ||
14487              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14488              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14489     {
14490       ToggleSwitchgateSwitch(x, y);
14491     }
14492     else if (element == EL_LIGHT_SWITCH ||
14493              element == EL_LIGHT_SWITCH_ACTIVE)
14494     {
14495       ToggleLightSwitch(x, y);
14496     }
14497     else if (element == EL_TIMEGATE_SWITCH ||
14498              element == EL_DC_TIMEGATE_SWITCH)
14499     {
14500       ActivateTimegateSwitch(x, y);
14501     }
14502     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14503              element == EL_BALLOON_SWITCH_RIGHT ||
14504              element == EL_BALLOON_SWITCH_UP    ||
14505              element == EL_BALLOON_SWITCH_DOWN  ||
14506              element == EL_BALLOON_SWITCH_NONE  ||
14507              element == EL_BALLOON_SWITCH_ANY)
14508     {
14509       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14510                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14511                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14512                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14513                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14514                              move_direction);
14515     }
14516     else if (element == EL_LAMP)
14517     {
14518       Tile[x][y] = EL_LAMP_ACTIVE;
14519       game.lights_still_needed--;
14520
14521       ResetGfxAnimation(x, y);
14522       TEST_DrawLevelField(x, y);
14523     }
14524     else if (element == EL_TIME_ORB_FULL)
14525     {
14526       Tile[x][y] = EL_TIME_ORB_EMPTY;
14527
14528       if (level.time > 0 || level.use_time_orb_bug)
14529       {
14530         TimeLeft += level.time_orb_time;
14531         game.no_time_limit = FALSE;
14532
14533         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14534
14535         DisplayGameControlValues();
14536       }
14537
14538       ResetGfxAnimation(x, y);
14539       TEST_DrawLevelField(x, y);
14540     }
14541     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14542              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14543     {
14544       int xx, yy;
14545
14546       game.ball_active = !game.ball_active;
14547
14548       SCAN_PLAYFIELD(xx, yy)
14549       {
14550         int e = Tile[xx][yy];
14551
14552         if (game.ball_active)
14553         {
14554           if (e == EL_EMC_MAGIC_BALL)
14555             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14556           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14557             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14558         }
14559         else
14560         {
14561           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14562             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14563           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14564             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14565         }
14566       }
14567     }
14568
14569     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14570                                         player->index_bit, dig_side);
14571
14572     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14573                                         player->index_bit, dig_side);
14574
14575     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14576                                         player->index_bit, dig_side);
14577
14578     return MP_ACTION;
14579   }
14580   else
14581   {
14582     if (!PLAYER_SWITCHING(player, x, y))
14583     {
14584       player->is_switching = TRUE;
14585       player->switch_x = x;
14586       player->switch_y = y;
14587
14588       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14589                                  player->index_bit, dig_side);
14590       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14591                                           player->index_bit, dig_side);
14592
14593       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14594                                  player->index_bit, dig_side);
14595       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14596                                           player->index_bit, dig_side);
14597     }
14598
14599     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14600                                player->index_bit, dig_side);
14601     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14602                                         player->index_bit, dig_side);
14603
14604     return MP_NO_ACTION;
14605   }
14606
14607   player->push_delay = -1;
14608
14609   if (is_player)                // function can also be called by EL_PENGUIN
14610   {
14611     if (Tile[x][y] != element)          // really digged/collected something
14612     {
14613       player->is_collecting = !player->is_digging;
14614       player->is_active = TRUE;
14615
14616       player->last_removed_element = element;
14617     }
14618   }
14619
14620   return MP_MOVING;
14621 }
14622
14623 static boolean DigFieldByCE(int x, int y, int digging_element)
14624 {
14625   int element = Tile[x][y];
14626
14627   if (!IS_FREE(x, y))
14628   {
14629     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14630                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14631                   ACTION_BREAKING);
14632
14633     // no element can dig solid indestructible elements
14634     if (IS_INDESTRUCTIBLE(element) &&
14635         !IS_DIGGABLE(element) &&
14636         !IS_COLLECTIBLE(element))
14637       return FALSE;
14638
14639     if (AmoebaNr[x][y] &&
14640         (element == EL_AMOEBA_FULL ||
14641          element == EL_BD_AMOEBA ||
14642          element == EL_AMOEBA_GROWING))
14643     {
14644       AmoebaCnt[AmoebaNr[x][y]]--;
14645       AmoebaCnt2[AmoebaNr[x][y]]--;
14646     }
14647
14648     if (IS_MOVING(x, y))
14649       RemoveMovingField(x, y);
14650     else
14651     {
14652       RemoveField(x, y);
14653       TEST_DrawLevelField(x, y);
14654     }
14655
14656     // if digged element was about to explode, prevent the explosion
14657     ExplodeField[x][y] = EX_TYPE_NONE;
14658
14659     PlayLevelSoundAction(x, y, action);
14660   }
14661
14662   Store[x][y] = EL_EMPTY;
14663
14664   // this makes it possible to leave the removed element again
14665   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14666     Store[x][y] = element;
14667
14668   return TRUE;
14669 }
14670
14671 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14672 {
14673   int jx = player->jx, jy = player->jy;
14674   int x = jx + dx, y = jy + dy;
14675   int snap_direction = (dx == -1 ? MV_LEFT  :
14676                         dx == +1 ? MV_RIGHT :
14677                         dy == -1 ? MV_UP    :
14678                         dy == +1 ? MV_DOWN  : MV_NONE);
14679   boolean can_continue_snapping = (level.continuous_snapping &&
14680                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14681
14682   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14683     return FALSE;
14684
14685   if (!player->active || !IN_LEV_FIELD(x, y))
14686     return FALSE;
14687
14688   if (dx && dy)
14689     return FALSE;
14690
14691   if (!dx && !dy)
14692   {
14693     if (player->MovPos == 0)
14694       player->is_pushing = FALSE;
14695
14696     player->is_snapping = FALSE;
14697
14698     if (player->MovPos == 0)
14699     {
14700       player->is_moving = FALSE;
14701       player->is_digging = FALSE;
14702       player->is_collecting = FALSE;
14703     }
14704
14705     return FALSE;
14706   }
14707
14708   // prevent snapping with already pressed snap key when not allowed
14709   if (player->is_snapping && !can_continue_snapping)
14710     return FALSE;
14711
14712   player->MovDir = snap_direction;
14713
14714   if (player->MovPos == 0)
14715   {
14716     player->is_moving = FALSE;
14717     player->is_digging = FALSE;
14718     player->is_collecting = FALSE;
14719   }
14720
14721   player->is_dropping = FALSE;
14722   player->is_dropping_pressed = FALSE;
14723   player->drop_pressed_delay = 0;
14724
14725   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14726     return FALSE;
14727
14728   player->is_snapping = TRUE;
14729   player->is_active = TRUE;
14730
14731   if (player->MovPos == 0)
14732   {
14733     player->is_moving = FALSE;
14734     player->is_digging = FALSE;
14735     player->is_collecting = FALSE;
14736   }
14737
14738   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14739     TEST_DrawLevelField(player->last_jx, player->last_jy);
14740
14741   TEST_DrawLevelField(x, y);
14742
14743   return TRUE;
14744 }
14745
14746 static boolean DropElement(struct PlayerInfo *player)
14747 {
14748   int old_element, new_element;
14749   int dropx = player->jx, dropy = player->jy;
14750   int drop_direction = player->MovDir;
14751   int drop_side = drop_direction;
14752   int drop_element = get_next_dropped_element(player);
14753
14754   /* do not drop an element on top of another element; when holding drop key
14755      pressed without moving, dropped element must move away before the next
14756      element can be dropped (this is especially important if the next element
14757      is dynamite, which can be placed on background for historical reasons) */
14758   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14759     return MP_ACTION;
14760
14761   if (IS_THROWABLE(drop_element))
14762   {
14763     dropx += GET_DX_FROM_DIR(drop_direction);
14764     dropy += GET_DY_FROM_DIR(drop_direction);
14765
14766     if (!IN_LEV_FIELD(dropx, dropy))
14767       return FALSE;
14768   }
14769
14770   old_element = Tile[dropx][dropy];     // old element at dropping position
14771   new_element = drop_element;           // default: no change when dropping
14772
14773   // check if player is active, not moving and ready to drop
14774   if (!player->active || player->MovPos || player->drop_delay > 0)
14775     return FALSE;
14776
14777   // check if player has anything that can be dropped
14778   if (new_element == EL_UNDEFINED)
14779     return FALSE;
14780
14781   // only set if player has anything that can be dropped
14782   player->is_dropping_pressed = TRUE;
14783
14784   // check if drop key was pressed long enough for EM style dynamite
14785   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14786     return FALSE;
14787
14788   // check if anything can be dropped at the current position
14789   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14790     return FALSE;
14791
14792   // collected custom elements can only be dropped on empty fields
14793   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14794     return FALSE;
14795
14796   if (old_element != EL_EMPTY)
14797     Back[dropx][dropy] = old_element;   // store old element on this field
14798
14799   ResetGfxAnimation(dropx, dropy);
14800   ResetRandomAnimationValue(dropx, dropy);
14801
14802   if (player->inventory_size > 0 ||
14803       player->inventory_infinite_element != EL_UNDEFINED)
14804   {
14805     if (player->inventory_size > 0)
14806     {
14807       player->inventory_size--;
14808
14809       DrawGameDoorValues();
14810
14811       if (new_element == EL_DYNAMITE)
14812         new_element = EL_DYNAMITE_ACTIVE;
14813       else if (new_element == EL_EM_DYNAMITE)
14814         new_element = EL_EM_DYNAMITE_ACTIVE;
14815       else if (new_element == EL_SP_DISK_RED)
14816         new_element = EL_SP_DISK_RED_ACTIVE;
14817     }
14818
14819     Tile[dropx][dropy] = new_element;
14820
14821     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14822       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14823                           el2img(Tile[dropx][dropy]), 0);
14824
14825     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14826
14827     // needed if previous element just changed to "empty" in the last frame
14828     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14829
14830     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14831                                player->index_bit, drop_side);
14832     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14833                                         CE_PLAYER_DROPS_X,
14834                                         player->index_bit, drop_side);
14835
14836     TestIfElementTouchesCustomElement(dropx, dropy);
14837   }
14838   else          // player is dropping a dyna bomb
14839   {
14840     player->dynabombs_left--;
14841
14842     Tile[dropx][dropy] = new_element;
14843
14844     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14845       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14846                           el2img(Tile[dropx][dropy]), 0);
14847
14848     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14849   }
14850
14851   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14852     InitField_WithBug1(dropx, dropy, FALSE);
14853
14854   new_element = Tile[dropx][dropy];     // element might have changed
14855
14856   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14857       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14858   {
14859     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14860       MovDir[dropx][dropy] = drop_direction;
14861
14862     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14863
14864     // do not cause impact style collision by dropping elements that can fall
14865     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14866   }
14867
14868   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14869   player->is_dropping = TRUE;
14870
14871   player->drop_pressed_delay = 0;
14872   player->is_dropping_pressed = FALSE;
14873
14874   player->drop_x = dropx;
14875   player->drop_y = dropy;
14876
14877   return TRUE;
14878 }
14879
14880 // ----------------------------------------------------------------------------
14881 // game sound playing functions
14882 // ----------------------------------------------------------------------------
14883
14884 static int *loop_sound_frame = NULL;
14885 static int *loop_sound_volume = NULL;
14886
14887 void InitPlayLevelSound(void)
14888 {
14889   int num_sounds = getSoundListSize();
14890
14891   checked_free(loop_sound_frame);
14892   checked_free(loop_sound_volume);
14893
14894   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14895   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14896 }
14897
14898 static void PlayLevelSound(int x, int y, int nr)
14899 {
14900   int sx = SCREENX(x), sy = SCREENY(y);
14901   int volume, stereo_position;
14902   int max_distance = 8;
14903   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14904
14905   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14906       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14907     return;
14908
14909   if (!IN_LEV_FIELD(x, y) ||
14910       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14911       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14912     return;
14913
14914   volume = SOUND_MAX_VOLUME;
14915
14916   if (!IN_SCR_FIELD(sx, sy))
14917   {
14918     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14919     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14920
14921     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14922   }
14923
14924   stereo_position = (SOUND_MAX_LEFT +
14925                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14926                      (SCR_FIELDX + 2 * max_distance));
14927
14928   if (IS_LOOP_SOUND(nr))
14929   {
14930     /* This assures that quieter loop sounds do not overwrite louder ones,
14931        while restarting sound volume comparison with each new game frame. */
14932
14933     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14934       return;
14935
14936     loop_sound_volume[nr] = volume;
14937     loop_sound_frame[nr] = FrameCounter;
14938   }
14939
14940   PlaySoundExt(nr, volume, stereo_position, type);
14941 }
14942
14943 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14944 {
14945   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14946                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14947                  y < LEVELY(BY1) ? LEVELY(BY1) :
14948                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14949                  sound_action);
14950 }
14951
14952 static void PlayLevelSoundAction(int x, int y, int action)
14953 {
14954   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14955 }
14956
14957 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14958 {
14959   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14960
14961   if (sound_effect != SND_UNDEFINED)
14962     PlayLevelSound(x, y, sound_effect);
14963 }
14964
14965 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14966                                               int action)
14967 {
14968   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14969
14970   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14971     PlayLevelSound(x, y, sound_effect);
14972 }
14973
14974 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14975 {
14976   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14977
14978   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14979     PlayLevelSound(x, y, sound_effect);
14980 }
14981
14982 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14983 {
14984   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14985
14986   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14987     StopSound(sound_effect);
14988 }
14989
14990 static int getLevelMusicNr(void)
14991 {
14992   if (levelset.music[level_nr] != MUS_UNDEFINED)
14993     return levelset.music[level_nr];            // from config file
14994   else
14995     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14996 }
14997
14998 static void FadeLevelSounds(void)
14999 {
15000   FadeSounds();
15001 }
15002
15003 static void FadeLevelMusic(void)
15004 {
15005   int music_nr = getLevelMusicNr();
15006   char *curr_music = getCurrentlyPlayingMusicFilename();
15007   char *next_music = getMusicInfoEntryFilename(music_nr);
15008
15009   if (!strEqual(curr_music, next_music))
15010     FadeMusic();
15011 }
15012
15013 void FadeLevelSoundsAndMusic(void)
15014 {
15015   FadeLevelSounds();
15016   FadeLevelMusic();
15017 }
15018
15019 static void PlayLevelMusic(void)
15020 {
15021   int music_nr = getLevelMusicNr();
15022   char *curr_music = getCurrentlyPlayingMusicFilename();
15023   char *next_music = getMusicInfoEntryFilename(music_nr);
15024
15025   if (!strEqual(curr_music, next_music))
15026     PlayMusicLoop(music_nr);
15027 }
15028
15029 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15030 {
15031   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15032   int offset = 0;
15033   int x = xx - offset;
15034   int y = yy - offset;
15035
15036   switch (sample)
15037   {
15038     case SOUND_blank:
15039       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15040       break;
15041
15042     case SOUND_roll:
15043       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15044       break;
15045
15046     case SOUND_stone:
15047       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15048       break;
15049
15050     case SOUND_nut:
15051       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15052       break;
15053
15054     case SOUND_crack:
15055       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15056       break;
15057
15058     case SOUND_bug:
15059       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15060       break;
15061
15062     case SOUND_tank:
15063       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15064       break;
15065
15066     case SOUND_android_clone:
15067       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15068       break;
15069
15070     case SOUND_android_move:
15071       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15072       break;
15073
15074     case SOUND_spring:
15075       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15076       break;
15077
15078     case SOUND_slurp:
15079       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15080       break;
15081
15082     case SOUND_eater:
15083       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15084       break;
15085
15086     case SOUND_eater_eat:
15087       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15088       break;
15089
15090     case SOUND_alien:
15091       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15092       break;
15093
15094     case SOUND_collect:
15095       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15096       break;
15097
15098     case SOUND_diamond:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15100       break;
15101
15102     case SOUND_squash:
15103       // !!! CHECK THIS !!!
15104 #if 1
15105       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15106 #else
15107       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15108 #endif
15109       break;
15110
15111     case SOUND_wonderfall:
15112       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15113       break;
15114
15115     case SOUND_drip:
15116       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15117       break;
15118
15119     case SOUND_push:
15120       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15121       break;
15122
15123     case SOUND_dirt:
15124       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15125       break;
15126
15127     case SOUND_acid:
15128       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15129       break;
15130
15131     case SOUND_ball:
15132       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15133       break;
15134
15135     case SOUND_slide:
15136       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15137       break;
15138
15139     case SOUND_wonder:
15140       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15141       break;
15142
15143     case SOUND_door:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15145       break;
15146
15147     case SOUND_exit_open:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15149       break;
15150
15151     case SOUND_exit_leave:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15153       break;
15154
15155     case SOUND_dynamite:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15157       break;
15158
15159     case SOUND_tick:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15161       break;
15162
15163     case SOUND_press:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15165       break;
15166
15167     case SOUND_wheel:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15169       break;
15170
15171     case SOUND_boom:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15173       break;
15174
15175     case SOUND_die:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15177       break;
15178
15179     case SOUND_time:
15180       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15181       break;
15182
15183     default:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15185       break;
15186   }
15187 }
15188
15189 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15190 {
15191   int element = map_element_SP_to_RND(element_sp);
15192   int action = map_action_SP_to_RND(action_sp);
15193   int offset = (setup.sp_show_border_elements ? 0 : 1);
15194   int x = xx - offset;
15195   int y = yy - offset;
15196
15197   PlayLevelSoundElementAction(x, y, element, action);
15198 }
15199
15200 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15201 {
15202   int element = map_element_MM_to_RND(element_mm);
15203   int action = map_action_MM_to_RND(action_mm);
15204   int offset = 0;
15205   int x = xx - offset;
15206   int y = yy - offset;
15207
15208   if (!IS_MM_ELEMENT(element))
15209     element = EL_MM_DEFAULT;
15210
15211   PlayLevelSoundElementAction(x, y, element, action);
15212 }
15213
15214 void PlaySound_MM(int sound_mm)
15215 {
15216   int sound = map_sound_MM_to_RND(sound_mm);
15217
15218   if (sound == SND_UNDEFINED)
15219     return;
15220
15221   PlaySound(sound);
15222 }
15223
15224 void PlaySoundLoop_MM(int sound_mm)
15225 {
15226   int sound = map_sound_MM_to_RND(sound_mm);
15227
15228   if (sound == SND_UNDEFINED)
15229     return;
15230
15231   PlaySoundLoop(sound);
15232 }
15233
15234 void StopSound_MM(int sound_mm)
15235 {
15236   int sound = map_sound_MM_to_RND(sound_mm);
15237
15238   if (sound == SND_UNDEFINED)
15239     return;
15240
15241   StopSound(sound);
15242 }
15243
15244 void RaiseScore(int value)
15245 {
15246   game.score += value;
15247
15248   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15249
15250   DisplayGameControlValues();
15251 }
15252
15253 void RaiseScoreElement(int element)
15254 {
15255   switch (element)
15256   {
15257     case EL_EMERALD:
15258     case EL_BD_DIAMOND:
15259     case EL_EMERALD_YELLOW:
15260     case EL_EMERALD_RED:
15261     case EL_EMERALD_PURPLE:
15262     case EL_SP_INFOTRON:
15263       RaiseScore(level.score[SC_EMERALD]);
15264       break;
15265     case EL_DIAMOND:
15266       RaiseScore(level.score[SC_DIAMOND]);
15267       break;
15268     case EL_CRYSTAL:
15269       RaiseScore(level.score[SC_CRYSTAL]);
15270       break;
15271     case EL_PEARL:
15272       RaiseScore(level.score[SC_PEARL]);
15273       break;
15274     case EL_BUG:
15275     case EL_BD_BUTTERFLY:
15276     case EL_SP_ELECTRON:
15277       RaiseScore(level.score[SC_BUG]);
15278       break;
15279     case EL_SPACESHIP:
15280     case EL_BD_FIREFLY:
15281     case EL_SP_SNIKSNAK:
15282       RaiseScore(level.score[SC_SPACESHIP]);
15283       break;
15284     case EL_YAMYAM:
15285     case EL_DARK_YAMYAM:
15286       RaiseScore(level.score[SC_YAMYAM]);
15287       break;
15288     case EL_ROBOT:
15289       RaiseScore(level.score[SC_ROBOT]);
15290       break;
15291     case EL_PACMAN:
15292       RaiseScore(level.score[SC_PACMAN]);
15293       break;
15294     case EL_NUT:
15295       RaiseScore(level.score[SC_NUT]);
15296       break;
15297     case EL_DYNAMITE:
15298     case EL_EM_DYNAMITE:
15299     case EL_SP_DISK_RED:
15300     case EL_DYNABOMB_INCREASE_NUMBER:
15301     case EL_DYNABOMB_INCREASE_SIZE:
15302     case EL_DYNABOMB_INCREASE_POWER:
15303       RaiseScore(level.score[SC_DYNAMITE]);
15304       break;
15305     case EL_SHIELD_NORMAL:
15306     case EL_SHIELD_DEADLY:
15307       RaiseScore(level.score[SC_SHIELD]);
15308       break;
15309     case EL_EXTRA_TIME:
15310       RaiseScore(level.extra_time_score);
15311       break;
15312     case EL_KEY_1:
15313     case EL_KEY_2:
15314     case EL_KEY_3:
15315     case EL_KEY_4:
15316     case EL_EM_KEY_1:
15317     case EL_EM_KEY_2:
15318     case EL_EM_KEY_3:
15319     case EL_EM_KEY_4:
15320     case EL_EMC_KEY_5:
15321     case EL_EMC_KEY_6:
15322     case EL_EMC_KEY_7:
15323     case EL_EMC_KEY_8:
15324     case EL_DC_KEY_WHITE:
15325       RaiseScore(level.score[SC_KEY]);
15326       break;
15327     default:
15328       RaiseScore(element_info[element].collect_score);
15329       break;
15330   }
15331 }
15332
15333 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15334 {
15335   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15336   {
15337     // closing door required in case of envelope style request dialogs
15338     if (!skip_request)
15339     {
15340       // prevent short reactivation of overlay buttons while closing door
15341       SetOverlayActive(FALSE);
15342
15343       CloseDoor(DOOR_CLOSE_1);
15344     }
15345
15346     if (network.enabled)
15347       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15348     else
15349     {
15350       if (quick_quit)
15351         FadeSkipNextFadeIn();
15352
15353       SetGameStatus(GAME_MODE_MAIN);
15354
15355       DrawMainMenu();
15356     }
15357   }
15358   else          // continue playing the game
15359   {
15360     if (tape.playing && tape.deactivate_display)
15361       TapeDeactivateDisplayOff(TRUE);
15362
15363     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15364
15365     if (tape.playing && tape.deactivate_display)
15366       TapeDeactivateDisplayOn();
15367   }
15368 }
15369
15370 void RequestQuitGame(boolean ask_if_really_quit)
15371 {
15372   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15373   boolean skip_request = game.all_players_gone || quick_quit;
15374
15375   RequestQuitGameExt(skip_request, quick_quit,
15376                      "Do you really want to quit the game?");
15377 }
15378
15379 void RequestRestartGame(char *message)
15380 {
15381   game.restart_game_message = NULL;
15382
15383   boolean has_started_game = hasStartedNetworkGame();
15384   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15385
15386   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15387   {
15388     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15389   }
15390   else
15391   {
15392     // needed in case of envelope request to close game panel
15393     CloseDoor(DOOR_CLOSE_1);
15394
15395     SetGameStatus(GAME_MODE_MAIN);
15396
15397     DrawMainMenu();
15398   }
15399 }
15400
15401 void CheckGameOver(void)
15402 {
15403   static boolean last_game_over = FALSE;
15404   static int game_over_delay = 0;
15405   int game_over_delay_value = 50;
15406   boolean game_over = checkGameFailed();
15407
15408   // do not handle game over if request dialog is already active
15409   if (game.request_active)
15410     return;
15411
15412   // do not ask to play again if game was never actually played
15413   if (!game.GamePlayed)
15414     return;
15415
15416   if (!game_over)
15417   {
15418     last_game_over = FALSE;
15419     game_over_delay = game_over_delay_value;
15420
15421     return;
15422   }
15423
15424   if (game_over_delay > 0)
15425   {
15426     game_over_delay--;
15427
15428     return;
15429   }
15430
15431   if (last_game_over != game_over)
15432     game.restart_game_message = (hasStartedNetworkGame() ?
15433                                  "Game over! Play it again?" :
15434                                  "Game over!");
15435
15436   last_game_over = game_over;
15437 }
15438
15439 boolean checkGameSolved(void)
15440 {
15441   // set for all game engines if level was solved
15442   return game.LevelSolved_GameEnd;
15443 }
15444
15445 boolean checkGameFailed(void)
15446 {
15447   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15448     return (game_em.game_over && !game_em.level_solved);
15449   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15450     return (game_sp.game_over && !game_sp.level_solved);
15451   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15452     return (game_mm.game_over && !game_mm.level_solved);
15453   else                          // GAME_ENGINE_TYPE_RND
15454     return (game.GameOver && !game.LevelSolved);
15455 }
15456
15457 boolean checkGameEnded(void)
15458 {
15459   return (checkGameSolved() || checkGameFailed());
15460 }
15461
15462
15463 // ----------------------------------------------------------------------------
15464 // random generator functions
15465 // ----------------------------------------------------------------------------
15466
15467 unsigned int InitEngineRandom_RND(int seed)
15468 {
15469   game.num_random_calls = 0;
15470
15471   return InitEngineRandom(seed);
15472 }
15473
15474 unsigned int RND(int max)
15475 {
15476   if (max > 0)
15477   {
15478     game.num_random_calls++;
15479
15480     return GetEngineRandom(max);
15481   }
15482
15483   return 0;
15484 }
15485
15486
15487 // ----------------------------------------------------------------------------
15488 // game engine snapshot handling functions
15489 // ----------------------------------------------------------------------------
15490
15491 struct EngineSnapshotInfo
15492 {
15493   // runtime values for custom element collect score
15494   int collect_score[NUM_CUSTOM_ELEMENTS];
15495
15496   // runtime values for group element choice position
15497   int choice_pos[NUM_GROUP_ELEMENTS];
15498
15499   // runtime values for belt position animations
15500   int belt_graphic[4][NUM_BELT_PARTS];
15501   int belt_anim_mode[4][NUM_BELT_PARTS];
15502 };
15503
15504 static struct EngineSnapshotInfo engine_snapshot_rnd;
15505 static char *snapshot_level_identifier = NULL;
15506 static int snapshot_level_nr = -1;
15507
15508 static void SaveEngineSnapshotValues_RND(void)
15509 {
15510   static int belt_base_active_element[4] =
15511   {
15512     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15513     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15514     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15515     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15516   };
15517   int i, j;
15518
15519   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15520   {
15521     int element = EL_CUSTOM_START + i;
15522
15523     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15524   }
15525
15526   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15527   {
15528     int element = EL_GROUP_START + i;
15529
15530     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15531   }
15532
15533   for (i = 0; i < 4; i++)
15534   {
15535     for (j = 0; j < NUM_BELT_PARTS; j++)
15536     {
15537       int element = belt_base_active_element[i] + j;
15538       int graphic = el2img(element);
15539       int anim_mode = graphic_info[graphic].anim_mode;
15540
15541       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15542       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15543     }
15544   }
15545 }
15546
15547 static void LoadEngineSnapshotValues_RND(void)
15548 {
15549   unsigned int num_random_calls = game.num_random_calls;
15550   int i, j;
15551
15552   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15553   {
15554     int element = EL_CUSTOM_START + i;
15555
15556     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15557   }
15558
15559   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15560   {
15561     int element = EL_GROUP_START + i;
15562
15563     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15564   }
15565
15566   for (i = 0; i < 4; i++)
15567   {
15568     for (j = 0; j < NUM_BELT_PARTS; j++)
15569     {
15570       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15571       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15572
15573       graphic_info[graphic].anim_mode = anim_mode;
15574     }
15575   }
15576
15577   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15578   {
15579     InitRND(tape.random_seed);
15580     for (i = 0; i < num_random_calls; i++)
15581       RND(1);
15582   }
15583
15584   if (game.num_random_calls != num_random_calls)
15585   {
15586     Error("number of random calls out of sync");
15587     Error("number of random calls should be %d", num_random_calls);
15588     Error("number of random calls is %d", game.num_random_calls);
15589
15590     Fail("this should not happen -- please debug");
15591   }
15592 }
15593
15594 void FreeEngineSnapshotSingle(void)
15595 {
15596   FreeSnapshotSingle();
15597
15598   setString(&snapshot_level_identifier, NULL);
15599   snapshot_level_nr = -1;
15600 }
15601
15602 void FreeEngineSnapshotList(void)
15603 {
15604   FreeSnapshotList();
15605 }
15606
15607 static ListNode *SaveEngineSnapshotBuffers(void)
15608 {
15609   ListNode *buffers = NULL;
15610
15611   // copy some special values to a structure better suited for the snapshot
15612
15613   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15614     SaveEngineSnapshotValues_RND();
15615   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15616     SaveEngineSnapshotValues_EM();
15617   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15618     SaveEngineSnapshotValues_SP(&buffers);
15619   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15620     SaveEngineSnapshotValues_MM(&buffers);
15621
15622   // save values stored in special snapshot structure
15623
15624   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15625     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15626   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15627     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15628   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15629     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15630   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15632
15633   // save further RND engine values
15634
15635   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15636   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15638
15639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15644
15645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15648
15649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15650
15651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15653
15654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15672
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15675
15676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15679
15680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15682
15683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15688
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15691
15692 #if 0
15693   ListNode *node = engine_snapshot_list_rnd;
15694   int num_bytes = 0;
15695
15696   while (node != NULL)
15697   {
15698     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15699
15700     node = node->next;
15701   }
15702
15703   Debug("game:playing:SaveEngineSnapshotBuffers",
15704         "size of engine snapshot: %d bytes", num_bytes);
15705 #endif
15706
15707   return buffers;
15708 }
15709
15710 void SaveEngineSnapshotSingle(void)
15711 {
15712   ListNode *buffers = SaveEngineSnapshotBuffers();
15713
15714   // finally save all snapshot buffers to single snapshot
15715   SaveSnapshotSingle(buffers);
15716
15717   // save level identification information
15718   setString(&snapshot_level_identifier, leveldir_current->identifier);
15719   snapshot_level_nr = level_nr;
15720 }
15721
15722 boolean CheckSaveEngineSnapshotToList(void)
15723 {
15724   boolean save_snapshot =
15725     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15726      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15727       game.snapshot.changed_action) ||
15728      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15729       game.snapshot.collected_item));
15730
15731   game.snapshot.changed_action = FALSE;
15732   game.snapshot.collected_item = FALSE;
15733   game.snapshot.save_snapshot = save_snapshot;
15734
15735   return save_snapshot;
15736 }
15737
15738 void SaveEngineSnapshotToList(void)
15739 {
15740   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15741       tape.quick_resume)
15742     return;
15743
15744   ListNode *buffers = SaveEngineSnapshotBuffers();
15745
15746   // finally save all snapshot buffers to snapshot list
15747   SaveSnapshotToList(buffers);
15748 }
15749
15750 void SaveEngineSnapshotToListInitial(void)
15751 {
15752   FreeEngineSnapshotList();
15753
15754   SaveEngineSnapshotToList();
15755 }
15756
15757 static void LoadEngineSnapshotValues(void)
15758 {
15759   // restore special values from snapshot structure
15760
15761   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15762     LoadEngineSnapshotValues_RND();
15763   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15764     LoadEngineSnapshotValues_EM();
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15766     LoadEngineSnapshotValues_SP();
15767   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15768     LoadEngineSnapshotValues_MM();
15769 }
15770
15771 void LoadEngineSnapshotSingle(void)
15772 {
15773   LoadSnapshotSingle();
15774
15775   LoadEngineSnapshotValues();
15776 }
15777
15778 static void LoadEngineSnapshot_Undo(int steps)
15779 {
15780   LoadSnapshotFromList_Older(steps);
15781
15782   LoadEngineSnapshotValues();
15783 }
15784
15785 static void LoadEngineSnapshot_Redo(int steps)
15786 {
15787   LoadSnapshotFromList_Newer(steps);
15788
15789   LoadEngineSnapshotValues();
15790 }
15791
15792 boolean CheckEngineSnapshotSingle(void)
15793 {
15794   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15795           snapshot_level_nr == level_nr);
15796 }
15797
15798 boolean CheckEngineSnapshotList(void)
15799 {
15800   return CheckSnapshotList();
15801 }
15802
15803
15804 // ---------- new game button stuff -------------------------------------------
15805
15806 static struct
15807 {
15808   int graphic;
15809   struct XY *pos;
15810   int gadget_id;
15811   boolean *setup_value;
15812   boolean allowed_on_tape;
15813   boolean is_touch_button;
15814   char *infotext;
15815 } gamebutton_info[NUM_GAME_BUTTONS] =
15816 {
15817   {
15818     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15819     GAME_CTRL_ID_STOP,                          NULL,
15820     TRUE, FALSE,                                "stop game"
15821   },
15822   {
15823     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15824     GAME_CTRL_ID_PAUSE,                         NULL,
15825     TRUE, FALSE,                                "pause game"
15826   },
15827   {
15828     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15829     GAME_CTRL_ID_PLAY,                          NULL,
15830     TRUE, FALSE,                                "play game"
15831   },
15832   {
15833     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15834     GAME_CTRL_ID_UNDO,                          NULL,
15835     TRUE, FALSE,                                "undo step"
15836   },
15837   {
15838     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15839     GAME_CTRL_ID_REDO,                          NULL,
15840     TRUE, FALSE,                                "redo step"
15841   },
15842   {
15843     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15844     GAME_CTRL_ID_SAVE,                          NULL,
15845     TRUE, FALSE,                                "save game"
15846   },
15847   {
15848     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15849     GAME_CTRL_ID_PAUSE2,                        NULL,
15850     TRUE, FALSE,                                "pause game"
15851   },
15852   {
15853     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15854     GAME_CTRL_ID_LOAD,                          NULL,
15855     TRUE, FALSE,                                "load game"
15856   },
15857   {
15858     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15859     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15860     FALSE, FALSE,                               "stop game"
15861   },
15862   {
15863     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15864     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15865     FALSE, FALSE,                               "pause game"
15866   },
15867   {
15868     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15869     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15870     FALSE, FALSE,                               "play game"
15871   },
15872   {
15873     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15874     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15875     FALSE, TRUE,                                "stop game"
15876   },
15877   {
15878     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15879     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15880     FALSE, TRUE,                                "pause game"
15881   },
15882   {
15883     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15884     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15885     TRUE, FALSE,                                "background music on/off"
15886   },
15887   {
15888     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15889     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15890     TRUE, FALSE,                                "sound loops on/off"
15891   },
15892   {
15893     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15894     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15895     TRUE, FALSE,                                "normal sounds on/off"
15896   },
15897   {
15898     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15899     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15900     FALSE, FALSE,                               "background music on/off"
15901   },
15902   {
15903     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15904     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15905     FALSE, FALSE,                               "sound loops on/off"
15906   },
15907   {
15908     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15909     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15910     FALSE, FALSE,                               "normal sounds on/off"
15911   }
15912 };
15913
15914 void CreateGameButtons(void)
15915 {
15916   int i;
15917
15918   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15919   {
15920     int graphic = gamebutton_info[i].graphic;
15921     struct GraphicInfo *gfx = &graphic_info[graphic];
15922     struct XY *pos = gamebutton_info[i].pos;
15923     struct GadgetInfo *gi;
15924     int button_type;
15925     boolean checked;
15926     unsigned int event_mask;
15927     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15928     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15929     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15930     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15931     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15932     int gd_x   = gfx->src_x;
15933     int gd_y   = gfx->src_y;
15934     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15935     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15936     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15937     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15938     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15939     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15940     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15941     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15942     int id = i;
15943
15944     if (gfx->bitmap == NULL)
15945     {
15946       game_gadget[id] = NULL;
15947
15948       continue;
15949     }
15950
15951     if (id == GAME_CTRL_ID_STOP ||
15952         id == GAME_CTRL_ID_PANEL_STOP ||
15953         id == GAME_CTRL_ID_TOUCH_STOP ||
15954         id == GAME_CTRL_ID_PLAY ||
15955         id == GAME_CTRL_ID_PANEL_PLAY ||
15956         id == GAME_CTRL_ID_SAVE ||
15957         id == GAME_CTRL_ID_LOAD)
15958     {
15959       button_type = GD_TYPE_NORMAL_BUTTON;
15960       checked = FALSE;
15961       event_mask = GD_EVENT_RELEASED;
15962     }
15963     else if (id == GAME_CTRL_ID_UNDO ||
15964              id == GAME_CTRL_ID_REDO)
15965     {
15966       button_type = GD_TYPE_NORMAL_BUTTON;
15967       checked = FALSE;
15968       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15969     }
15970     else
15971     {
15972       button_type = GD_TYPE_CHECK_BUTTON;
15973       checked = (gamebutton_info[i].setup_value != NULL ?
15974                  *gamebutton_info[i].setup_value : FALSE);
15975       event_mask = GD_EVENT_PRESSED;
15976     }
15977
15978     gi = CreateGadget(GDI_CUSTOM_ID, id,
15979                       GDI_IMAGE_ID, graphic,
15980                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15981                       GDI_X, base_x + x,
15982                       GDI_Y, base_y + y,
15983                       GDI_WIDTH, gfx->width,
15984                       GDI_HEIGHT, gfx->height,
15985                       GDI_TYPE, button_type,
15986                       GDI_STATE, GD_BUTTON_UNPRESSED,
15987                       GDI_CHECKED, checked,
15988                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15989                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15990                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15991                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15992                       GDI_DIRECT_DRAW, FALSE,
15993                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15994                       GDI_EVENT_MASK, event_mask,
15995                       GDI_CALLBACK_ACTION, HandleGameButtons,
15996                       GDI_END);
15997
15998     if (gi == NULL)
15999       Fail("cannot create gadget");
16000
16001     game_gadget[id] = gi;
16002   }
16003 }
16004
16005 void FreeGameButtons(void)
16006 {
16007   int i;
16008
16009   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16010     FreeGadget(game_gadget[i]);
16011 }
16012
16013 static void UnmapGameButtonsAtSamePosition(int id)
16014 {
16015   int i;
16016
16017   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16018     if (i != id &&
16019         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16020         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16021       UnmapGadget(game_gadget[i]);
16022 }
16023
16024 static void UnmapGameButtonsAtSamePosition_All(void)
16025 {
16026   if (setup.show_snapshot_buttons)
16027   {
16028     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16029     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16030     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16031   }
16032   else
16033   {
16034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16037
16038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16041   }
16042 }
16043
16044 static void MapGameButtonsAtSamePosition(int id)
16045 {
16046   int i;
16047
16048   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16049     if (i != id &&
16050         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16051         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16052       MapGadget(game_gadget[i]);
16053
16054   UnmapGameButtonsAtSamePosition_All();
16055 }
16056
16057 void MapUndoRedoButtons(void)
16058 {
16059   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16060   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16061
16062   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16063   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16064 }
16065
16066 void UnmapUndoRedoButtons(void)
16067 {
16068   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16069   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16070
16071   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16072   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16073 }
16074
16075 void ModifyPauseButtons(void)
16076 {
16077   static int ids[] =
16078   {
16079     GAME_CTRL_ID_PAUSE,
16080     GAME_CTRL_ID_PAUSE2,
16081     GAME_CTRL_ID_PANEL_PAUSE,
16082     GAME_CTRL_ID_TOUCH_PAUSE,
16083     -1
16084   };
16085   int i;
16086
16087   for (i = 0; ids[i] > -1; i++)
16088     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16089 }
16090
16091 static void MapGameButtonsExt(boolean on_tape)
16092 {
16093   int i;
16094
16095   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16096     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16097         i != GAME_CTRL_ID_UNDO &&
16098         i != GAME_CTRL_ID_REDO)
16099       MapGadget(game_gadget[i]);
16100
16101   UnmapGameButtonsAtSamePosition_All();
16102
16103   RedrawGameButtons();
16104 }
16105
16106 static void UnmapGameButtonsExt(boolean on_tape)
16107 {
16108   int i;
16109
16110   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16111     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16112       UnmapGadget(game_gadget[i]);
16113 }
16114
16115 static void RedrawGameButtonsExt(boolean on_tape)
16116 {
16117   int i;
16118
16119   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16120     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16121       RedrawGadget(game_gadget[i]);
16122 }
16123
16124 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16125 {
16126   if (gi == NULL)
16127     return;
16128
16129   gi->checked = state;
16130 }
16131
16132 static void RedrawSoundButtonGadget(int id)
16133 {
16134   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16135              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16136              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16137              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16138              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16139              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16140              id);
16141
16142   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16143   RedrawGadget(game_gadget[id2]);
16144 }
16145
16146 void MapGameButtons(void)
16147 {
16148   MapGameButtonsExt(FALSE);
16149 }
16150
16151 void UnmapGameButtons(void)
16152 {
16153   UnmapGameButtonsExt(FALSE);
16154 }
16155
16156 void RedrawGameButtons(void)
16157 {
16158   RedrawGameButtonsExt(FALSE);
16159 }
16160
16161 void MapGameButtonsOnTape(void)
16162 {
16163   MapGameButtonsExt(TRUE);
16164 }
16165
16166 void UnmapGameButtonsOnTape(void)
16167 {
16168   UnmapGameButtonsExt(TRUE);
16169 }
16170
16171 void RedrawGameButtonsOnTape(void)
16172 {
16173   RedrawGameButtonsExt(TRUE);
16174 }
16175
16176 static void GameUndoRedoExt(void)
16177 {
16178   ClearPlayerAction();
16179
16180   tape.pausing = TRUE;
16181
16182   RedrawPlayfield();
16183   UpdateAndDisplayGameControlValues();
16184
16185   DrawCompleteVideoDisplay();
16186   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16187   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16188   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16189
16190   BackToFront();
16191 }
16192
16193 static void GameUndo(int steps)
16194 {
16195   if (!CheckEngineSnapshotList())
16196     return;
16197
16198   LoadEngineSnapshot_Undo(steps);
16199
16200   GameUndoRedoExt();
16201 }
16202
16203 static void GameRedo(int steps)
16204 {
16205   if (!CheckEngineSnapshotList())
16206     return;
16207
16208   LoadEngineSnapshot_Redo(steps);
16209
16210   GameUndoRedoExt();
16211 }
16212
16213 static void HandleGameButtonsExt(int id, int button)
16214 {
16215   static boolean game_undo_executed = FALSE;
16216   int steps = BUTTON_STEPSIZE(button);
16217   boolean handle_game_buttons =
16218     (game_status == GAME_MODE_PLAYING ||
16219      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16220
16221   if (!handle_game_buttons)
16222     return;
16223
16224   switch (id)
16225   {
16226     case GAME_CTRL_ID_STOP:
16227     case GAME_CTRL_ID_PANEL_STOP:
16228     case GAME_CTRL_ID_TOUCH_STOP:
16229       if (game_status == GAME_MODE_MAIN)
16230         break;
16231
16232       if (tape.playing)
16233         TapeStop();
16234       else
16235         RequestQuitGame(TRUE);
16236
16237       break;
16238
16239     case GAME_CTRL_ID_PAUSE:
16240     case GAME_CTRL_ID_PAUSE2:
16241     case GAME_CTRL_ID_PANEL_PAUSE:
16242     case GAME_CTRL_ID_TOUCH_PAUSE:
16243       if (network.enabled && game_status == GAME_MODE_PLAYING)
16244       {
16245         if (tape.pausing)
16246           SendToServer_ContinuePlaying();
16247         else
16248           SendToServer_PausePlaying();
16249       }
16250       else
16251         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16252
16253       game_undo_executed = FALSE;
16254
16255       break;
16256
16257     case GAME_CTRL_ID_PLAY:
16258     case GAME_CTRL_ID_PANEL_PLAY:
16259       if (game_status == GAME_MODE_MAIN)
16260       {
16261         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16262       }
16263       else if (tape.pausing)
16264       {
16265         if (network.enabled)
16266           SendToServer_ContinuePlaying();
16267         else
16268           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16269       }
16270       break;
16271
16272     case GAME_CTRL_ID_UNDO:
16273       // Important: When using "save snapshot when collecting an item" mode,
16274       // load last (current) snapshot for first "undo" after pressing "pause"
16275       // (else the last-but-one snapshot would be loaded, because the snapshot
16276       // pointer already points to the last snapshot when pressing "pause",
16277       // which is fine for "every step/move" mode, but not for "every collect")
16278       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16279           !game_undo_executed)
16280         steps--;
16281
16282       game_undo_executed = TRUE;
16283
16284       GameUndo(steps);
16285       break;
16286
16287     case GAME_CTRL_ID_REDO:
16288       GameRedo(steps);
16289       break;
16290
16291     case GAME_CTRL_ID_SAVE:
16292       TapeQuickSave();
16293       break;
16294
16295     case GAME_CTRL_ID_LOAD:
16296       TapeQuickLoad();
16297       break;
16298
16299     case SOUND_CTRL_ID_MUSIC:
16300     case SOUND_CTRL_ID_PANEL_MUSIC:
16301       if (setup.sound_music)
16302       { 
16303         setup.sound_music = FALSE;
16304
16305         FadeMusic();
16306       }
16307       else if (audio.music_available)
16308       { 
16309         setup.sound = setup.sound_music = TRUE;
16310
16311         SetAudioMode(setup.sound);
16312
16313         if (game_status == GAME_MODE_PLAYING)
16314           PlayLevelMusic();
16315       }
16316
16317       RedrawSoundButtonGadget(id);
16318
16319       break;
16320
16321     case SOUND_CTRL_ID_LOOPS:
16322     case SOUND_CTRL_ID_PANEL_LOOPS:
16323       if (setup.sound_loops)
16324         setup.sound_loops = FALSE;
16325       else if (audio.loops_available)
16326       {
16327         setup.sound = setup.sound_loops = TRUE;
16328
16329         SetAudioMode(setup.sound);
16330       }
16331
16332       RedrawSoundButtonGadget(id);
16333
16334       break;
16335
16336     case SOUND_CTRL_ID_SIMPLE:
16337     case SOUND_CTRL_ID_PANEL_SIMPLE:
16338       if (setup.sound_simple)
16339         setup.sound_simple = FALSE;
16340       else if (audio.sound_available)
16341       {
16342         setup.sound = setup.sound_simple = TRUE;
16343
16344         SetAudioMode(setup.sound);
16345       }
16346
16347       RedrawSoundButtonGadget(id);
16348
16349       break;
16350
16351     default:
16352       break;
16353   }
16354 }
16355
16356 static void HandleGameButtons(struct GadgetInfo *gi)
16357 {
16358   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16359 }
16360
16361 void HandleSoundButtonKeys(Key key)
16362 {
16363   if (key == setup.shortcut.sound_simple)
16364     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16365   else if (key == setup.shortcut.sound_loops)
16366     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16367   else if (key == setup.shortcut.sound_music)
16368     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16369 }