2535720ae6a4af3b9ab39825e58b344802e2dce5
[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_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3546   int initial_move_dir = MV_DOWN;
3547   int i, j, x, y;
3548
3549   // required here to update video display before fading (FIX THIS)
3550   DrawMaskedBorder(REDRAW_DOOR_2);
3551
3552   if (!game.restart_level)
3553     CloseDoor(DOOR_CLOSE_1);
3554
3555   SetGameStatus(GAME_MODE_PLAYING);
3556
3557   if (level_editor_test_game)
3558     FadeSkipNextFadeOut();
3559   else
3560     FadeSetEnterScreen();
3561
3562   if (CheckFadeAll())
3563     fade_mask = REDRAW_ALL;
3564
3565   FadeLevelSoundsAndMusic();
3566
3567   ExpireSoundLoops(TRUE);
3568
3569   FadeOut(fade_mask);
3570
3571   if (level_editor_test_game)
3572     FadeSkipNextFadeIn();
3573
3574   // needed if different viewport properties defined for playing
3575   ChangeViewportPropertiesIfNeeded();
3576
3577   ClearField();
3578
3579   DrawCompleteVideoDisplay();
3580
3581   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3582
3583   InitGameEngine();
3584   InitGameControlValues();
3585
3586   if (tape.recording)
3587   {
3588     // initialize tape actions from game when recording tape
3589     tape.use_key_actions   = game.use_key_actions;
3590     tape.use_mouse_actions = game.use_mouse_actions;
3591
3592     // initialize visible playfield size when recording tape (for team mode)
3593     tape.scr_fieldx = SCR_FIELDX;
3594     tape.scr_fieldy = SCR_FIELDY;
3595   }
3596
3597   // don't play tapes over network
3598   network_playing = (network.enabled && !tape.playing);
3599
3600   for (i = 0; i < MAX_PLAYERS; i++)
3601   {
3602     struct PlayerInfo *player = &stored_player[i];
3603
3604     player->index_nr = i;
3605     player->index_bit = (1 << i);
3606     player->element_nr = EL_PLAYER_1 + i;
3607
3608     player->present = FALSE;
3609     player->active = FALSE;
3610     player->mapped = FALSE;
3611
3612     player->killed = FALSE;
3613     player->reanimated = FALSE;
3614     player->buried = FALSE;
3615
3616     player->action = 0;
3617     player->effective_action = 0;
3618     player->programmed_action = 0;
3619     player->snap_action = 0;
3620
3621     player->mouse_action.lx = 0;
3622     player->mouse_action.ly = 0;
3623     player->mouse_action.button = 0;
3624     player->mouse_action.button_hint = 0;
3625
3626     player->effective_mouse_action.lx = 0;
3627     player->effective_mouse_action.ly = 0;
3628     player->effective_mouse_action.button = 0;
3629     player->effective_mouse_action.button_hint = 0;
3630
3631     for (j = 0; j < MAX_NUM_KEYS; j++)
3632       player->key[j] = FALSE;
3633
3634     player->num_white_keys = 0;
3635
3636     player->dynabomb_count = 0;
3637     player->dynabomb_size = 1;
3638     player->dynabombs_left = 0;
3639     player->dynabomb_xl = FALSE;
3640
3641     player->MovDir = initial_move_dir;
3642     player->MovPos = 0;
3643     player->GfxPos = 0;
3644     player->GfxDir = initial_move_dir;
3645     player->GfxAction = ACTION_DEFAULT;
3646     player->Frame = 0;
3647     player->StepFrame = 0;
3648
3649     player->initial_element = player->element_nr;
3650     player->artwork_element =
3651       (level.use_artwork_element[i] ? level.artwork_element[i] :
3652        player->element_nr);
3653     player->use_murphy = FALSE;
3654
3655     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3656     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3657
3658     player->gravity = level.initial_player_gravity[i];
3659
3660     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3661
3662     player->actual_frame_counter = 0;
3663
3664     player->step_counter = 0;
3665
3666     player->last_move_dir = initial_move_dir;
3667
3668     player->is_active = FALSE;
3669
3670     player->is_waiting = FALSE;
3671     player->is_moving = FALSE;
3672     player->is_auto_moving = FALSE;
3673     player->is_digging = FALSE;
3674     player->is_snapping = FALSE;
3675     player->is_collecting = FALSE;
3676     player->is_pushing = FALSE;
3677     player->is_switching = FALSE;
3678     player->is_dropping = FALSE;
3679     player->is_dropping_pressed = FALSE;
3680
3681     player->is_bored = FALSE;
3682     player->is_sleeping = FALSE;
3683
3684     player->was_waiting = TRUE;
3685     player->was_moving = FALSE;
3686     player->was_snapping = FALSE;
3687     player->was_dropping = FALSE;
3688
3689     player->force_dropping = FALSE;
3690
3691     player->frame_counter_bored = -1;
3692     player->frame_counter_sleeping = -1;
3693
3694     player->anim_delay_counter = 0;
3695     player->post_delay_counter = 0;
3696
3697     player->dir_waiting = initial_move_dir;
3698     player->action_waiting = ACTION_DEFAULT;
3699     player->last_action_waiting = ACTION_DEFAULT;
3700     player->special_action_bored = ACTION_DEFAULT;
3701     player->special_action_sleeping = ACTION_DEFAULT;
3702
3703     player->switch_x = -1;
3704     player->switch_y = -1;
3705
3706     player->drop_x = -1;
3707     player->drop_y = -1;
3708
3709     player->show_envelope = 0;
3710
3711     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3712
3713     player->push_delay       = -1;      // initialized when pushing starts
3714     player->push_delay_value = game.initial_push_delay_value;
3715
3716     player->drop_delay = 0;
3717     player->drop_pressed_delay = 0;
3718
3719     player->last_jx = -1;
3720     player->last_jy = -1;
3721     player->jx = -1;
3722     player->jy = -1;
3723
3724     player->shield_normal_time_left = 0;
3725     player->shield_deadly_time_left = 0;
3726
3727     player->last_removed_element = EL_UNDEFINED;
3728
3729     player->inventory_infinite_element = EL_UNDEFINED;
3730     player->inventory_size = 0;
3731
3732     if (level.use_initial_inventory[i])
3733     {
3734       for (j = 0; j < level.initial_inventory_size[i]; j++)
3735       {
3736         int element = level.initial_inventory_content[i][j];
3737         int collect_count = element_info[element].collect_count_initial;
3738         int k;
3739
3740         if (!IS_CUSTOM_ELEMENT(element))
3741           collect_count = 1;
3742
3743         if (collect_count == 0)
3744           player->inventory_infinite_element = element;
3745         else
3746           for (k = 0; k < collect_count; k++)
3747             if (player->inventory_size < MAX_INVENTORY_SIZE)
3748               player->inventory_element[player->inventory_size++] = element;
3749       }
3750     }
3751
3752     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3753     SnapField(player, 0, 0);
3754
3755     map_player_action[i] = i;
3756   }
3757
3758   network_player_action_received = FALSE;
3759
3760   // initial null action
3761   if (network_playing)
3762     SendToServer_MovePlayer(MV_NONE);
3763
3764   FrameCounter = 0;
3765   TimeFrames = 0;
3766   TimePlayed = 0;
3767   TimeLeft = level.time;
3768   TapeTime = 0;
3769
3770   ScreenMovDir = MV_NONE;
3771   ScreenMovPos = 0;
3772   ScreenGfxPos = 0;
3773
3774   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3775
3776   game.robot_wheel_x = -1;
3777   game.robot_wheel_y = -1;
3778
3779   game.exit_x = -1;
3780   game.exit_y = -1;
3781
3782   game.all_players_gone = FALSE;
3783
3784   game.LevelSolved = FALSE;
3785   game.GameOver = FALSE;
3786
3787   game.GamePlayed = !tape.playing;
3788
3789   game.LevelSolved_GameWon = FALSE;
3790   game.LevelSolved_GameEnd = FALSE;
3791   game.LevelSolved_SaveTape = FALSE;
3792   game.LevelSolved_SaveScore = FALSE;
3793
3794   game.LevelSolved_CountingTime = 0;
3795   game.LevelSolved_CountingScore = 0;
3796   game.LevelSolved_CountingHealth = 0;
3797
3798   game.panel.active = TRUE;
3799
3800   game.no_time_limit = (level.time == 0);
3801
3802   game.yamyam_content_nr = 0;
3803   game.robot_wheel_active = FALSE;
3804   game.magic_wall_active = FALSE;
3805   game.magic_wall_time_left = 0;
3806   game.light_time_left = 0;
3807   game.timegate_time_left = 0;
3808   game.switchgate_pos = 0;
3809   game.wind_direction = level.wind_direction_initial;
3810
3811   game.time_final = 0;
3812   game.score_time_final = 0;
3813
3814   game.score = 0;
3815   game.score_final = 0;
3816
3817   game.health = MAX_HEALTH;
3818   game.health_final = MAX_HEALTH;
3819
3820   game.gems_still_needed = level.gems_needed;
3821   game.sokoban_fields_still_needed = 0;
3822   game.sokoban_objects_still_needed = 0;
3823   game.lights_still_needed = 0;
3824   game.players_still_needed = 0;
3825   game.friends_still_needed = 0;
3826
3827   game.lenses_time_left = 0;
3828   game.magnify_time_left = 0;
3829
3830   game.ball_active = level.ball_active_initial;
3831   game.ball_content_nr = 0;
3832
3833   game.explosions_delayed = TRUE;
3834
3835   game.envelope_active = FALSE;
3836
3837   for (i = 0; i < NUM_BELTS; i++)
3838   {
3839     game.belt_dir[i] = MV_NONE;
3840     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3841   }
3842
3843   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3844     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3845
3846 #if DEBUG_INIT_PLAYER
3847   DebugPrintPlayerStatus("Player status at level initialization");
3848 #endif
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     Tile[x][y] = Last[x][y] = level.field[x][y];
3853     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854     ChangeDelay[x][y] = 0;
3855     ChangePage[x][y] = -1;
3856     CustomValue[x][y] = 0;              // initialized in InitField()
3857     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3858     AmoebaNr[x][y] = 0;
3859     WasJustMoving[x][y] = 0;
3860     WasJustFalling[x][y] = 0;
3861     CheckCollision[x][y] = 0;
3862     CheckImpact[x][y] = 0;
3863     Stop[x][y] = FALSE;
3864     Pushed[x][y] = FALSE;
3865
3866     ChangeCount[x][y] = 0;
3867     ChangeEvent[x][y] = -1;
3868
3869     ExplodePhase[x][y] = 0;
3870     ExplodeDelay[x][y] = 0;
3871     ExplodeField[x][y] = EX_TYPE_NONE;
3872
3873     RunnerVisit[x][y] = 0;
3874     PlayerVisit[x][y] = 0;
3875
3876     GfxFrame[x][y] = 0;
3877     GfxRandom[x][y] = INIT_GFX_RANDOM();
3878     GfxElement[x][y] = EL_UNDEFINED;
3879     GfxAction[x][y] = ACTION_DEFAULT;
3880     GfxDir[x][y] = MV_NONE;
3881     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3882   }
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3887       emulate_bd = FALSE;
3888     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3889       emulate_sp = FALSE;
3890
3891     InitField(x, y, TRUE);
3892
3893     ResetGfxAnimation(x, y);
3894   }
3895
3896   InitBeltMovement();
3897
3898   for (i = 0; i < MAX_PLAYERS; i++)
3899   {
3900     struct PlayerInfo *player = &stored_player[i];
3901
3902     // set number of special actions for bored and sleeping animation
3903     player->num_special_action_bored =
3904       get_num_special_action(player->artwork_element,
3905                              ACTION_BORING_1, ACTION_BORING_LAST);
3906     player->num_special_action_sleeping =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3909   }
3910
3911   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3912                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3913
3914   // initialize type of slippery elements
3915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3916   {
3917     if (!IS_CUSTOM_ELEMENT(i))
3918     {
3919       // default: elements slip down either to the left or right randomly
3920       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3921
3922       // SP style elements prefer to slip down on the left side
3923       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3924         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3925
3926       // BD style elements prefer to slip down on the left side
3927       if (game.emulation == EMU_BOULDERDASH)
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929     }
3930   }
3931
3932   // initialize explosion and ignition delay
3933   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3934   {
3935     if (!IS_CUSTOM_ELEMENT(i))
3936     {
3937       int num_phase = 8;
3938       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3939                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3940                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3941       int last_phase = (num_phase + 1) * delay;
3942       int half_phase = (num_phase / 2) * delay;
3943
3944       element_info[i].explosion_delay = last_phase - 1;
3945       element_info[i].ignition_delay = half_phase;
3946
3947       if (i == EL_BLACK_ORB)
3948         element_info[i].ignition_delay = 1;
3949     }
3950   }
3951
3952   // correct non-moving belts to start moving left
3953   for (i = 0; i < NUM_BELTS; i++)
3954     if (game.belt_dir[i] == MV_NONE)
3955       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3956
3957 #if USE_NEW_PLAYER_ASSIGNMENTS
3958   // use preferred player also in local single-player mode
3959   if (!network.enabled && !game.team_mode)
3960   {
3961     int new_index_nr = setup.network_player_nr;
3962
3963     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3964     {
3965       for (i = 0; i < MAX_PLAYERS; i++)
3966         stored_player[i].connected_locally = FALSE;
3967
3968       stored_player[new_index_nr].connected_locally = TRUE;
3969     }
3970   }
3971
3972   for (i = 0; i < MAX_PLAYERS; i++)
3973   {
3974     stored_player[i].connected = FALSE;
3975
3976     // in network game mode, the local player might not be the first player
3977     if (stored_player[i].connected_locally)
3978       local_player = &stored_player[i];
3979   }
3980
3981   if (!network.enabled)
3982     local_player->connected = TRUE;
3983
3984   if (tape.playing)
3985   {
3986     for (i = 0; i < MAX_PLAYERS; i++)
3987       stored_player[i].connected = tape.player_participates[i];
3988   }
3989   else if (network.enabled)
3990   {
3991     // add team mode players connected over the network (needed for correct
3992     // assignment of player figures from level to locally playing players)
3993
3994     for (i = 0; i < MAX_PLAYERS; i++)
3995       if (stored_player[i].connected_network)
3996         stored_player[i].connected = TRUE;
3997   }
3998   else if (game.team_mode)
3999   {
4000     // try to guess locally connected team mode players (needed for correct
4001     // assignment of player figures from level to locally playing players)
4002
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004       if (setup.input[i].use_joystick ||
4005           setup.input[i].key.left != KSYM_UNDEFINED)
4006         stored_player[i].connected = TRUE;
4007   }
4008
4009 #if DEBUG_INIT_PLAYER
4010   DebugPrintPlayerStatus("Player status after level initialization");
4011 #endif
4012
4013 #if DEBUG_INIT_PLAYER
4014   Debug("game:init:player", "Reassigning players ...");
4015 #endif
4016
4017   // check if any connected player was not found in playfield
4018   for (i = 0; i < MAX_PLAYERS; i++)
4019   {
4020     struct PlayerInfo *player = &stored_player[i];
4021
4022     if (player->connected && !player->present)
4023     {
4024       struct PlayerInfo *field_player = NULL;
4025
4026 #if DEBUG_INIT_PLAYER
4027       Debug("game:init:player",
4028             "- looking for field player for player %d ...", i + 1);
4029 #endif
4030
4031       // assign first free player found that is present in the playfield
4032
4033       // first try: look for unmapped playfield player that is not connected
4034       for (j = 0; j < MAX_PLAYERS; j++)
4035         if (field_player == NULL &&
4036             stored_player[j].present &&
4037             !stored_player[j].mapped &&
4038             !stored_player[j].connected)
4039           field_player = &stored_player[j];
4040
4041       // second try: look for *any* unmapped playfield player
4042       for (j = 0; j < MAX_PLAYERS; j++)
4043         if (field_player == NULL &&
4044             stored_player[j].present &&
4045             !stored_player[j].mapped)
4046           field_player = &stored_player[j];
4047
4048       if (field_player != NULL)
4049       {
4050         int jx = field_player->jx, jy = field_player->jy;
4051
4052 #if DEBUG_INIT_PLAYER
4053         Debug("game:init:player", "- found player %d",
4054               field_player->index_nr + 1);
4055 #endif
4056
4057         player->present = FALSE;
4058         player->active = FALSE;
4059
4060         field_player->present = TRUE;
4061         field_player->active = TRUE;
4062
4063         /*
4064         player->initial_element = field_player->initial_element;
4065         player->artwork_element = field_player->artwork_element;
4066
4067         player->block_last_field       = field_player->block_last_field;
4068         player->block_delay_adjustment = field_player->block_delay_adjustment;
4069         */
4070
4071         StorePlayer[jx][jy] = field_player->element_nr;
4072
4073         field_player->jx = field_player->last_jx = jx;
4074         field_player->jy = field_player->last_jy = jy;
4075
4076         if (local_player == player)
4077           local_player = field_player;
4078
4079         map_player_action[field_player->index_nr] = i;
4080
4081         field_player->mapped = TRUE;
4082
4083 #if DEBUG_INIT_PLAYER
4084         Debug("game:init:player", "- map_player_action[%d] == %d",
4085               field_player->index_nr + 1, i + 1);
4086 #endif
4087       }
4088     }
4089
4090     if (player->connected && player->present)
4091       player->mapped = TRUE;
4092   }
4093
4094 #if DEBUG_INIT_PLAYER
4095   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4096 #endif
4097
4098 #else
4099
4100   // check if any connected player was not found in playfield
4101   for (i = 0; i < MAX_PLAYERS; i++)
4102   {
4103     struct PlayerInfo *player = &stored_player[i];
4104
4105     if (player->connected && !player->present)
4106     {
4107       for (j = 0; j < MAX_PLAYERS; j++)
4108       {
4109         struct PlayerInfo *field_player = &stored_player[j];
4110         int jx = field_player->jx, jy = field_player->jy;
4111
4112         // assign first free player found that is present in the playfield
4113         if (field_player->present && !field_player->connected)
4114         {
4115           player->present = TRUE;
4116           player->active = TRUE;
4117
4118           field_player->present = FALSE;
4119           field_player->active = FALSE;
4120
4121           player->initial_element = field_player->initial_element;
4122           player->artwork_element = field_player->artwork_element;
4123
4124           player->block_last_field       = field_player->block_last_field;
4125           player->block_delay_adjustment = field_player->block_delay_adjustment;
4126
4127           StorePlayer[jx][jy] = player->element_nr;
4128
4129           player->jx = player->last_jx = jx;
4130           player->jy = player->last_jy = jy;
4131
4132           break;
4133         }
4134       }
4135     }
4136   }
4137 #endif
4138
4139 #if 0
4140   Debug("game:init:player", "local_player->present == %d",
4141         local_player->present);
4142 #endif
4143
4144   // set focus to local player for network games, else to all players
4145   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4146   game.centered_player_nr_next = game.centered_player_nr;
4147   game.set_centered_player = FALSE;
4148   game.set_centered_player_wrap = FALSE;
4149
4150   if (network_playing && tape.recording)
4151   {
4152     // store client dependent player focus when recording network games
4153     tape.centered_player_nr_next = game.centered_player_nr_next;
4154     tape.set_centered_player = TRUE;
4155   }
4156
4157   if (tape.playing)
4158   {
4159     // when playing a tape, eliminate all players who do not participate
4160
4161 #if USE_NEW_PLAYER_ASSIGNMENTS
4162
4163     if (!game.team_mode)
4164     {
4165       for (i = 0; i < MAX_PLAYERS; i++)
4166       {
4167         if (stored_player[i].active &&
4168             !tape.player_participates[map_player_action[i]])
4169         {
4170           struct PlayerInfo *player = &stored_player[i];
4171           int jx = player->jx, jy = player->jy;
4172
4173 #if DEBUG_INIT_PLAYER
4174           Debug("game:init:player", "Removing player %d at (%d, %d)",
4175                 i + 1, jx, jy);
4176 #endif
4177
4178           player->active = FALSE;
4179           StorePlayer[jx][jy] = 0;
4180           Tile[jx][jy] = EL_EMPTY;
4181         }
4182       }
4183     }
4184
4185 #else
4186
4187     for (i = 0; i < MAX_PLAYERS; i++)
4188     {
4189       if (stored_player[i].active &&
4190           !tape.player_participates[i])
4191       {
4192         struct PlayerInfo *player = &stored_player[i];
4193         int jx = player->jx, jy = player->jy;
4194
4195         player->active = FALSE;
4196         StorePlayer[jx][jy] = 0;
4197         Tile[jx][jy] = EL_EMPTY;
4198       }
4199     }
4200 #endif
4201   }
4202   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4203   {
4204     // when in single player mode, eliminate all but the local player
4205
4206     for (i = 0; i < MAX_PLAYERS; i++)
4207     {
4208       struct PlayerInfo *player = &stored_player[i];
4209
4210       if (player->active && player != local_player)
4211       {
4212         int jx = player->jx, jy = player->jy;
4213
4214         player->active = FALSE;
4215         player->present = FALSE;
4216
4217         StorePlayer[jx][jy] = 0;
4218         Tile[jx][jy] = EL_EMPTY;
4219       }
4220     }
4221   }
4222
4223   for (i = 0; i < MAX_PLAYERS; i++)
4224     if (stored_player[i].active)
4225       game.players_still_needed++;
4226
4227   if (level.solved_by_one_player)
4228     game.players_still_needed = 1;
4229
4230   // when recording the game, store which players take part in the game
4231   if (tape.recording)
4232   {
4233 #if USE_NEW_PLAYER_ASSIGNMENTS
4234     for (i = 0; i < MAX_PLAYERS; i++)
4235       if (stored_player[i].connected)
4236         tape.player_participates[i] = TRUE;
4237 #else
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].active)
4240         tape.player_participates[i] = TRUE;
4241 #endif
4242   }
4243
4244 #if DEBUG_INIT_PLAYER
4245   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4246 #endif
4247
4248   if (BorderElement == EL_EMPTY)
4249   {
4250     SBX_Left = 0;
4251     SBX_Right = lev_fieldx - SCR_FIELDX;
4252     SBY_Upper = 0;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY;
4254   }
4255   else
4256   {
4257     SBX_Left = -1;
4258     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4259     SBY_Upper = -1;
4260     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4261   }
4262
4263   if (full_lev_fieldx <= SCR_FIELDX)
4264     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4265   if (full_lev_fieldy <= SCR_FIELDY)
4266     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4267
4268   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4269     SBX_Left--;
4270   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4271     SBY_Upper--;
4272
4273   // if local player not found, look for custom element that might create
4274   // the player (make some assumptions about the right custom element)
4275   if (!local_player->present)
4276   {
4277     int start_x = 0, start_y = 0;
4278     int found_rating = 0;
4279     int found_element = EL_UNDEFINED;
4280     int player_nr = local_player->index_nr;
4281
4282     SCAN_PLAYFIELD(x, y)
4283     {
4284       int element = Tile[x][y];
4285       int content;
4286       int xx, yy;
4287       boolean is_player;
4288
4289       if (level.use_start_element[player_nr] &&
4290           level.start_element[player_nr] == element &&
4291           found_rating < 4)
4292       {
4293         start_x = x;
4294         start_y = y;
4295
4296         found_rating = 4;
4297         found_element = element;
4298       }
4299
4300       if (!IS_CUSTOM_ELEMENT(element))
4301         continue;
4302
4303       if (CAN_CHANGE(element))
4304       {
4305         for (i = 0; i < element_info[element].num_change_pages; i++)
4306         {
4307           // check for player created from custom element as single target
4308           content = element_info[element].change_page[i].target_element;
4309           is_player = IS_PLAYER_ELEMENT(content);
4310
4311           if (is_player && (found_rating < 3 ||
4312                             (found_rating == 3 && element < found_element)))
4313           {
4314             start_x = x;
4315             start_y = y;
4316
4317             found_rating = 3;
4318             found_element = element;
4319           }
4320         }
4321       }
4322
4323       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4324       {
4325         // check for player created from custom element as explosion content
4326         content = element_info[element].content.e[xx][yy];
4327         is_player = IS_PLAYER_ELEMENT(content);
4328
4329         if (is_player && (found_rating < 2 ||
4330                           (found_rating == 2 && element < found_element)))
4331         {
4332           start_x = x + xx - 1;
4333           start_y = y + yy - 1;
4334
4335           found_rating = 2;
4336           found_element = element;
4337         }
4338
4339         if (!CAN_CHANGE(element))
4340           continue;
4341
4342         for (i = 0; i < element_info[element].num_change_pages; i++)
4343         {
4344           // check for player created from custom element as extended target
4345           content =
4346             element_info[element].change_page[i].target_content.e[xx][yy];
4347
4348           is_player = IS_PLAYER_ELEMENT(content);
4349
4350           if (is_player && (found_rating < 1 ||
4351                             (found_rating == 1 && element < found_element)))
4352           {
4353             start_x = x + xx - 1;
4354             start_y = y + yy - 1;
4355
4356             found_rating = 1;
4357             found_element = element;
4358           }
4359         }
4360       }
4361     }
4362
4363     scroll_x = SCROLL_POSITION_X(start_x);
4364     scroll_y = SCROLL_POSITION_Y(start_y);
4365   }
4366   else
4367   {
4368     scroll_x = SCROLL_POSITION_X(local_player->jx);
4369     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4370   }
4371
4372   // !!! FIX THIS (START) !!!
4373   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4374   {
4375     InitGameEngine_EM();
4376   }
4377   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4378   {
4379     InitGameEngine_SP();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4382   {
4383     InitGameEngine_MM();
4384   }
4385   else
4386   {
4387     DrawLevel(REDRAW_FIELD);
4388     DrawAllPlayers();
4389
4390     // after drawing the level, correct some elements
4391     if (game.timegate_time_left == 0)
4392       CloseAllOpenTimegates();
4393   }
4394
4395   // blit playfield from scroll buffer to normal back buffer for fading in
4396   BlitScreenToBitmap(backbuffer);
4397   // !!! FIX THIS (END) !!!
4398
4399   DrawMaskedBorder(fade_mask);
4400
4401   FadeIn(fade_mask);
4402
4403 #if 1
4404   // full screen redraw is required at this point in the following cases:
4405   // - special editor door undrawn when game was started from level editor
4406   // - drawing area (playfield) was changed and has to be removed completely
4407   redraw_mask = REDRAW_ALL;
4408   BackToFront();
4409 #endif
4410
4411   if (!game.restart_level)
4412   {
4413     // copy default game door content to main double buffer
4414
4415     // !!! CHECK AGAIN !!!
4416     SetPanelBackground();
4417     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4418     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4419   }
4420
4421   SetPanelBackground();
4422   SetDrawBackgroundMask(REDRAW_DOOR_1);
4423
4424   UpdateAndDisplayGameControlValues();
4425
4426   if (!game.restart_level)
4427   {
4428     UnmapGameButtons();
4429     UnmapTapeButtons();
4430
4431     FreeGameButtons();
4432     CreateGameButtons();
4433
4434     MapGameButtons();
4435     MapTapeButtons();
4436
4437     // copy actual game door content to door double buffer for OpenDoor()
4438     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4439
4440     OpenDoor(DOOR_OPEN_ALL);
4441
4442     KeyboardAutoRepeatOffUnlessAutoplay();
4443
4444 #if DEBUG_INIT_PLAYER
4445     DebugPrintPlayerStatus("Player status (final)");
4446 #endif
4447   }
4448
4449   UnmapAllGadgets();
4450
4451   MapGameButtons();
4452   MapTapeButtons();
4453
4454   if (!game.restart_level && !tape.playing)
4455   {
4456     LevelStats_incPlayed(level_nr);
4457
4458     SaveLevelSetup_SeriesInfo();
4459   }
4460
4461   game.restart_level = FALSE;
4462   game.restart_game_message = NULL;
4463
4464   game.request_active = FALSE;
4465   game.request_active_or_moving = FALSE;
4466
4467   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4468     InitGameActions_MM();
4469
4470   SaveEngineSnapshotToListInitial();
4471
4472   if (!game.restart_level)
4473   {
4474     PlaySound(SND_GAME_STARTING);
4475
4476     if (setup.sound_music)
4477       PlayLevelMusic();
4478   }
4479 }
4480
4481 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4482                         int actual_player_x, int actual_player_y)
4483 {
4484   // this is used for non-R'n'D game engines to update certain engine values
4485
4486   // needed to determine if sounds are played within the visible screen area
4487   scroll_x = actual_scroll_x;
4488   scroll_y = actual_scroll_y;
4489
4490   // needed to get player position for "follow finger" playing input method
4491   local_player->jx = actual_player_x;
4492   local_player->jy = actual_player_y;
4493 }
4494
4495 void InitMovDir(int x, int y)
4496 {
4497   int i, element = Tile[x][y];
4498   static int xy[4][2] =
4499   {
4500     {  0, +1 },
4501     { +1,  0 },
4502     {  0, -1 },
4503     { -1,  0 }
4504   };
4505   static int direction[3][4] =
4506   {
4507     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4508     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4509     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4510   };
4511
4512   switch (element)
4513   {
4514     case EL_BUG_RIGHT:
4515     case EL_BUG_UP:
4516     case EL_BUG_LEFT:
4517     case EL_BUG_DOWN:
4518       Tile[x][y] = EL_BUG;
4519       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4520       break;
4521
4522     case EL_SPACESHIP_RIGHT:
4523     case EL_SPACESHIP_UP:
4524     case EL_SPACESHIP_LEFT:
4525     case EL_SPACESHIP_DOWN:
4526       Tile[x][y] = EL_SPACESHIP;
4527       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4528       break;
4529
4530     case EL_BD_BUTTERFLY_RIGHT:
4531     case EL_BD_BUTTERFLY_UP:
4532     case EL_BD_BUTTERFLY_LEFT:
4533     case EL_BD_BUTTERFLY_DOWN:
4534       Tile[x][y] = EL_BD_BUTTERFLY;
4535       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4536       break;
4537
4538     case EL_BD_FIREFLY_RIGHT:
4539     case EL_BD_FIREFLY_UP:
4540     case EL_BD_FIREFLY_LEFT:
4541     case EL_BD_FIREFLY_DOWN:
4542       Tile[x][y] = EL_BD_FIREFLY;
4543       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4544       break;
4545
4546     case EL_PACMAN_RIGHT:
4547     case EL_PACMAN_UP:
4548     case EL_PACMAN_LEFT:
4549     case EL_PACMAN_DOWN:
4550       Tile[x][y] = EL_PACMAN;
4551       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4552       break;
4553
4554     case EL_YAMYAM_LEFT:
4555     case EL_YAMYAM_RIGHT:
4556     case EL_YAMYAM_UP:
4557     case EL_YAMYAM_DOWN:
4558       Tile[x][y] = EL_YAMYAM;
4559       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4560       break;
4561
4562     case EL_SP_SNIKSNAK:
4563       MovDir[x][y] = MV_UP;
4564       break;
4565
4566     case EL_SP_ELECTRON:
4567       MovDir[x][y] = MV_LEFT;
4568       break;
4569
4570     case EL_MOLE_LEFT:
4571     case EL_MOLE_RIGHT:
4572     case EL_MOLE_UP:
4573     case EL_MOLE_DOWN:
4574       Tile[x][y] = EL_MOLE;
4575       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4576       break;
4577
4578     case EL_SPRING_LEFT:
4579     case EL_SPRING_RIGHT:
4580       Tile[x][y] = EL_SPRING;
4581       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4582       break;
4583
4584     default:
4585       if (IS_CUSTOM_ELEMENT(element))
4586       {
4587         struct ElementInfo *ei = &element_info[element];
4588         int move_direction_initial = ei->move_direction_initial;
4589         int move_pattern = ei->move_pattern;
4590
4591         if (move_direction_initial == MV_START_PREVIOUS)
4592         {
4593           if (MovDir[x][y] != MV_NONE)
4594             return;
4595
4596           move_direction_initial = MV_START_AUTOMATIC;
4597         }
4598
4599         if (move_direction_initial == MV_START_RANDOM)
4600           MovDir[x][y] = 1 << RND(4);
4601         else if (move_direction_initial & MV_ANY_DIRECTION)
4602           MovDir[x][y] = move_direction_initial;
4603         else if (move_pattern == MV_ALL_DIRECTIONS ||
4604                  move_pattern == MV_TURNING_LEFT ||
4605                  move_pattern == MV_TURNING_RIGHT ||
4606                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4607                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4608                  move_pattern == MV_TURNING_RANDOM)
4609           MovDir[x][y] = 1 << RND(4);
4610         else if (move_pattern == MV_HORIZONTAL)
4611           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4612         else if (move_pattern == MV_VERTICAL)
4613           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4614         else if (move_pattern & MV_ANY_DIRECTION)
4615           MovDir[x][y] = element_info[element].move_pattern;
4616         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4617                  move_pattern == MV_ALONG_RIGHT_SIDE)
4618         {
4619           // use random direction as default start direction
4620           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4621             MovDir[x][y] = 1 << RND(4);
4622
4623           for (i = 0; i < NUM_DIRECTIONS; i++)
4624           {
4625             int x1 = x + xy[i][0];
4626             int y1 = y + xy[i][1];
4627
4628             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4629             {
4630               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4631                 MovDir[x][y] = direction[0][i];
4632               else
4633                 MovDir[x][y] = direction[1][i];
4634
4635               break;
4636             }
4637           }
4638         }                
4639       }
4640       else
4641       {
4642         MovDir[x][y] = 1 << RND(4);
4643
4644         if (element != EL_BUG &&
4645             element != EL_SPACESHIP &&
4646             element != EL_BD_BUTTERFLY &&
4647             element != EL_BD_FIREFLY)
4648           break;
4649
4650         for (i = 0; i < NUM_DIRECTIONS; i++)
4651         {
4652           int x1 = x + xy[i][0];
4653           int y1 = y + xy[i][1];
4654
4655           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4656           {
4657             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4658             {
4659               MovDir[x][y] = direction[0][i];
4660               break;
4661             }
4662             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4663                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4664             {
4665               MovDir[x][y] = direction[1][i];
4666               break;
4667             }
4668           }
4669         }
4670       }
4671       break;
4672   }
4673
4674   GfxDir[x][y] = MovDir[x][y];
4675 }
4676
4677 void InitAmoebaNr(int x, int y)
4678 {
4679   int i;
4680   int group_nr = AmoebaNeighbourNr(x, y);
4681
4682   if (group_nr == 0)
4683   {
4684     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4685     {
4686       if (AmoebaCnt[i] == 0)
4687       {
4688         group_nr = i;
4689         break;
4690       }
4691     }
4692   }
4693
4694   AmoebaNr[x][y] = group_nr;
4695   AmoebaCnt[group_nr]++;
4696   AmoebaCnt2[group_nr]++;
4697 }
4698
4699 static void LevelSolved_SetFinalGameValues(void)
4700 {
4701   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4702   game.score_time_final = (level.use_step_counter ? TimePlayed :
4703                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4704
4705   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4706                       game_em.lev->score :
4707                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4708                       game_mm.score :
4709                       game.score);
4710
4711   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4712                        MM_HEALTH(game_mm.laser_overload_value) :
4713                        game.health);
4714
4715   game.LevelSolved_CountingTime = game.time_final;
4716   game.LevelSolved_CountingScore = game.score_final;
4717   game.LevelSolved_CountingHealth = game.health_final;
4718 }
4719
4720 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4721 {
4722   game.LevelSolved_CountingTime = time;
4723   game.LevelSolved_CountingScore = score;
4724   game.LevelSolved_CountingHealth = health;
4725
4726   game_panel_controls[GAME_PANEL_TIME].value = time;
4727   game_panel_controls[GAME_PANEL_SCORE].value = score;
4728   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4729
4730   DisplayGameControlValues();
4731 }
4732
4733 static void LevelSolved(void)
4734 {
4735   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4736       game.players_still_needed > 0)
4737     return;
4738
4739   game.LevelSolved = TRUE;
4740   game.GameOver = TRUE;
4741
4742   // needed here to display correct panel values while player walks into exit
4743   LevelSolved_SetFinalGameValues();
4744 }
4745
4746 void GameWon(void)
4747 {
4748   static int time_count_steps;
4749   static int time, time_final;
4750   static float score, score_final; // needed for time score < 10 for 10 seconds
4751   static int health, health_final;
4752   static int game_over_delay_1 = 0;
4753   static int game_over_delay_2 = 0;
4754   static int game_over_delay_3 = 0;
4755   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4756   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4757
4758   if (!game.LevelSolved_GameWon)
4759   {
4760     int i;
4761
4762     // do not start end game actions before the player stops moving (to exit)
4763     if (local_player->active && local_player->MovPos)
4764       return;
4765
4766     // calculate final game values after player finished walking into exit
4767     LevelSolved_SetFinalGameValues();
4768
4769     game.LevelSolved_GameWon = TRUE;
4770     game.LevelSolved_SaveTape = tape.recording;
4771     game.LevelSolved_SaveScore = !tape.playing;
4772
4773     if (!tape.playing)
4774     {
4775       LevelStats_incSolved(level_nr);
4776
4777       SaveLevelSetup_SeriesInfo();
4778     }
4779
4780     if (tape.auto_play)         // tape might already be stopped here
4781       tape.auto_play_level_solved = TRUE;
4782
4783     TapeStop();
4784
4785     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4786     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4787     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4788
4789     time = time_final = game.time_final;
4790     score = score_final = game.score_final;
4791     health = health_final = game.health_final;
4792
4793     // update game panel values before (delayed) counting of score (if any)
4794     LevelSolved_DisplayFinalGameValues(time, score, health);
4795
4796     // if level has time score defined, calculate new final game values
4797     if (time_score > 0)
4798     {
4799       int time_final_max = 999;
4800       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4801       int time_frames = 0;
4802       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4803       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4804
4805       if (TimeLeft > 0)
4806       {
4807         time_final = 0;
4808         time_frames = time_frames_left;
4809       }
4810       else if (game.no_time_limit && TimePlayed < time_final_max)
4811       {
4812         time_final = time_final_max;
4813         time_frames = time_frames_final_max - time_frames_played;
4814       }
4815
4816       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4817
4818       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4819
4820       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4821       {
4822         health_final = 0;
4823         score_final += health * time_score;
4824       }
4825
4826       game.score_final = score_final;
4827       game.health_final = health_final;
4828     }
4829
4830     // if not counting score after game, immediately update game panel values
4831     if (level_editor_test_game || !setup.count_score_after_game)
4832     {
4833       time = time_final;
4834       score = score_final;
4835
4836       LevelSolved_DisplayFinalGameValues(time, score, health);
4837     }
4838
4839     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4840     {
4841       // check if last player has left the level
4842       if (game.exit_x >= 0 &&
4843           game.exit_y >= 0)
4844       {
4845         int x = game.exit_x;
4846         int y = game.exit_y;
4847         int element = Tile[x][y];
4848
4849         // close exit door after last player
4850         if ((game.all_players_gone &&
4851              (element == EL_EXIT_OPEN ||
4852               element == EL_SP_EXIT_OPEN ||
4853               element == EL_STEEL_EXIT_OPEN)) ||
4854             element == EL_EM_EXIT_OPEN ||
4855             element == EL_EM_STEEL_EXIT_OPEN)
4856         {
4857
4858           Tile[x][y] =
4859             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4860              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4861              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4862              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4863              EL_EM_STEEL_EXIT_CLOSING);
4864
4865           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4866         }
4867
4868         // player disappears
4869         DrawLevelField(x, y);
4870       }
4871
4872       for (i = 0; i < MAX_PLAYERS; i++)
4873       {
4874         struct PlayerInfo *player = &stored_player[i];
4875
4876         if (player->present)
4877         {
4878           RemovePlayer(player);
4879
4880           // player disappears
4881           DrawLevelField(player->jx, player->jy);
4882         }
4883       }
4884     }
4885
4886     PlaySound(SND_GAME_WINNING);
4887   }
4888
4889   if (setup.count_score_after_game)
4890   {
4891     if (time != time_final)
4892     {
4893       if (game_over_delay_1 > 0)
4894       {
4895         game_over_delay_1--;
4896
4897         return;
4898       }
4899
4900       int time_to_go = ABS(time_final - time);
4901       int time_count_dir = (time < time_final ? +1 : -1);
4902
4903       if (time_to_go < time_count_steps)
4904         time_count_steps = 1;
4905
4906       time  += time_count_steps * time_count_dir;
4907       score += time_count_steps * time_score;
4908
4909       // set final score to correct rounding differences after counting score
4910       if (time == time_final)
4911         score = score_final;
4912
4913       LevelSolved_DisplayFinalGameValues(time, score, health);
4914
4915       if (time == time_final)
4916         StopSound(SND_GAME_LEVELTIME_BONUS);
4917       else if (setup.sound_loops)
4918         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4919       else
4920         PlaySound(SND_GAME_LEVELTIME_BONUS);
4921
4922       return;
4923     }
4924
4925     if (health != health_final)
4926     {
4927       if (game_over_delay_2 > 0)
4928       {
4929         game_over_delay_2--;
4930
4931         return;
4932       }
4933
4934       int health_count_dir = (health < health_final ? +1 : -1);
4935
4936       health += health_count_dir;
4937       score  += time_score;
4938
4939       LevelSolved_DisplayFinalGameValues(time, score, health);
4940
4941       if (health == health_final)
4942         StopSound(SND_GAME_LEVELTIME_BONUS);
4943       else if (setup.sound_loops)
4944         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4945       else
4946         PlaySound(SND_GAME_LEVELTIME_BONUS);
4947
4948       return;
4949     }
4950   }
4951
4952   game.panel.active = FALSE;
4953
4954   if (game_over_delay_3 > 0)
4955   {
4956     game_over_delay_3--;
4957
4958     return;
4959   }
4960
4961   GameEnd();
4962 }
4963
4964 void GameEnd(void)
4965 {
4966   // used instead of "level_nr" (needed for network games)
4967   int last_level_nr = levelset.level_nr;
4968
4969   game.LevelSolved_GameEnd = TRUE;
4970
4971   if (game.LevelSolved_SaveTape)
4972   {
4973     // make sure that request dialog to save tape does not open door again
4974     if (!global.use_envelope_request)
4975       CloseDoor(DOOR_CLOSE_1);
4976
4977     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4978
4979     // set unique basename for score tape (also saved in high score table)
4980     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4981   }
4982
4983   // if no tape is to be saved, close both doors simultaneously
4984   CloseDoor(DOOR_CLOSE_ALL);
4985
4986   if (level_editor_test_game)
4987   {
4988     SetGameStatus(GAME_MODE_MAIN);
4989
4990     DrawMainMenu();
4991
4992     return;
4993   }
4994
4995   if (!game.LevelSolved_SaveScore)
4996   {
4997     SetGameStatus(GAME_MODE_MAIN);
4998
4999     DrawMainMenu();
5000
5001     return;
5002   }
5003
5004   if (level_nr == leveldir_current->handicap_level)
5005   {
5006     leveldir_current->handicap_level++;
5007
5008     SaveLevelSetup_SeriesInfo();
5009   }
5010
5011   // save score and score tape before potentially erasing tape below
5012   NewHighScore(last_level_nr);
5013
5014   if (setup.increment_levels &&
5015       level_nr < leveldir_current->last_level &&
5016       !network_playing)
5017   {
5018     level_nr++;         // advance to next level
5019     TapeErase();        // start with empty tape
5020
5021     if (setup.auto_play_next_level)
5022     {
5023       LoadLevel(level_nr);
5024
5025       SaveLevelSetup_SeriesInfo();
5026     }
5027   }
5028
5029   if (scores.last_added >= 0 && setup.show_scores_after_game)
5030   {
5031     SetGameStatus(GAME_MODE_SCORES);
5032
5033     DrawHallOfFame(last_level_nr);
5034   }
5035   else if (setup.auto_play_next_level && setup.increment_levels &&
5036            last_level_nr < leveldir_current->last_level &&
5037            !network_playing)
5038   {
5039     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5040   }
5041   else
5042   {
5043     SetGameStatus(GAME_MODE_MAIN);
5044
5045     DrawMainMenu();
5046   }
5047 }
5048
5049 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5050                          boolean one_score_entry_per_name)
5051 {
5052   int i;
5053
5054   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5055     return -1;
5056
5057   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5058   {
5059     struct ScoreEntry *entry = &list->entry[i];
5060     boolean score_is_better = (new_entry->score >  entry->score);
5061     boolean score_is_equal  = (new_entry->score == entry->score);
5062     boolean time_is_better  = (new_entry->time  <  entry->time);
5063     boolean time_is_equal   = (new_entry->time  == entry->time);
5064     boolean better_by_score = (score_is_better ||
5065                                (score_is_equal && time_is_better));
5066     boolean better_by_time  = (time_is_better ||
5067                                (time_is_equal && score_is_better));
5068     boolean is_better = (level.rate_time_over_score ? better_by_time :
5069                          better_by_score);
5070     boolean entry_is_empty = (entry->score == 0 &&
5071                               entry->time == 0);
5072
5073     // prevent adding server score entries if also existing in local score file
5074     // (special case: historic score entries have an empty tape basename entry)
5075     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5076         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5077       return -1;
5078
5079     if (is_better || entry_is_empty)
5080     {
5081       // player has made it to the hall of fame
5082
5083       if (i < MAX_SCORE_ENTRIES - 1)
5084       {
5085         int m = MAX_SCORE_ENTRIES - 1;
5086         int l;
5087
5088         if (one_score_entry_per_name)
5089         {
5090           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5091             if (strEqual(list->entry[l].name, new_entry->name))
5092               m = l;
5093
5094           if (m == i)   // player's new highscore overwrites his old one
5095             goto put_into_list;
5096         }
5097
5098         for (l = m; l > i; l--)
5099           list->entry[l] = list->entry[l - 1];
5100       }
5101
5102       put_into_list:
5103
5104       *entry = *new_entry;
5105
5106       return i;
5107     }
5108     else if (one_score_entry_per_name &&
5109              strEqual(entry->name, new_entry->name))
5110     {
5111       // player already in high score list with better score or time
5112
5113       return -1;
5114     }
5115   }
5116
5117   return -1;
5118 }
5119
5120 void NewHighScore(int level_nr)
5121 {
5122   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5123   boolean one_per_name = FALSE;
5124
5125   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5126   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5127
5128   new_entry.score = game.score_final;
5129   new_entry.time = game.score_time_final;
5130
5131   LoadScore(level_nr);
5132
5133   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5134
5135   if (scores.last_added >= 0)
5136   {
5137     SaveScore(level_nr);
5138
5139     // store last added local score entry (before merging server scores)
5140     scores.last_added_local = scores.last_added;
5141   }
5142
5143   if (game.LevelSolved_SaveTape)
5144   {
5145     SaveScoreTape(level_nr);
5146     SaveServerScore(level_nr);
5147   }
5148 }
5149
5150 void MergeServerScore(void)
5151 {
5152   struct ScoreEntry last_added_entry;
5153   boolean one_per_name = FALSE;
5154   int i;
5155
5156   if (scores.last_added >= 0)
5157     last_added_entry = scores.entry[scores.last_added];
5158
5159   for (i = 0; i < server_scores.num_entries; i++)
5160   {
5161     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5162
5163     if (pos >= 0 && pos <= scores.last_added)
5164       scores.last_added++;
5165   }
5166
5167   if (scores.last_added >= MAX_SCORE_ENTRIES)
5168   {
5169     scores.last_added = MAX_SCORE_ENTRIES - 1;
5170     scores.force_last_added = TRUE;
5171
5172     scores.entry[scores.last_added] = last_added_entry;
5173   }
5174 }
5175
5176 static int getElementMoveStepsizeExt(int x, int y, int direction)
5177 {
5178   int element = Tile[x][y];
5179   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5180   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5181   int horiz_move = (dx != 0);
5182   int sign = (horiz_move ? dx : dy);
5183   int step = sign * element_info[element].move_stepsize;
5184
5185   // special values for move stepsize for spring and things on conveyor belt
5186   if (horiz_move)
5187   {
5188     if (CAN_FALL(element) &&
5189         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5190       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5191     else if (element == EL_SPRING)
5192       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5193   }
5194
5195   return step;
5196 }
5197
5198 static int getElementMoveStepsize(int x, int y)
5199 {
5200   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5201 }
5202
5203 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5204 {
5205   if (player->GfxAction != action || player->GfxDir != dir)
5206   {
5207     player->GfxAction = action;
5208     player->GfxDir = dir;
5209     player->Frame = 0;
5210     player->StepFrame = 0;
5211   }
5212 }
5213
5214 static void ResetGfxFrame(int x, int y)
5215 {
5216   // profiling showed that "autotest" spends 10~20% of its time in this function
5217   if (DrawingDeactivatedField())
5218     return;
5219
5220   int element = Tile[x][y];
5221   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5222
5223   if (graphic_info[graphic].anim_global_sync)
5224     GfxFrame[x][y] = FrameCounter;
5225   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5226     GfxFrame[x][y] = CustomValue[x][y];
5227   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5228     GfxFrame[x][y] = element_info[element].collect_score;
5229   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5230     GfxFrame[x][y] = ChangeDelay[x][y];
5231 }
5232
5233 static void ResetGfxAnimation(int x, int y)
5234 {
5235   GfxAction[x][y] = ACTION_DEFAULT;
5236   GfxDir[x][y] = MovDir[x][y];
5237   GfxFrame[x][y] = 0;
5238
5239   ResetGfxFrame(x, y);
5240 }
5241
5242 static void ResetRandomAnimationValue(int x, int y)
5243 {
5244   GfxRandom[x][y] = INIT_GFX_RANDOM();
5245 }
5246
5247 static void InitMovingField(int x, int y, int direction)
5248 {
5249   int element = Tile[x][y];
5250   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5251   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5252   int newx = x + dx;
5253   int newy = y + dy;
5254   boolean is_moving_before, is_moving_after;
5255
5256   // check if element was/is moving or being moved before/after mode change
5257   is_moving_before = (WasJustMoving[x][y] != 0);
5258   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5259
5260   // reset animation only for moving elements which change direction of moving
5261   // or which just started or stopped moving
5262   // (else CEs with property "can move" / "not moving" are reset each frame)
5263   if (is_moving_before != is_moving_after ||
5264       direction != MovDir[x][y])
5265     ResetGfxAnimation(x, y);
5266
5267   MovDir[x][y] = direction;
5268   GfxDir[x][y] = direction;
5269
5270   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5271                      direction == MV_DOWN && CAN_FALL(element) ?
5272                      ACTION_FALLING : ACTION_MOVING);
5273
5274   // this is needed for CEs with property "can move" / "not moving"
5275
5276   if (is_moving_after)
5277   {
5278     if (Tile[newx][newy] == EL_EMPTY)
5279       Tile[newx][newy] = EL_BLOCKED;
5280
5281     MovDir[newx][newy] = MovDir[x][y];
5282
5283     CustomValue[newx][newy] = CustomValue[x][y];
5284
5285     GfxFrame[newx][newy] = GfxFrame[x][y];
5286     GfxRandom[newx][newy] = GfxRandom[x][y];
5287     GfxAction[newx][newy] = GfxAction[x][y];
5288     GfxDir[newx][newy] = GfxDir[x][y];
5289   }
5290 }
5291
5292 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5293 {
5294   int direction = MovDir[x][y];
5295   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5296   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5297
5298   *goes_to_x = newx;
5299   *goes_to_y = newy;
5300 }
5301
5302 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5303 {
5304   int oldx = x, oldy = y;
5305   int direction = MovDir[x][y];
5306
5307   if (direction == MV_LEFT)
5308     oldx++;
5309   else if (direction == MV_RIGHT)
5310     oldx--;
5311   else if (direction == MV_UP)
5312     oldy++;
5313   else if (direction == MV_DOWN)
5314     oldy--;
5315
5316   *comes_from_x = oldx;
5317   *comes_from_y = oldy;
5318 }
5319
5320 static int MovingOrBlocked2Element(int x, int y)
5321 {
5322   int element = Tile[x][y];
5323
5324   if (element == EL_BLOCKED)
5325   {
5326     int oldx, oldy;
5327
5328     Blocked2Moving(x, y, &oldx, &oldy);
5329     return Tile[oldx][oldy];
5330   }
5331   else
5332     return element;
5333 }
5334
5335 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5336 {
5337   // like MovingOrBlocked2Element(), but if element is moving
5338   // and (x,y) is the field the moving element is just leaving,
5339   // return EL_BLOCKED instead of the element value
5340   int element = Tile[x][y];
5341
5342   if (IS_MOVING(x, y))
5343   {
5344     if (element == EL_BLOCKED)
5345     {
5346       int oldx, oldy;
5347
5348       Blocked2Moving(x, y, &oldx, &oldy);
5349       return Tile[oldx][oldy];
5350     }
5351     else
5352       return EL_BLOCKED;
5353   }
5354   else
5355     return element;
5356 }
5357
5358 static void RemoveField(int x, int y)
5359 {
5360   Tile[x][y] = EL_EMPTY;
5361
5362   MovPos[x][y] = 0;
5363   MovDir[x][y] = 0;
5364   MovDelay[x][y] = 0;
5365
5366   CustomValue[x][y] = 0;
5367
5368   AmoebaNr[x][y] = 0;
5369   ChangeDelay[x][y] = 0;
5370   ChangePage[x][y] = -1;
5371   Pushed[x][y] = FALSE;
5372
5373   GfxElement[x][y] = EL_UNDEFINED;
5374   GfxAction[x][y] = ACTION_DEFAULT;
5375   GfxDir[x][y] = MV_NONE;
5376 }
5377
5378 static void RemoveMovingField(int x, int y)
5379 {
5380   int oldx = x, oldy = y, newx = x, newy = y;
5381   int element = Tile[x][y];
5382   int next_element = EL_UNDEFINED;
5383
5384   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5385     return;
5386
5387   if (IS_MOVING(x, y))
5388   {
5389     Moving2Blocked(x, y, &newx, &newy);
5390
5391     if (Tile[newx][newy] != EL_BLOCKED)
5392     {
5393       // element is moving, but target field is not free (blocked), but
5394       // already occupied by something different (example: acid pool);
5395       // in this case, only remove the moving field, but not the target
5396
5397       RemoveField(oldx, oldy);
5398
5399       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401       TEST_DrawLevelField(oldx, oldy);
5402
5403       return;
5404     }
5405   }
5406   else if (element == EL_BLOCKED)
5407   {
5408     Blocked2Moving(x, y, &oldx, &oldy);
5409     if (!IS_MOVING(oldx, oldy))
5410       return;
5411   }
5412
5413   if (element == EL_BLOCKED &&
5414       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5415        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5416        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5417        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5418        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5419        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5420     next_element = get_next_element(Tile[oldx][oldy]);
5421
5422   RemoveField(oldx, oldy);
5423   RemoveField(newx, newy);
5424
5425   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5426
5427   if (next_element != EL_UNDEFINED)
5428     Tile[oldx][oldy] = next_element;
5429
5430   TEST_DrawLevelField(oldx, oldy);
5431   TEST_DrawLevelField(newx, newy);
5432 }
5433
5434 void DrawDynamite(int x, int y)
5435 {
5436   int sx = SCREENX(x), sy = SCREENY(y);
5437   int graphic = el2img(Tile[x][y]);
5438   int frame;
5439
5440   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5441     return;
5442
5443   if (IS_WALKABLE_INSIDE(Back[x][y]))
5444     return;
5445
5446   if (Back[x][y])
5447     DrawLevelElement(x, y, Back[x][y]);
5448   else if (Store[x][y])
5449     DrawLevelElement(x, y, Store[x][y]);
5450   else if (game.use_masked_elements)
5451     DrawLevelElement(x, y, EL_EMPTY);
5452
5453   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5454
5455   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5456     DrawGraphicThruMask(sx, sy, graphic, frame);
5457   else
5458     DrawGraphic(sx, sy, graphic, frame);
5459 }
5460
5461 static void CheckDynamite(int x, int y)
5462 {
5463   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5464   {
5465     MovDelay[x][y]--;
5466
5467     if (MovDelay[x][y] != 0)
5468     {
5469       DrawDynamite(x, y);
5470       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5471
5472       return;
5473     }
5474   }
5475
5476   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5477
5478   Bang(x, y);
5479 }
5480
5481 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5482 {
5483   boolean num_checked_players = 0;
5484   int i;
5485
5486   for (i = 0; i < MAX_PLAYERS; i++)
5487   {
5488     if (stored_player[i].active)
5489     {
5490       int sx = stored_player[i].jx;
5491       int sy = stored_player[i].jy;
5492
5493       if (num_checked_players == 0)
5494       {
5495         *sx1 = *sx2 = sx;
5496         *sy1 = *sy2 = sy;
5497       }
5498       else
5499       {
5500         *sx1 = MIN(*sx1, sx);
5501         *sy1 = MIN(*sy1, sy);
5502         *sx2 = MAX(*sx2, sx);
5503         *sy2 = MAX(*sy2, sy);
5504       }
5505
5506       num_checked_players++;
5507     }
5508   }
5509 }
5510
5511 static boolean checkIfAllPlayersFitToScreen_RND(void)
5512 {
5513   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5514
5515   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5516
5517   return (sx2 - sx1 < SCR_FIELDX &&
5518           sy2 - sy1 < SCR_FIELDY);
5519 }
5520
5521 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5522 {
5523   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5524
5525   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5526
5527   *sx = (sx1 + sx2) / 2;
5528   *sy = (sy1 + sy2) / 2;
5529 }
5530
5531 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5532                                boolean center_screen, boolean quick_relocation)
5533 {
5534   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5535   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5536   boolean no_delay = (tape.warp_forward);
5537   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5538   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5539   int new_scroll_x, new_scroll_y;
5540
5541   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5542   {
5543     // case 1: quick relocation inside visible screen (without scrolling)
5544
5545     RedrawPlayfield();
5546
5547     return;
5548   }
5549
5550   if (!level.shifted_relocation || center_screen)
5551   {
5552     // relocation _with_ centering of screen
5553
5554     new_scroll_x = SCROLL_POSITION_X(x);
5555     new_scroll_y = SCROLL_POSITION_Y(y);
5556   }
5557   else
5558   {
5559     // relocation _without_ centering of screen
5560
5561     int center_scroll_x = SCROLL_POSITION_X(old_x);
5562     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5563     int offset_x = x + (scroll_x - center_scroll_x);
5564     int offset_y = y + (scroll_y - center_scroll_y);
5565
5566     // for new screen position, apply previous offset to center position
5567     new_scroll_x = SCROLL_POSITION_X(offset_x);
5568     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5569   }
5570
5571   if (quick_relocation)
5572   {
5573     // case 2: quick relocation (redraw without visible scrolling)
5574
5575     scroll_x = new_scroll_x;
5576     scroll_y = new_scroll_y;
5577
5578     RedrawPlayfield();
5579
5580     return;
5581   }
5582
5583   // case 3: visible relocation (with scrolling to new position)
5584
5585   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5586
5587   SetVideoFrameDelay(wait_delay_value);
5588
5589   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5590   {
5591     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5592     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5593
5594     if (dx == 0 && dy == 0)             // no scrolling needed at all
5595       break;
5596
5597     scroll_x -= dx;
5598     scroll_y -= dy;
5599
5600     // set values for horizontal/vertical screen scrolling (half tile size)
5601     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5602     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5603     int pos_x = dx * TILEX / 2;
5604     int pos_y = dy * TILEY / 2;
5605     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5606     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5607
5608     ScrollLevel(dx, dy);
5609     DrawAllPlayers();
5610
5611     // scroll in two steps of half tile size to make things smoother
5612     BlitScreenToBitmapExt_RND(window, fx, fy);
5613
5614     // scroll second step to align at full tile size
5615     BlitScreenToBitmap(window);
5616   }
5617
5618   DrawAllPlayers();
5619   BackToFront();
5620
5621   SetVideoFrameDelay(frame_delay_value_old);
5622 }
5623
5624 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5625 {
5626   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5627   int player_nr = GET_PLAYER_NR(el_player);
5628   struct PlayerInfo *player = &stored_player[player_nr];
5629   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5630   boolean no_delay = (tape.warp_forward);
5631   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5632   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5633   int old_jx = player->jx;
5634   int old_jy = player->jy;
5635   int old_element = Tile[old_jx][old_jy];
5636   int element = Tile[jx][jy];
5637   boolean player_relocated = (old_jx != jx || old_jy != jy);
5638
5639   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5640   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5641   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5642   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5643   int leave_side_horiz = move_dir_horiz;
5644   int leave_side_vert  = move_dir_vert;
5645   int enter_side = enter_side_horiz | enter_side_vert;
5646   int leave_side = leave_side_horiz | leave_side_vert;
5647
5648   if (player->buried)           // do not reanimate dead player
5649     return;
5650
5651   if (!player_relocated)        // no need to relocate the player
5652     return;
5653
5654   if (IS_PLAYER(jx, jy))        // player already placed at new position
5655   {
5656     RemoveField(jx, jy);        // temporarily remove newly placed player
5657     DrawLevelField(jx, jy);
5658   }
5659
5660   if (player->present)
5661   {
5662     while (player->MovPos)
5663     {
5664       ScrollPlayer(player, SCROLL_GO_ON);
5665       ScrollScreen(NULL, SCROLL_GO_ON);
5666
5667       AdvanceFrameAndPlayerCounters(player->index_nr);
5668
5669       DrawPlayer(player);
5670
5671       BackToFront_WithFrameDelay(wait_delay_value);
5672     }
5673
5674     DrawPlayer(player);         // needed here only to cleanup last field
5675     DrawLevelField(player->jx, player->jy);     // remove player graphic
5676
5677     player->is_moving = FALSE;
5678   }
5679
5680   if (IS_CUSTOM_ELEMENT(old_element))
5681     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5682                                CE_LEFT_BY_PLAYER,
5683                                player->index_bit, leave_side);
5684
5685   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5686                                       CE_PLAYER_LEAVES_X,
5687                                       player->index_bit, leave_side);
5688
5689   Tile[jx][jy] = el_player;
5690   InitPlayerField(jx, jy, el_player, TRUE);
5691
5692   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5693      possible that the relocation target field did not contain a player element,
5694      but a walkable element, to which the new player was relocated -- in this
5695      case, restore that (already initialized!) element on the player field */
5696   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5697   {
5698     Tile[jx][jy] = element;     // restore previously existing element
5699   }
5700
5701   // only visually relocate centered player
5702   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5703                      FALSE, level.instant_relocation);
5704
5705   TestIfPlayerTouchesBadThing(jx, jy);
5706   TestIfPlayerTouchesCustomElement(jx, jy);
5707
5708   if (IS_CUSTOM_ELEMENT(element))
5709     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5710                                player->index_bit, enter_side);
5711
5712   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5713                                       player->index_bit, enter_side);
5714
5715   if (player->is_switching)
5716   {
5717     /* ensure that relocation while still switching an element does not cause
5718        a new element to be treated as also switched directly after relocation
5719        (this is important for teleporter switches that teleport the player to
5720        a place where another teleporter switch is in the same direction, which
5721        would then incorrectly be treated as immediately switched before the
5722        direction key that caused the switch was released) */
5723
5724     player->switch_x += jx - old_jx;
5725     player->switch_y += jy - old_jy;
5726   }
5727 }
5728
5729 static void Explode(int ex, int ey, int phase, int mode)
5730 {
5731   int x, y;
5732   int last_phase;
5733   int border_element;
5734
5735   // !!! eliminate this variable !!!
5736   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5737
5738   if (game.explosions_delayed)
5739   {
5740     ExplodeField[ex][ey] = mode;
5741     return;
5742   }
5743
5744   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5745   {
5746     int center_element = Tile[ex][ey];
5747     int artwork_element, explosion_element;     // set these values later
5748
5749     // remove things displayed in background while burning dynamite
5750     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5751       Back[ex][ey] = 0;
5752
5753     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5754     {
5755       // put moving element to center field (and let it explode there)
5756       center_element = MovingOrBlocked2Element(ex, ey);
5757       RemoveMovingField(ex, ey);
5758       Tile[ex][ey] = center_element;
5759     }
5760
5761     // now "center_element" is finally determined -- set related values now
5762     artwork_element = center_element;           // for custom player artwork
5763     explosion_element = center_element;         // for custom player artwork
5764
5765     if (IS_PLAYER(ex, ey))
5766     {
5767       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5768
5769       artwork_element = stored_player[player_nr].artwork_element;
5770
5771       if (level.use_explosion_element[player_nr])
5772       {
5773         explosion_element = level.explosion_element[player_nr];
5774         artwork_element = explosion_element;
5775       }
5776     }
5777
5778     if (mode == EX_TYPE_NORMAL ||
5779         mode == EX_TYPE_CENTER ||
5780         mode == EX_TYPE_CROSS)
5781       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5782
5783     last_phase = element_info[explosion_element].explosion_delay + 1;
5784
5785     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5786     {
5787       int xx = x - ex + 1;
5788       int yy = y - ey + 1;
5789       int element;
5790
5791       if (!IN_LEV_FIELD(x, y) ||
5792           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5793           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5794         continue;
5795
5796       element = Tile[x][y];
5797
5798       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5799       {
5800         element = MovingOrBlocked2Element(x, y);
5801
5802         if (!IS_EXPLOSION_PROOF(element))
5803           RemoveMovingField(x, y);
5804       }
5805
5806       // indestructible elements can only explode in center (but not flames)
5807       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5808                                            mode == EX_TYPE_BORDER)) ||
5809           element == EL_FLAMES)
5810         continue;
5811
5812       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5813          behaviour, for example when touching a yamyam that explodes to rocks
5814          with active deadly shield, a rock is created under the player !!! */
5815       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5816 #if 0
5817       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5818           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5819            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5820 #else
5821       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5822 #endif
5823       {
5824         if (IS_ACTIVE_BOMB(element))
5825         {
5826           // re-activate things under the bomb like gate or penguin
5827           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5828           Back[x][y] = 0;
5829         }
5830
5831         continue;
5832       }
5833
5834       // save walkable background elements while explosion on same tile
5835       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5836           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5837         Back[x][y] = element;
5838
5839       // ignite explodable elements reached by other explosion
5840       if (element == EL_EXPLOSION)
5841         element = Store2[x][y];
5842
5843       if (AmoebaNr[x][y] &&
5844           (element == EL_AMOEBA_FULL ||
5845            element == EL_BD_AMOEBA ||
5846            element == EL_AMOEBA_GROWING))
5847       {
5848         AmoebaCnt[AmoebaNr[x][y]]--;
5849         AmoebaCnt2[AmoebaNr[x][y]]--;
5850       }
5851
5852       RemoveField(x, y);
5853
5854       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5855       {
5856         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5857
5858         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5859
5860         if (PLAYERINFO(ex, ey)->use_murphy)
5861           Store[x][y] = EL_EMPTY;
5862       }
5863
5864       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5865       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5866       else if (IS_PLAYER_ELEMENT(center_element))
5867         Store[x][y] = EL_EMPTY;
5868       else if (center_element == EL_YAMYAM)
5869         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5870       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5871         Store[x][y] = element_info[center_element].content.e[xx][yy];
5872 #if 1
5873       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5874       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5875       // otherwise) -- FIX THIS !!!
5876       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5877         Store[x][y] = element_info[element].content.e[1][1];
5878 #else
5879       else if (!CAN_EXPLODE(element))
5880         Store[x][y] = element_info[element].content.e[1][1];
5881 #endif
5882       else
5883         Store[x][y] = EL_EMPTY;
5884
5885       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5886           center_element == EL_AMOEBA_TO_DIAMOND)
5887         Store2[x][y] = element;
5888
5889       Tile[x][y] = EL_EXPLOSION;
5890       GfxElement[x][y] = artwork_element;
5891
5892       ExplodePhase[x][y] = 1;
5893       ExplodeDelay[x][y] = last_phase;
5894
5895       Stop[x][y] = TRUE;
5896     }
5897
5898     if (center_element == EL_YAMYAM)
5899       game.yamyam_content_nr =
5900         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5901
5902     return;
5903   }
5904
5905   if (Stop[ex][ey])
5906     return;
5907
5908   x = ex;
5909   y = ey;
5910
5911   if (phase == 1)
5912     GfxFrame[x][y] = 0;         // restart explosion animation
5913
5914   last_phase = ExplodeDelay[x][y];
5915
5916   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5917
5918   // this can happen if the player leaves an explosion just in time
5919   if (GfxElement[x][y] == EL_UNDEFINED)
5920     GfxElement[x][y] = EL_EMPTY;
5921
5922   border_element = Store2[x][y];
5923   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5924     border_element = StorePlayer[x][y];
5925
5926   if (phase == element_info[border_element].ignition_delay ||
5927       phase == last_phase)
5928   {
5929     boolean border_explosion = FALSE;
5930
5931     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5932         !PLAYER_EXPLOSION_PROTECTED(x, y))
5933     {
5934       KillPlayerUnlessExplosionProtected(x, y);
5935       border_explosion = TRUE;
5936     }
5937     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5938     {
5939       Tile[x][y] = Store2[x][y];
5940       Store2[x][y] = 0;
5941       Bang(x, y);
5942       border_explosion = TRUE;
5943     }
5944     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5945     {
5946       AmoebaToDiamond(x, y);
5947       Store2[x][y] = 0;
5948       border_explosion = TRUE;
5949     }
5950
5951     // if an element just explodes due to another explosion (chain-reaction),
5952     // do not immediately end the new explosion when it was the last frame of
5953     // the explosion (as it would be done in the following "if"-statement!)
5954     if (border_explosion && phase == last_phase)
5955       return;
5956   }
5957
5958   if (phase == last_phase)
5959   {
5960     int element;
5961
5962     element = Tile[x][y] = Store[x][y];
5963     Store[x][y] = Store2[x][y] = 0;
5964     GfxElement[x][y] = EL_UNDEFINED;
5965
5966     // player can escape from explosions and might therefore be still alive
5967     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5968         element <= EL_PLAYER_IS_EXPLODING_4)
5969     {
5970       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5971       int explosion_element = EL_PLAYER_1 + player_nr;
5972       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5973       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5974
5975       if (level.use_explosion_element[player_nr])
5976         explosion_element = level.explosion_element[player_nr];
5977
5978       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5979                     element_info[explosion_element].content.e[xx][yy]);
5980     }
5981
5982     // restore probably existing indestructible background element
5983     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5984       element = Tile[x][y] = Back[x][y];
5985     Back[x][y] = 0;
5986
5987     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5988     GfxDir[x][y] = MV_NONE;
5989     ChangeDelay[x][y] = 0;
5990     ChangePage[x][y] = -1;
5991
5992     CustomValue[x][y] = 0;
5993
5994     InitField_WithBug2(x, y, FALSE);
5995
5996     TEST_DrawLevelField(x, y);
5997
5998     TestIfElementTouchesCustomElement(x, y);
5999
6000     if (GFX_CRUMBLED(element))
6001       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6002
6003     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6004       StorePlayer[x][y] = 0;
6005
6006     if (IS_PLAYER_ELEMENT(element))
6007       RelocatePlayer(x, y, element);
6008   }
6009   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6010   {
6011     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6012     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6013
6014     if (phase == delay)
6015       TEST_DrawLevelFieldCrumbled(x, y);
6016
6017     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6018     {
6019       DrawLevelElement(x, y, Back[x][y]);
6020       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6021     }
6022     else if (IS_WALKABLE_UNDER(Back[x][y]))
6023     {
6024       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6025       DrawLevelElementThruMask(x, y, Back[x][y]);
6026     }
6027     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6028       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6029   }
6030 }
6031
6032 static void DynaExplode(int ex, int ey)
6033 {
6034   int i, j;
6035   int dynabomb_element = Tile[ex][ey];
6036   int dynabomb_size = 1;
6037   boolean dynabomb_xl = FALSE;
6038   struct PlayerInfo *player;
6039   static int xy[4][2] =
6040   {
6041     { 0, -1 },
6042     { -1, 0 },
6043     { +1, 0 },
6044     { 0, +1 }
6045   };
6046
6047   if (IS_ACTIVE_BOMB(dynabomb_element))
6048   {
6049     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6050     dynabomb_size = player->dynabomb_size;
6051     dynabomb_xl = player->dynabomb_xl;
6052     player->dynabombs_left++;
6053   }
6054
6055   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6056
6057   for (i = 0; i < NUM_DIRECTIONS; i++)
6058   {
6059     for (j = 1; j <= dynabomb_size; j++)
6060     {
6061       int x = ex + j * xy[i][0];
6062       int y = ey + j * xy[i][1];
6063       int element;
6064
6065       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6066         break;
6067
6068       element = Tile[x][y];
6069
6070       // do not restart explosions of fields with active bombs
6071       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6072         continue;
6073
6074       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6075
6076       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6077           !IS_DIGGABLE(element) && !dynabomb_xl)
6078         break;
6079     }
6080   }
6081 }
6082
6083 void Bang(int x, int y)
6084 {
6085   int element = MovingOrBlocked2Element(x, y);
6086   int explosion_type = EX_TYPE_NORMAL;
6087
6088   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6089   {
6090     struct PlayerInfo *player = PLAYERINFO(x, y);
6091
6092     element = Tile[x][y] = player->initial_element;
6093
6094     if (level.use_explosion_element[player->index_nr])
6095     {
6096       int explosion_element = level.explosion_element[player->index_nr];
6097
6098       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6099         explosion_type = EX_TYPE_CROSS;
6100       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6101         explosion_type = EX_TYPE_CENTER;
6102     }
6103   }
6104
6105   switch (element)
6106   {
6107     case EL_BUG:
6108     case EL_SPACESHIP:
6109     case EL_BD_BUTTERFLY:
6110     case EL_BD_FIREFLY:
6111     case EL_YAMYAM:
6112     case EL_DARK_YAMYAM:
6113     case EL_ROBOT:
6114     case EL_PACMAN:
6115     case EL_MOLE:
6116       RaiseScoreElement(element);
6117       break;
6118
6119     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6120     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6121     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6122     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6123     case EL_DYNABOMB_INCREASE_NUMBER:
6124     case EL_DYNABOMB_INCREASE_SIZE:
6125     case EL_DYNABOMB_INCREASE_POWER:
6126       explosion_type = EX_TYPE_DYNA;
6127       break;
6128
6129     case EL_DC_LANDMINE:
6130       explosion_type = EX_TYPE_CENTER;
6131       break;
6132
6133     case EL_PENGUIN:
6134     case EL_LAMP:
6135     case EL_LAMP_ACTIVE:
6136     case EL_AMOEBA_TO_DIAMOND:
6137       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6138         explosion_type = EX_TYPE_CENTER;
6139       break;
6140
6141     default:
6142       if (element_info[element].explosion_type == EXPLODES_CROSS)
6143         explosion_type = EX_TYPE_CROSS;
6144       else if (element_info[element].explosion_type == EXPLODES_1X1)
6145         explosion_type = EX_TYPE_CENTER;
6146       break;
6147   }
6148
6149   if (explosion_type == EX_TYPE_DYNA)
6150     DynaExplode(x, y);
6151   else
6152     Explode(x, y, EX_PHASE_START, explosion_type);
6153
6154   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6155 }
6156
6157 static void SplashAcid(int x, int y)
6158 {
6159   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6160       (!IN_LEV_FIELD(x - 1, y - 2) ||
6161        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6162     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6163
6164   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6165       (!IN_LEV_FIELD(x + 1, y - 2) ||
6166        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6167     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6168
6169   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6170 }
6171
6172 static void InitBeltMovement(void)
6173 {
6174   static int belt_base_element[4] =
6175   {
6176     EL_CONVEYOR_BELT_1_LEFT,
6177     EL_CONVEYOR_BELT_2_LEFT,
6178     EL_CONVEYOR_BELT_3_LEFT,
6179     EL_CONVEYOR_BELT_4_LEFT
6180   };
6181   static int belt_base_active_element[4] =
6182   {
6183     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6184     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6185     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6186     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6187   };
6188
6189   int x, y, i, j;
6190
6191   // set frame order for belt animation graphic according to belt direction
6192   for (i = 0; i < NUM_BELTS; i++)
6193   {
6194     int belt_nr = i;
6195
6196     for (j = 0; j < NUM_BELT_PARTS; j++)
6197     {
6198       int element = belt_base_active_element[belt_nr] + j;
6199       int graphic_1 = el2img(element);
6200       int graphic_2 = el2panelimg(element);
6201
6202       if (game.belt_dir[i] == MV_LEFT)
6203       {
6204         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6205         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6206       }
6207       else
6208       {
6209         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6210         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6211       }
6212     }
6213   }
6214
6215   SCAN_PLAYFIELD(x, y)
6216   {
6217     int element = Tile[x][y];
6218
6219     for (i = 0; i < NUM_BELTS; i++)
6220     {
6221       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6222       {
6223         int e_belt_nr = getBeltNrFromBeltElement(element);
6224         int belt_nr = i;
6225
6226         if (e_belt_nr == belt_nr)
6227         {
6228           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6229
6230           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6231         }
6232       }
6233     }
6234   }
6235 }
6236
6237 static void ToggleBeltSwitch(int x, int y)
6238 {
6239   static int belt_base_element[4] =
6240   {
6241     EL_CONVEYOR_BELT_1_LEFT,
6242     EL_CONVEYOR_BELT_2_LEFT,
6243     EL_CONVEYOR_BELT_3_LEFT,
6244     EL_CONVEYOR_BELT_4_LEFT
6245   };
6246   static int belt_base_active_element[4] =
6247   {
6248     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6249     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6250     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6251     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6252   };
6253   static int belt_base_switch_element[4] =
6254   {
6255     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6256     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6257     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6258     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6259   };
6260   static int belt_move_dir[4] =
6261   {
6262     MV_LEFT,
6263     MV_NONE,
6264     MV_RIGHT,
6265     MV_NONE,
6266   };
6267
6268   int element = Tile[x][y];
6269   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6270   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6271   int belt_dir = belt_move_dir[belt_dir_nr];
6272   int xx, yy, i;
6273
6274   if (!IS_BELT_SWITCH(element))
6275     return;
6276
6277   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6278   game.belt_dir[belt_nr] = belt_dir;
6279
6280   if (belt_dir_nr == 3)
6281     belt_dir_nr = 1;
6282
6283   // set frame order for belt animation graphic according to belt direction
6284   for (i = 0; i < NUM_BELT_PARTS; i++)
6285   {
6286     int element = belt_base_active_element[belt_nr] + i;
6287     int graphic_1 = el2img(element);
6288     int graphic_2 = el2panelimg(element);
6289
6290     if (belt_dir == MV_LEFT)
6291     {
6292       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6293       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6294     }
6295     else
6296     {
6297       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6298       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6299     }
6300   }
6301
6302   SCAN_PLAYFIELD(xx, yy)
6303   {
6304     int element = Tile[xx][yy];
6305
6306     if (IS_BELT_SWITCH(element))
6307     {
6308       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6309
6310       if (e_belt_nr == belt_nr)
6311       {
6312         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6313         TEST_DrawLevelField(xx, yy);
6314       }
6315     }
6316     else if (IS_BELT(element) && belt_dir != MV_NONE)
6317     {
6318       int e_belt_nr = getBeltNrFromBeltElement(element);
6319
6320       if (e_belt_nr == belt_nr)
6321       {
6322         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6323
6324         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6325         TEST_DrawLevelField(xx, yy);
6326       }
6327     }
6328     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6329     {
6330       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6331
6332       if (e_belt_nr == belt_nr)
6333       {
6334         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6335
6336         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6337         TEST_DrawLevelField(xx, yy);
6338       }
6339     }
6340   }
6341 }
6342
6343 static void ToggleSwitchgateSwitch(int x, int y)
6344 {
6345   int xx, yy;
6346
6347   game.switchgate_pos = !game.switchgate_pos;
6348
6349   SCAN_PLAYFIELD(xx, yy)
6350   {
6351     int element = Tile[xx][yy];
6352
6353     if (element == EL_SWITCHGATE_SWITCH_UP)
6354     {
6355       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6356       TEST_DrawLevelField(xx, yy);
6357     }
6358     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6359     {
6360       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6361       TEST_DrawLevelField(xx, yy);
6362     }
6363     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6364     {
6365       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6366       TEST_DrawLevelField(xx, yy);
6367     }
6368     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6369     {
6370       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6371       TEST_DrawLevelField(xx, yy);
6372     }
6373     else if (element == EL_SWITCHGATE_OPEN ||
6374              element == EL_SWITCHGATE_OPENING)
6375     {
6376       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6377
6378       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6379     }
6380     else if (element == EL_SWITCHGATE_CLOSED ||
6381              element == EL_SWITCHGATE_CLOSING)
6382     {
6383       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6384
6385       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6386     }
6387   }
6388 }
6389
6390 static int getInvisibleActiveFromInvisibleElement(int element)
6391 {
6392   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6393           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6394           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6395           element);
6396 }
6397
6398 static int getInvisibleFromInvisibleActiveElement(int element)
6399 {
6400   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6401           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6402           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6403           element);
6404 }
6405
6406 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6407 {
6408   int x, y;
6409
6410   SCAN_PLAYFIELD(x, y)
6411   {
6412     int element = Tile[x][y];
6413
6414     if (element == EL_LIGHT_SWITCH &&
6415         game.light_time_left > 0)
6416     {
6417       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6418       TEST_DrawLevelField(x, y);
6419     }
6420     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6421              game.light_time_left == 0)
6422     {
6423       Tile[x][y] = EL_LIGHT_SWITCH;
6424       TEST_DrawLevelField(x, y);
6425     }
6426     else if (element == EL_EMC_DRIPPER &&
6427              game.light_time_left > 0)
6428     {
6429       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6430       TEST_DrawLevelField(x, y);
6431     }
6432     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6433              game.light_time_left == 0)
6434     {
6435       Tile[x][y] = EL_EMC_DRIPPER;
6436       TEST_DrawLevelField(x, y);
6437     }
6438     else if (element == EL_INVISIBLE_STEELWALL ||
6439              element == EL_INVISIBLE_WALL ||
6440              element == EL_INVISIBLE_SAND)
6441     {
6442       if (game.light_time_left > 0)
6443         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6444
6445       TEST_DrawLevelField(x, y);
6446
6447       // uncrumble neighbour fields, if needed
6448       if (element == EL_INVISIBLE_SAND)
6449         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6450     }
6451     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6452              element == EL_INVISIBLE_WALL_ACTIVE ||
6453              element == EL_INVISIBLE_SAND_ACTIVE)
6454     {
6455       if (game.light_time_left == 0)
6456         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6457
6458       TEST_DrawLevelField(x, y);
6459
6460       // re-crumble neighbour fields, if needed
6461       if (element == EL_INVISIBLE_SAND)
6462         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6463     }
6464   }
6465 }
6466
6467 static void RedrawAllInvisibleElementsForLenses(void)
6468 {
6469   int x, y;
6470
6471   SCAN_PLAYFIELD(x, y)
6472   {
6473     int element = Tile[x][y];
6474
6475     if (element == EL_EMC_DRIPPER &&
6476         game.lenses_time_left > 0)
6477     {
6478       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6479       TEST_DrawLevelField(x, y);
6480     }
6481     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6482              game.lenses_time_left == 0)
6483     {
6484       Tile[x][y] = EL_EMC_DRIPPER;
6485       TEST_DrawLevelField(x, y);
6486     }
6487     else if (element == EL_INVISIBLE_STEELWALL ||
6488              element == EL_INVISIBLE_WALL ||
6489              element == EL_INVISIBLE_SAND)
6490     {
6491       if (game.lenses_time_left > 0)
6492         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6493
6494       TEST_DrawLevelField(x, y);
6495
6496       // uncrumble neighbour fields, if needed
6497       if (element == EL_INVISIBLE_SAND)
6498         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6499     }
6500     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6501              element == EL_INVISIBLE_WALL_ACTIVE ||
6502              element == EL_INVISIBLE_SAND_ACTIVE)
6503     {
6504       if (game.lenses_time_left == 0)
6505         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6506
6507       TEST_DrawLevelField(x, y);
6508
6509       // re-crumble neighbour fields, if needed
6510       if (element == EL_INVISIBLE_SAND)
6511         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6512     }
6513   }
6514 }
6515
6516 static void RedrawAllInvisibleElementsForMagnifier(void)
6517 {
6518   int x, y;
6519
6520   SCAN_PLAYFIELD(x, y)
6521   {
6522     int element = Tile[x][y];
6523
6524     if (element == EL_EMC_FAKE_GRASS &&
6525         game.magnify_time_left > 0)
6526     {
6527       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6528       TEST_DrawLevelField(x, y);
6529     }
6530     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6531              game.magnify_time_left == 0)
6532     {
6533       Tile[x][y] = EL_EMC_FAKE_GRASS;
6534       TEST_DrawLevelField(x, y);
6535     }
6536     else if (IS_GATE_GRAY(element) &&
6537              game.magnify_time_left > 0)
6538     {
6539       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6540                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6541                     IS_EM_GATE_GRAY(element) ?
6542                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6543                     IS_EMC_GATE_GRAY(element) ?
6544                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6545                     IS_DC_GATE_GRAY(element) ?
6546                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6547                     element);
6548       TEST_DrawLevelField(x, y);
6549     }
6550     else if (IS_GATE_GRAY_ACTIVE(element) &&
6551              game.magnify_time_left == 0)
6552     {
6553       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6554                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6555                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6556                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6557                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6558                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6559                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6560                     EL_DC_GATE_WHITE_GRAY :
6561                     element);
6562       TEST_DrawLevelField(x, y);
6563     }
6564   }
6565 }
6566
6567 static void ToggleLightSwitch(int x, int y)
6568 {
6569   int element = Tile[x][y];
6570
6571   game.light_time_left =
6572     (element == EL_LIGHT_SWITCH ?
6573      level.time_light * FRAMES_PER_SECOND : 0);
6574
6575   RedrawAllLightSwitchesAndInvisibleElements();
6576 }
6577
6578 static void ActivateTimegateSwitch(int x, int y)
6579 {
6580   int xx, yy;
6581
6582   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6583
6584   SCAN_PLAYFIELD(xx, yy)
6585   {
6586     int element = Tile[xx][yy];
6587
6588     if (element == EL_TIMEGATE_CLOSED ||
6589         element == EL_TIMEGATE_CLOSING)
6590     {
6591       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6592       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6593     }
6594
6595     /*
6596     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6597     {
6598       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6599       TEST_DrawLevelField(xx, yy);
6600     }
6601     */
6602
6603   }
6604
6605   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6606                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6607 }
6608
6609 static void Impact(int x, int y)
6610 {
6611   boolean last_line = (y == lev_fieldy - 1);
6612   boolean object_hit = FALSE;
6613   boolean impact = (last_line || object_hit);
6614   int element = Tile[x][y];
6615   int smashed = EL_STEELWALL;
6616
6617   if (!last_line)       // check if element below was hit
6618   {
6619     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6620       return;
6621
6622     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6623                                          MovDir[x][y + 1] != MV_DOWN ||
6624                                          MovPos[x][y + 1] <= TILEY / 2));
6625
6626     // do not smash moving elements that left the smashed field in time
6627     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6628         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6629       object_hit = FALSE;
6630
6631 #if USE_QUICKSAND_IMPACT_BUGFIX
6632     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6633     {
6634       RemoveMovingField(x, y + 1);
6635       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6636       Tile[x][y + 2] = EL_ROCK;
6637       TEST_DrawLevelField(x, y + 2);
6638
6639       object_hit = TRUE;
6640     }
6641
6642     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6643     {
6644       RemoveMovingField(x, y + 1);
6645       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6646       Tile[x][y + 2] = EL_ROCK;
6647       TEST_DrawLevelField(x, y + 2);
6648
6649       object_hit = TRUE;
6650     }
6651 #endif
6652
6653     if (object_hit)
6654       smashed = MovingOrBlocked2Element(x, y + 1);
6655
6656     impact = (last_line || object_hit);
6657   }
6658
6659   if (!last_line && smashed == EL_ACID) // element falls into acid
6660   {
6661     SplashAcid(x, y + 1);
6662     return;
6663   }
6664
6665   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6666   // only reset graphic animation if graphic really changes after impact
6667   if (impact &&
6668       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6669   {
6670     ResetGfxAnimation(x, y);
6671     TEST_DrawLevelField(x, y);
6672   }
6673
6674   if (impact && CAN_EXPLODE_IMPACT(element))
6675   {
6676     Bang(x, y);
6677     return;
6678   }
6679   else if (impact && element == EL_PEARL &&
6680            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6681   {
6682     ResetGfxAnimation(x, y);
6683
6684     Tile[x][y] = EL_PEARL_BREAKING;
6685     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6686     return;
6687   }
6688   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6689   {
6690     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6691
6692     return;
6693   }
6694
6695   if (impact && element == EL_AMOEBA_DROP)
6696   {
6697     if (object_hit && IS_PLAYER(x, y + 1))
6698       KillPlayerUnlessEnemyProtected(x, y + 1);
6699     else if (object_hit && smashed == EL_PENGUIN)
6700       Bang(x, y + 1);
6701     else
6702     {
6703       Tile[x][y] = EL_AMOEBA_GROWING;
6704       Store[x][y] = EL_AMOEBA_WET;
6705
6706       ResetRandomAnimationValue(x, y);
6707     }
6708     return;
6709   }
6710
6711   if (object_hit)               // check which object was hit
6712   {
6713     if ((CAN_PASS_MAGIC_WALL(element) && 
6714          (smashed == EL_MAGIC_WALL ||
6715           smashed == EL_BD_MAGIC_WALL)) ||
6716         (CAN_PASS_DC_MAGIC_WALL(element) &&
6717          smashed == EL_DC_MAGIC_WALL))
6718     {
6719       int xx, yy;
6720       int activated_magic_wall =
6721         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6722          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6723          EL_DC_MAGIC_WALL_ACTIVE);
6724
6725       // activate magic wall / mill
6726       SCAN_PLAYFIELD(xx, yy)
6727       {
6728         if (Tile[xx][yy] == smashed)
6729           Tile[xx][yy] = activated_magic_wall;
6730       }
6731
6732       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6733       game.magic_wall_active = TRUE;
6734
6735       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6736                             SND_MAGIC_WALL_ACTIVATING :
6737                             smashed == EL_BD_MAGIC_WALL ?
6738                             SND_BD_MAGIC_WALL_ACTIVATING :
6739                             SND_DC_MAGIC_WALL_ACTIVATING));
6740     }
6741
6742     if (IS_PLAYER(x, y + 1))
6743     {
6744       if (CAN_SMASH_PLAYER(element))
6745       {
6746         KillPlayerUnlessEnemyProtected(x, y + 1);
6747         return;
6748       }
6749     }
6750     else if (smashed == EL_PENGUIN)
6751     {
6752       if (CAN_SMASH_PLAYER(element))
6753       {
6754         Bang(x, y + 1);
6755         return;
6756       }
6757     }
6758     else if (element == EL_BD_DIAMOND)
6759     {
6760       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6761       {
6762         Bang(x, y + 1);
6763         return;
6764       }
6765     }
6766     else if (((element == EL_SP_INFOTRON ||
6767                element == EL_SP_ZONK) &&
6768               (smashed == EL_SP_SNIKSNAK ||
6769                smashed == EL_SP_ELECTRON ||
6770                smashed == EL_SP_DISK_ORANGE)) ||
6771              (element == EL_SP_INFOTRON &&
6772               smashed == EL_SP_DISK_YELLOW))
6773     {
6774       Bang(x, y + 1);
6775       return;
6776     }
6777     else if (CAN_SMASH_EVERYTHING(element))
6778     {
6779       if (IS_CLASSIC_ENEMY(smashed) ||
6780           CAN_EXPLODE_SMASHED(smashed))
6781       {
6782         Bang(x, y + 1);
6783         return;
6784       }
6785       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6786       {
6787         if (smashed == EL_LAMP ||
6788             smashed == EL_LAMP_ACTIVE)
6789         {
6790           Bang(x, y + 1);
6791           return;
6792         }
6793         else if (smashed == EL_NUT)
6794         {
6795           Tile[x][y + 1] = EL_NUT_BREAKING;
6796           PlayLevelSound(x, y, SND_NUT_BREAKING);
6797           RaiseScoreElement(EL_NUT);
6798           return;
6799         }
6800         else if (smashed == EL_PEARL)
6801         {
6802           ResetGfxAnimation(x, y);
6803
6804           Tile[x][y + 1] = EL_PEARL_BREAKING;
6805           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6806           return;
6807         }
6808         else if (smashed == EL_DIAMOND)
6809         {
6810           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6811           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6812           return;
6813         }
6814         else if (IS_BELT_SWITCH(smashed))
6815         {
6816           ToggleBeltSwitch(x, y + 1);
6817         }
6818         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6819                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6820                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6821                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6822         {
6823           ToggleSwitchgateSwitch(x, y + 1);
6824         }
6825         else if (smashed == EL_LIGHT_SWITCH ||
6826                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6827         {
6828           ToggleLightSwitch(x, y + 1);
6829         }
6830         else
6831         {
6832           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6833
6834           CheckElementChangeBySide(x, y + 1, smashed, element,
6835                                    CE_SWITCHED, CH_SIDE_TOP);
6836           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6837                                             CH_SIDE_TOP);
6838         }
6839       }
6840       else
6841       {
6842         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6843       }
6844     }
6845   }
6846
6847   // play sound of magic wall / mill
6848   if (!last_line &&
6849       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6850        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6851        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6852   {
6853     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6854       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6855     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6856       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6857     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6858       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6859
6860     return;
6861   }
6862
6863   // play sound of object that hits the ground
6864   if (last_line || object_hit)
6865     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6866 }
6867
6868 static void TurnRoundExt(int x, int y)
6869 {
6870   static struct
6871   {
6872     int dx, dy;
6873   } move_xy[] =
6874   {
6875     {  0,  0 },
6876     { -1,  0 },
6877     { +1,  0 },
6878     {  0,  0 },
6879     {  0, -1 },
6880     {  0,  0 }, { 0, 0 }, { 0, 0 },
6881     {  0, +1 }
6882   };
6883   static struct
6884   {
6885     int left, right, back;
6886   } turn[] =
6887   {
6888     { 0,        0,              0        },
6889     { MV_DOWN,  MV_UP,          MV_RIGHT },
6890     { MV_UP,    MV_DOWN,        MV_LEFT  },
6891     { 0,        0,              0        },
6892     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6893     { 0,        0,              0        },
6894     { 0,        0,              0        },
6895     { 0,        0,              0        },
6896     { MV_RIGHT, MV_LEFT,        MV_UP    }
6897   };
6898
6899   int element = Tile[x][y];
6900   int move_pattern = element_info[element].move_pattern;
6901
6902   int old_move_dir = MovDir[x][y];
6903   int left_dir  = turn[old_move_dir].left;
6904   int right_dir = turn[old_move_dir].right;
6905   int back_dir  = turn[old_move_dir].back;
6906
6907   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6908   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6909   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6910   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6911
6912   int left_x  = x + left_dx,  left_y  = y + left_dy;
6913   int right_x = x + right_dx, right_y = y + right_dy;
6914   int move_x  = x + move_dx,  move_y  = y + move_dy;
6915
6916   int xx, yy;
6917
6918   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6919   {
6920     TestIfBadThingTouchesOtherBadThing(x, y);
6921
6922     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6923       MovDir[x][y] = right_dir;
6924     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6925       MovDir[x][y] = left_dir;
6926
6927     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6928       MovDelay[x][y] = 9;
6929     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6930       MovDelay[x][y] = 1;
6931   }
6932   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6933   {
6934     TestIfBadThingTouchesOtherBadThing(x, y);
6935
6936     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6937       MovDir[x][y] = left_dir;
6938     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6939       MovDir[x][y] = right_dir;
6940
6941     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6942       MovDelay[x][y] = 9;
6943     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6944       MovDelay[x][y] = 1;
6945   }
6946   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6947   {
6948     TestIfBadThingTouchesOtherBadThing(x, y);
6949
6950     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6951       MovDir[x][y] = left_dir;
6952     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6953       MovDir[x][y] = right_dir;
6954
6955     if (MovDir[x][y] != old_move_dir)
6956       MovDelay[x][y] = 9;
6957   }
6958   else if (element == EL_YAMYAM)
6959   {
6960     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6961     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6962
6963     if (can_turn_left && can_turn_right)
6964       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6965     else if (can_turn_left)
6966       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6967     else if (can_turn_right)
6968       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6969     else
6970       MovDir[x][y] = back_dir;
6971
6972     MovDelay[x][y] = 16 + 16 * RND(3);
6973   }
6974   else if (element == EL_DARK_YAMYAM)
6975   {
6976     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6977                                                          left_x, left_y);
6978     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6979                                                          right_x, right_y);
6980
6981     if (can_turn_left && can_turn_right)
6982       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6983     else if (can_turn_left)
6984       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6985     else if (can_turn_right)
6986       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6987     else
6988       MovDir[x][y] = back_dir;
6989
6990     MovDelay[x][y] = 16 + 16 * RND(3);
6991   }
6992   else if (element == EL_PACMAN)
6993   {
6994     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6995     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6996
6997     if (can_turn_left && can_turn_right)
6998       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6999     else if (can_turn_left)
7000       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7001     else if (can_turn_right)
7002       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7003     else
7004       MovDir[x][y] = back_dir;
7005
7006     MovDelay[x][y] = 6 + RND(40);
7007   }
7008   else if (element == EL_PIG)
7009   {
7010     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7011     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7012     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7013     boolean should_turn_left, should_turn_right, should_move_on;
7014     int rnd_value = 24;
7015     int rnd = RND(rnd_value);
7016
7017     should_turn_left = (can_turn_left &&
7018                         (!can_move_on ||
7019                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7020                                                    y + back_dy + left_dy)));
7021     should_turn_right = (can_turn_right &&
7022                          (!can_move_on ||
7023                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7024                                                     y + back_dy + right_dy)));
7025     should_move_on = (can_move_on &&
7026                       (!can_turn_left ||
7027                        !can_turn_right ||
7028                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7029                                                  y + move_dy + left_dy) ||
7030                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7031                                                  y + move_dy + right_dy)));
7032
7033     if (should_turn_left || should_turn_right || should_move_on)
7034     {
7035       if (should_turn_left && should_turn_right && should_move_on)
7036         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7037                         rnd < 2 * rnd_value / 3 ? right_dir :
7038                         old_move_dir);
7039       else if (should_turn_left && should_turn_right)
7040         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7041       else if (should_turn_left && should_move_on)
7042         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7043       else if (should_turn_right && should_move_on)
7044         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7045       else if (should_turn_left)
7046         MovDir[x][y] = left_dir;
7047       else if (should_turn_right)
7048         MovDir[x][y] = right_dir;
7049       else if (should_move_on)
7050         MovDir[x][y] = old_move_dir;
7051     }
7052     else if (can_move_on && rnd > rnd_value / 8)
7053       MovDir[x][y] = old_move_dir;
7054     else if (can_turn_left && can_turn_right)
7055       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7056     else if (can_turn_left && rnd > rnd_value / 8)
7057       MovDir[x][y] = left_dir;
7058     else if (can_turn_right && rnd > rnd_value/8)
7059       MovDir[x][y] = right_dir;
7060     else
7061       MovDir[x][y] = back_dir;
7062
7063     xx = x + move_xy[MovDir[x][y]].dx;
7064     yy = y + move_xy[MovDir[x][y]].dy;
7065
7066     if (!IN_LEV_FIELD(xx, yy) ||
7067         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7068       MovDir[x][y] = old_move_dir;
7069
7070     MovDelay[x][y] = 0;
7071   }
7072   else if (element == EL_DRAGON)
7073   {
7074     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7075     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7076     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7077     int rnd_value = 24;
7078     int rnd = RND(rnd_value);
7079
7080     if (can_move_on && rnd > rnd_value / 8)
7081       MovDir[x][y] = old_move_dir;
7082     else if (can_turn_left && can_turn_right)
7083       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7084     else if (can_turn_left && rnd > rnd_value / 8)
7085       MovDir[x][y] = left_dir;
7086     else if (can_turn_right && rnd > rnd_value / 8)
7087       MovDir[x][y] = right_dir;
7088     else
7089       MovDir[x][y] = back_dir;
7090
7091     xx = x + move_xy[MovDir[x][y]].dx;
7092     yy = y + move_xy[MovDir[x][y]].dy;
7093
7094     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7095       MovDir[x][y] = old_move_dir;
7096
7097     MovDelay[x][y] = 0;
7098   }
7099   else if (element == EL_MOLE)
7100   {
7101     boolean can_move_on =
7102       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7103                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7104                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7105     if (!can_move_on)
7106     {
7107       boolean can_turn_left =
7108         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7109                               IS_AMOEBOID(Tile[left_x][left_y])));
7110
7111       boolean can_turn_right =
7112         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7113                               IS_AMOEBOID(Tile[right_x][right_y])));
7114
7115       if (can_turn_left && can_turn_right)
7116         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7117       else if (can_turn_left)
7118         MovDir[x][y] = left_dir;
7119       else
7120         MovDir[x][y] = right_dir;
7121     }
7122
7123     if (MovDir[x][y] != old_move_dir)
7124       MovDelay[x][y] = 9;
7125   }
7126   else if (element == EL_BALLOON)
7127   {
7128     MovDir[x][y] = game.wind_direction;
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_SPRING)
7132   {
7133     if (MovDir[x][y] & MV_HORIZONTAL)
7134     {
7135       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7136           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7137       {
7138         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7139         ResetGfxAnimation(move_x, move_y);
7140         TEST_DrawLevelField(move_x, move_y);
7141
7142         MovDir[x][y] = back_dir;
7143       }
7144       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7145                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7146         MovDir[x][y] = MV_NONE;
7147     }
7148
7149     MovDelay[x][y] = 0;
7150   }
7151   else if (element == EL_ROBOT ||
7152            element == EL_SATELLITE ||
7153            element == EL_PENGUIN ||
7154            element == EL_EMC_ANDROID)
7155   {
7156     int attr_x = -1, attr_y = -1;
7157
7158     if (game.all_players_gone)
7159     {
7160       attr_x = game.exit_x;
7161       attr_y = game.exit_y;
7162     }
7163     else
7164     {
7165       int i;
7166
7167       for (i = 0; i < MAX_PLAYERS; i++)
7168       {
7169         struct PlayerInfo *player = &stored_player[i];
7170         int jx = player->jx, jy = player->jy;
7171
7172         if (!player->active)
7173           continue;
7174
7175         if (attr_x == -1 ||
7176             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7177         {
7178           attr_x = jx;
7179           attr_y = jy;
7180         }
7181       }
7182     }
7183
7184     if (element == EL_ROBOT &&
7185         game.robot_wheel_x >= 0 &&
7186         game.robot_wheel_y >= 0 &&
7187         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7188          game.engine_version < VERSION_IDENT(3,1,0,0)))
7189     {
7190       attr_x = game.robot_wheel_x;
7191       attr_y = game.robot_wheel_y;
7192     }
7193
7194     if (element == EL_PENGUIN)
7195     {
7196       int i;
7197       static int xy[4][2] =
7198       {
7199         { 0, -1 },
7200         { -1, 0 },
7201         { +1, 0 },
7202         { 0, +1 }
7203       };
7204
7205       for (i = 0; i < NUM_DIRECTIONS; i++)
7206       {
7207         int ex = x + xy[i][0];
7208         int ey = y + xy[i][1];
7209
7210         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7211                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7212                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7213                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7214         {
7215           attr_x = ex;
7216           attr_y = ey;
7217           break;
7218         }
7219       }
7220     }
7221
7222     MovDir[x][y] = MV_NONE;
7223     if (attr_x < x)
7224       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7225     else if (attr_x > x)
7226       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7227     if (attr_y < y)
7228       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7229     else if (attr_y > y)
7230       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7231
7232     if (element == EL_ROBOT)
7233     {
7234       int newx, newy;
7235
7236       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7237         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7238       Moving2Blocked(x, y, &newx, &newy);
7239
7240       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7241         MovDelay[x][y] = 8 + 8 * !RND(3);
7242       else
7243         MovDelay[x][y] = 16;
7244     }
7245     else if (element == EL_PENGUIN)
7246     {
7247       int newx, newy;
7248
7249       MovDelay[x][y] = 1;
7250
7251       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7252       {
7253         boolean first_horiz = RND(2);
7254         int new_move_dir = MovDir[x][y];
7255
7256         MovDir[x][y] =
7257           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7258         Moving2Blocked(x, y, &newx, &newy);
7259
7260         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7261           return;
7262
7263         MovDir[x][y] =
7264           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7265         Moving2Blocked(x, y, &newx, &newy);
7266
7267         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7268           return;
7269
7270         MovDir[x][y] = old_move_dir;
7271         return;
7272       }
7273     }
7274     else if (element == EL_SATELLITE)
7275     {
7276       int newx, newy;
7277
7278       MovDelay[x][y] = 1;
7279
7280       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7281       {
7282         boolean first_horiz = RND(2);
7283         int new_move_dir = MovDir[x][y];
7284
7285         MovDir[x][y] =
7286           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7287         Moving2Blocked(x, y, &newx, &newy);
7288
7289         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7290           return;
7291
7292         MovDir[x][y] =
7293           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7294         Moving2Blocked(x, y, &newx, &newy);
7295
7296         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7297           return;
7298
7299         MovDir[x][y] = old_move_dir;
7300         return;
7301       }
7302     }
7303     else if (element == EL_EMC_ANDROID)
7304     {
7305       static int check_pos[16] =
7306       {
7307         -1,             //  0 => (invalid)
7308         7,              //  1 => MV_LEFT
7309         3,              //  2 => MV_RIGHT
7310         -1,             //  3 => (invalid)
7311         1,              //  4 =>            MV_UP
7312         0,              //  5 => MV_LEFT  | MV_UP
7313         2,              //  6 => MV_RIGHT | MV_UP
7314         -1,             //  7 => (invalid)
7315         5,              //  8 =>            MV_DOWN
7316         6,              //  9 => MV_LEFT  | MV_DOWN
7317         4,              // 10 => MV_RIGHT | MV_DOWN
7318         -1,             // 11 => (invalid)
7319         -1,             // 12 => (invalid)
7320         -1,             // 13 => (invalid)
7321         -1,             // 14 => (invalid)
7322         -1,             // 15 => (invalid)
7323       };
7324       static struct
7325       {
7326         int dx, dy;
7327         int dir;
7328       } check_xy[8] =
7329       {
7330         { -1, -1,       MV_LEFT  | MV_UP   },
7331         {  0, -1,                  MV_UP   },
7332         { +1, -1,       MV_RIGHT | MV_UP   },
7333         { +1,  0,       MV_RIGHT           },
7334         { +1, +1,       MV_RIGHT | MV_DOWN },
7335         {  0, +1,                  MV_DOWN },
7336         { -1, +1,       MV_LEFT  | MV_DOWN },
7337         { -1,  0,       MV_LEFT            },
7338       };
7339       int start_pos, check_order;
7340       boolean can_clone = FALSE;
7341       int i;
7342
7343       // check if there is any free field around current position
7344       for (i = 0; i < 8; i++)
7345       {
7346         int newx = x + check_xy[i].dx;
7347         int newy = y + check_xy[i].dy;
7348
7349         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7350         {
7351           can_clone = TRUE;
7352
7353           break;
7354         }
7355       }
7356
7357       if (can_clone)            // randomly find an element to clone
7358       {
7359         can_clone = FALSE;
7360
7361         start_pos = check_pos[RND(8)];
7362         check_order = (RND(2) ? -1 : +1);
7363
7364         for (i = 0; i < 8; i++)
7365         {
7366           int pos_raw = start_pos + i * check_order;
7367           int pos = (pos_raw + 8) % 8;
7368           int newx = x + check_xy[pos].dx;
7369           int newy = y + check_xy[pos].dy;
7370
7371           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7372           {
7373             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7374             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7375
7376             Store[x][y] = Tile[newx][newy];
7377
7378             can_clone = TRUE;
7379
7380             break;
7381           }
7382         }
7383       }
7384
7385       if (can_clone)            // randomly find a direction to move
7386       {
7387         can_clone = FALSE;
7388
7389         start_pos = check_pos[RND(8)];
7390         check_order = (RND(2) ? -1 : +1);
7391
7392         for (i = 0; i < 8; i++)
7393         {
7394           int pos_raw = start_pos + i * check_order;
7395           int pos = (pos_raw + 8) % 8;
7396           int newx = x + check_xy[pos].dx;
7397           int newy = y + check_xy[pos].dy;
7398           int new_move_dir = check_xy[pos].dir;
7399
7400           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7401           {
7402             MovDir[x][y] = new_move_dir;
7403             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7404
7405             can_clone = TRUE;
7406
7407             break;
7408           }
7409         }
7410       }
7411
7412       if (can_clone)            // cloning and moving successful
7413         return;
7414
7415       // cannot clone -- try to move towards player
7416
7417       start_pos = check_pos[MovDir[x][y] & 0x0f];
7418       check_order = (RND(2) ? -1 : +1);
7419
7420       for (i = 0; i < 3; i++)
7421       {
7422         // first check start_pos, then previous/next or (next/previous) pos
7423         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7424         int pos = (pos_raw + 8) % 8;
7425         int newx = x + check_xy[pos].dx;
7426         int newy = y + check_xy[pos].dy;
7427         int new_move_dir = check_xy[pos].dir;
7428
7429         if (IS_PLAYER(newx, newy))
7430           break;
7431
7432         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7433         {
7434           MovDir[x][y] = new_move_dir;
7435           MovDelay[x][y] = level.android_move_time * 8 + 1;
7436
7437           break;
7438         }
7439       }
7440     }
7441   }
7442   else if (move_pattern == MV_TURNING_LEFT ||
7443            move_pattern == MV_TURNING_RIGHT ||
7444            move_pattern == MV_TURNING_LEFT_RIGHT ||
7445            move_pattern == MV_TURNING_RIGHT_LEFT ||
7446            move_pattern == MV_TURNING_RANDOM ||
7447            move_pattern == MV_ALL_DIRECTIONS)
7448   {
7449     boolean can_turn_left =
7450       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7451     boolean can_turn_right =
7452       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7453
7454     if (element_info[element].move_stepsize == 0)       // "not moving"
7455       return;
7456
7457     if (move_pattern == MV_TURNING_LEFT)
7458       MovDir[x][y] = left_dir;
7459     else if (move_pattern == MV_TURNING_RIGHT)
7460       MovDir[x][y] = right_dir;
7461     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7462       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7463     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7464       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7465     else if (move_pattern == MV_TURNING_RANDOM)
7466       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7467                       can_turn_right && !can_turn_left ? right_dir :
7468                       RND(2) ? left_dir : right_dir);
7469     else if (can_turn_left && can_turn_right)
7470       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7471     else if (can_turn_left)
7472       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7473     else if (can_turn_right)
7474       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7475     else
7476       MovDir[x][y] = back_dir;
7477
7478     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7479   }
7480   else if (move_pattern == MV_HORIZONTAL ||
7481            move_pattern == MV_VERTICAL)
7482   {
7483     if (move_pattern & old_move_dir)
7484       MovDir[x][y] = back_dir;
7485     else if (move_pattern == MV_HORIZONTAL)
7486       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7487     else if (move_pattern == MV_VERTICAL)
7488       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7489
7490     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7491   }
7492   else if (move_pattern & MV_ANY_DIRECTION)
7493   {
7494     MovDir[x][y] = move_pattern;
7495     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7496   }
7497   else if (move_pattern & MV_WIND_DIRECTION)
7498   {
7499     MovDir[x][y] = game.wind_direction;
7500     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7501   }
7502   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7503   {
7504     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7505       MovDir[x][y] = left_dir;
7506     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7507       MovDir[x][y] = right_dir;
7508
7509     if (MovDir[x][y] != old_move_dir)
7510       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7511   }
7512   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7513   {
7514     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7515       MovDir[x][y] = right_dir;
7516     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7517       MovDir[x][y] = left_dir;
7518
7519     if (MovDir[x][y] != old_move_dir)
7520       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7521   }
7522   else if (move_pattern == MV_TOWARDS_PLAYER ||
7523            move_pattern == MV_AWAY_FROM_PLAYER)
7524   {
7525     int attr_x = -1, attr_y = -1;
7526     int newx, newy;
7527     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7528
7529     if (game.all_players_gone)
7530     {
7531       attr_x = game.exit_x;
7532       attr_y = game.exit_y;
7533     }
7534     else
7535     {
7536       int i;
7537
7538       for (i = 0; i < MAX_PLAYERS; i++)
7539       {
7540         struct PlayerInfo *player = &stored_player[i];
7541         int jx = player->jx, jy = player->jy;
7542
7543         if (!player->active)
7544           continue;
7545
7546         if (attr_x == -1 ||
7547             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7548         {
7549           attr_x = jx;
7550           attr_y = jy;
7551         }
7552       }
7553     }
7554
7555     MovDir[x][y] = MV_NONE;
7556     if (attr_x < x)
7557       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7558     else if (attr_x > x)
7559       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7560     if (attr_y < y)
7561       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7562     else if (attr_y > y)
7563       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7564
7565     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7566
7567     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7568     {
7569       boolean first_horiz = RND(2);
7570       int new_move_dir = MovDir[x][y];
7571
7572       if (element_info[element].move_stepsize == 0)     // "not moving"
7573       {
7574         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7575         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576
7577         return;
7578       }
7579
7580       MovDir[x][y] =
7581         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7582       Moving2Blocked(x, y, &newx, &newy);
7583
7584       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7585         return;
7586
7587       MovDir[x][y] =
7588         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7589       Moving2Blocked(x, y, &newx, &newy);
7590
7591       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7592         return;
7593
7594       MovDir[x][y] = old_move_dir;
7595     }
7596   }
7597   else if (move_pattern == MV_WHEN_PUSHED ||
7598            move_pattern == MV_WHEN_DROPPED)
7599   {
7600     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7601       MovDir[x][y] = MV_NONE;
7602
7603     MovDelay[x][y] = 0;
7604   }
7605   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7606   {
7607     static int test_xy[7][2] =
7608     {
7609       { 0, -1 },
7610       { -1, 0 },
7611       { +1, 0 },
7612       { 0, +1 },
7613       { 0, -1 },
7614       { -1, 0 },
7615       { +1, 0 },
7616     };
7617     static int test_dir[7] =
7618     {
7619       MV_UP,
7620       MV_LEFT,
7621       MV_RIGHT,
7622       MV_DOWN,
7623       MV_UP,
7624       MV_LEFT,
7625       MV_RIGHT,
7626     };
7627     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7628     int move_preference = -1000000;     // start with very low preference
7629     int new_move_dir = MV_NONE;
7630     int start_test = RND(4);
7631     int i;
7632
7633     for (i = 0; i < NUM_DIRECTIONS; i++)
7634     {
7635       int move_dir = test_dir[start_test + i];
7636       int move_dir_preference;
7637
7638       xx = x + test_xy[start_test + i][0];
7639       yy = y + test_xy[start_test + i][1];
7640
7641       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7642           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7643       {
7644         new_move_dir = move_dir;
7645
7646         break;
7647       }
7648
7649       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7650         continue;
7651
7652       move_dir_preference = -1 * RunnerVisit[xx][yy];
7653       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7654         move_dir_preference = PlayerVisit[xx][yy];
7655
7656       if (move_dir_preference > move_preference)
7657       {
7658         // prefer field that has not been visited for the longest time
7659         move_preference = move_dir_preference;
7660         new_move_dir = move_dir;
7661       }
7662       else if (move_dir_preference == move_preference &&
7663                move_dir == old_move_dir)
7664       {
7665         // prefer last direction when all directions are preferred equally
7666         move_preference = move_dir_preference;
7667         new_move_dir = move_dir;
7668       }
7669     }
7670
7671     MovDir[x][y] = new_move_dir;
7672     if (old_move_dir != new_move_dir)
7673       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674   }
7675 }
7676
7677 static void TurnRound(int x, int y)
7678 {
7679   int direction = MovDir[x][y];
7680
7681   TurnRoundExt(x, y);
7682
7683   GfxDir[x][y] = MovDir[x][y];
7684
7685   if (direction != MovDir[x][y])
7686     GfxFrame[x][y] = 0;
7687
7688   if (MovDelay[x][y])
7689     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7690
7691   ResetGfxFrame(x, y);
7692 }
7693
7694 static boolean JustBeingPushed(int x, int y)
7695 {
7696   int i;
7697
7698   for (i = 0; i < MAX_PLAYERS; i++)
7699   {
7700     struct PlayerInfo *player = &stored_player[i];
7701
7702     if (player->active && player->is_pushing && player->MovPos)
7703     {
7704       int next_jx = player->jx + (player->jx - player->last_jx);
7705       int next_jy = player->jy + (player->jy - player->last_jy);
7706
7707       if (x == next_jx && y == next_jy)
7708         return TRUE;
7709     }
7710   }
7711
7712   return FALSE;
7713 }
7714
7715 static void StartMoving(int x, int y)
7716 {
7717   boolean started_moving = FALSE;       // some elements can fall _and_ move
7718   int element = Tile[x][y];
7719
7720   if (Stop[x][y])
7721     return;
7722
7723   if (MovDelay[x][y] == 0)
7724     GfxAction[x][y] = ACTION_DEFAULT;
7725
7726   if (CAN_FALL(element) && y < lev_fieldy - 1)
7727   {
7728     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7729         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7730       if (JustBeingPushed(x, y))
7731         return;
7732
7733     if (element == EL_QUICKSAND_FULL)
7734     {
7735       if (IS_FREE(x, y + 1))
7736       {
7737         InitMovingField(x, y, MV_DOWN);
7738         started_moving = TRUE;
7739
7740         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7741 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7742         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7743           Store[x][y] = EL_ROCK;
7744 #else
7745         Store[x][y] = EL_ROCK;
7746 #endif
7747
7748         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7749       }
7750       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7751       {
7752         if (!MovDelay[x][y])
7753         {
7754           MovDelay[x][y] = TILEY + 1;
7755
7756           ResetGfxAnimation(x, y);
7757           ResetGfxAnimation(x, y + 1);
7758         }
7759
7760         if (MovDelay[x][y])
7761         {
7762           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7763           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7764
7765           MovDelay[x][y]--;
7766           if (MovDelay[x][y])
7767             return;
7768         }
7769
7770         Tile[x][y] = EL_QUICKSAND_EMPTY;
7771         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7772         Store[x][y + 1] = Store[x][y];
7773         Store[x][y] = 0;
7774
7775         PlayLevelSoundAction(x, y, ACTION_FILLING);
7776       }
7777       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7778       {
7779         if (!MovDelay[x][y])
7780         {
7781           MovDelay[x][y] = TILEY + 1;
7782
7783           ResetGfxAnimation(x, y);
7784           ResetGfxAnimation(x, y + 1);
7785         }
7786
7787         if (MovDelay[x][y])
7788         {
7789           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7790           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7791
7792           MovDelay[x][y]--;
7793           if (MovDelay[x][y])
7794             return;
7795         }
7796
7797         Tile[x][y] = EL_QUICKSAND_EMPTY;
7798         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7799         Store[x][y + 1] = Store[x][y];
7800         Store[x][y] = 0;
7801
7802         PlayLevelSoundAction(x, y, ACTION_FILLING);
7803       }
7804     }
7805     else if (element == EL_QUICKSAND_FAST_FULL)
7806     {
7807       if (IS_FREE(x, y + 1))
7808       {
7809         InitMovingField(x, y, MV_DOWN);
7810         started_moving = TRUE;
7811
7812         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7813 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7814         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7815           Store[x][y] = EL_ROCK;
7816 #else
7817         Store[x][y] = EL_ROCK;
7818 #endif
7819
7820         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7821       }
7822       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7823       {
7824         if (!MovDelay[x][y])
7825         {
7826           MovDelay[x][y] = TILEY + 1;
7827
7828           ResetGfxAnimation(x, y);
7829           ResetGfxAnimation(x, y + 1);
7830         }
7831
7832         if (MovDelay[x][y])
7833         {
7834           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7835           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7836
7837           MovDelay[x][y]--;
7838           if (MovDelay[x][y])
7839             return;
7840         }
7841
7842         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7843         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7844         Store[x][y + 1] = Store[x][y];
7845         Store[x][y] = 0;
7846
7847         PlayLevelSoundAction(x, y, ACTION_FILLING);
7848       }
7849       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7850       {
7851         if (!MovDelay[x][y])
7852         {
7853           MovDelay[x][y] = TILEY + 1;
7854
7855           ResetGfxAnimation(x, y);
7856           ResetGfxAnimation(x, y + 1);
7857         }
7858
7859         if (MovDelay[x][y])
7860         {
7861           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7862           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7863
7864           MovDelay[x][y]--;
7865           if (MovDelay[x][y])
7866             return;
7867         }
7868
7869         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7870         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7871         Store[x][y + 1] = Store[x][y];
7872         Store[x][y] = 0;
7873
7874         PlayLevelSoundAction(x, y, ACTION_FILLING);
7875       }
7876     }
7877     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7878              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7879     {
7880       InitMovingField(x, y, MV_DOWN);
7881       started_moving = TRUE;
7882
7883       Tile[x][y] = EL_QUICKSAND_FILLING;
7884       Store[x][y] = element;
7885
7886       PlayLevelSoundAction(x, y, ACTION_FILLING);
7887     }
7888     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7889              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7890     {
7891       InitMovingField(x, y, MV_DOWN);
7892       started_moving = TRUE;
7893
7894       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7895       Store[x][y] = element;
7896
7897       PlayLevelSoundAction(x, y, ACTION_FILLING);
7898     }
7899     else if (element == EL_MAGIC_WALL_FULL)
7900     {
7901       if (IS_FREE(x, y + 1))
7902       {
7903         InitMovingField(x, y, MV_DOWN);
7904         started_moving = TRUE;
7905
7906         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7907         Store[x][y] = EL_CHANGED(Store[x][y]);
7908       }
7909       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7910       {
7911         if (!MovDelay[x][y])
7912           MovDelay[x][y] = TILEY / 4 + 1;
7913
7914         if (MovDelay[x][y])
7915         {
7916           MovDelay[x][y]--;
7917           if (MovDelay[x][y])
7918             return;
7919         }
7920
7921         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7922         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7923         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7924         Store[x][y] = 0;
7925       }
7926     }
7927     else if (element == EL_BD_MAGIC_WALL_FULL)
7928     {
7929       if (IS_FREE(x, y + 1))
7930       {
7931         InitMovingField(x, y, MV_DOWN);
7932         started_moving = TRUE;
7933
7934         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7935         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7936       }
7937       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7938       {
7939         if (!MovDelay[x][y])
7940           MovDelay[x][y] = TILEY / 4 + 1;
7941
7942         if (MovDelay[x][y])
7943         {
7944           MovDelay[x][y]--;
7945           if (MovDelay[x][y])
7946             return;
7947         }
7948
7949         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7950         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7951         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7952         Store[x][y] = 0;
7953       }
7954     }
7955     else if (element == EL_DC_MAGIC_WALL_FULL)
7956     {
7957       if (IS_FREE(x, y + 1))
7958       {
7959         InitMovingField(x, y, MV_DOWN);
7960         started_moving = TRUE;
7961
7962         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7963         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7964       }
7965       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7966       {
7967         if (!MovDelay[x][y])
7968           MovDelay[x][y] = TILEY / 4 + 1;
7969
7970         if (MovDelay[x][y])
7971         {
7972           MovDelay[x][y]--;
7973           if (MovDelay[x][y])
7974             return;
7975         }
7976
7977         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7978         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7979         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7980         Store[x][y] = 0;
7981       }
7982     }
7983     else if ((CAN_PASS_MAGIC_WALL(element) &&
7984               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7985                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7986              (CAN_PASS_DC_MAGIC_WALL(element) &&
7987               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7988
7989     {
7990       InitMovingField(x, y, MV_DOWN);
7991       started_moving = TRUE;
7992
7993       Tile[x][y] =
7994         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7995          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7996          EL_DC_MAGIC_WALL_FILLING);
7997       Store[x][y] = element;
7998     }
7999     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8000     {
8001       SplashAcid(x, y + 1);
8002
8003       InitMovingField(x, y, MV_DOWN);
8004       started_moving = TRUE;
8005
8006       Store[x][y] = EL_ACID;
8007     }
8008     else if (
8009              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8010               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8011              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8012               CAN_FALL(element) && WasJustFalling[x][y] &&
8013               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8014
8015              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8016               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8017               (Tile[x][y + 1] == EL_BLOCKED)))
8018     {
8019       /* this is needed for a special case not covered by calling "Impact()"
8020          from "ContinueMoving()": if an element moves to a tile directly below
8021          another element which was just falling on that tile (which was empty
8022          in the previous frame), the falling element above would just stop
8023          instead of smashing the element below (in previous version, the above
8024          element was just checked for "moving" instead of "falling", resulting
8025          in incorrect smashes caused by horizontal movement of the above
8026          element; also, the case of the player being the element to smash was
8027          simply not covered here... :-/ ) */
8028
8029       CheckCollision[x][y] = 0;
8030       CheckImpact[x][y] = 0;
8031
8032       Impact(x, y);
8033     }
8034     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8035     {
8036       if (MovDir[x][y] == MV_NONE)
8037       {
8038         InitMovingField(x, y, MV_DOWN);
8039         started_moving = TRUE;
8040       }
8041     }
8042     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8043     {
8044       if (WasJustFalling[x][y]) // prevent animation from being restarted
8045         MovDir[x][y] = MV_DOWN;
8046
8047       InitMovingField(x, y, MV_DOWN);
8048       started_moving = TRUE;
8049     }
8050     else if (element == EL_AMOEBA_DROP)
8051     {
8052       Tile[x][y] = EL_AMOEBA_GROWING;
8053       Store[x][y] = EL_AMOEBA_WET;
8054     }
8055     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8056               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8057              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8058              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8059     {
8060       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8061                                 (IS_FREE(x - 1, y + 1) ||
8062                                  Tile[x - 1][y + 1] == EL_ACID));
8063       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8064                                 (IS_FREE(x + 1, y + 1) ||
8065                                  Tile[x + 1][y + 1] == EL_ACID));
8066       boolean can_fall_any  = (can_fall_left || can_fall_right);
8067       boolean can_fall_both = (can_fall_left && can_fall_right);
8068       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8069
8070       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8071       {
8072         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8073           can_fall_right = FALSE;
8074         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8075           can_fall_left = FALSE;
8076         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8077           can_fall_right = FALSE;
8078         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8079           can_fall_left = FALSE;
8080
8081         can_fall_any  = (can_fall_left || can_fall_right);
8082         can_fall_both = FALSE;
8083       }
8084
8085       if (can_fall_both)
8086       {
8087         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8088           can_fall_right = FALSE;       // slip down on left side
8089         else
8090           can_fall_left = !(can_fall_right = RND(2));
8091
8092         can_fall_both = FALSE;
8093       }
8094
8095       if (can_fall_any)
8096       {
8097         // if not determined otherwise, prefer left side for slipping down
8098         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8099         started_moving = TRUE;
8100       }
8101     }
8102     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8103     {
8104       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8105       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8106       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8107       int belt_dir = game.belt_dir[belt_nr];
8108
8109       if ((belt_dir == MV_LEFT  && left_is_free) ||
8110           (belt_dir == MV_RIGHT && right_is_free))
8111       {
8112         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8113
8114         InitMovingField(x, y, belt_dir);
8115         started_moving = TRUE;
8116
8117         Pushed[x][y] = TRUE;
8118         Pushed[nextx][y] = TRUE;
8119
8120         GfxAction[x][y] = ACTION_DEFAULT;
8121       }
8122       else
8123       {
8124         MovDir[x][y] = 0;       // if element was moving, stop it
8125       }
8126     }
8127   }
8128
8129   // not "else if" because of elements that can fall and move (EL_SPRING)
8130   if (CAN_MOVE(element) && !started_moving)
8131   {
8132     int move_pattern = element_info[element].move_pattern;
8133     int newx, newy;
8134
8135     Moving2Blocked(x, y, &newx, &newy);
8136
8137     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8138       return;
8139
8140     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8141         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8142     {
8143       WasJustMoving[x][y] = 0;
8144       CheckCollision[x][y] = 0;
8145
8146       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8147
8148       if (Tile[x][y] != element)        // element has changed
8149         return;
8150     }
8151
8152     if (!MovDelay[x][y])        // start new movement phase
8153     {
8154       // all objects that can change their move direction after each step
8155       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8156
8157       if (element != EL_YAMYAM &&
8158           element != EL_DARK_YAMYAM &&
8159           element != EL_PACMAN &&
8160           !(move_pattern & MV_ANY_DIRECTION) &&
8161           move_pattern != MV_TURNING_LEFT &&
8162           move_pattern != MV_TURNING_RIGHT &&
8163           move_pattern != MV_TURNING_LEFT_RIGHT &&
8164           move_pattern != MV_TURNING_RIGHT_LEFT &&
8165           move_pattern != MV_TURNING_RANDOM)
8166       {
8167         TurnRound(x, y);
8168
8169         if (MovDelay[x][y] && (element == EL_BUG ||
8170                                element == EL_SPACESHIP ||
8171                                element == EL_SP_SNIKSNAK ||
8172                                element == EL_SP_ELECTRON ||
8173                                element == EL_MOLE))
8174           TEST_DrawLevelField(x, y);
8175       }
8176     }
8177
8178     if (MovDelay[x][y])         // wait some time before next movement
8179     {
8180       MovDelay[x][y]--;
8181
8182       if (element == EL_ROBOT ||
8183           element == EL_YAMYAM ||
8184           element == EL_DARK_YAMYAM)
8185       {
8186         DrawLevelElementAnimationIfNeeded(x, y, element);
8187         PlayLevelSoundAction(x, y, ACTION_WAITING);
8188       }
8189       else if (element == EL_SP_ELECTRON)
8190         DrawLevelElementAnimationIfNeeded(x, y, element);
8191       else if (element == EL_DRAGON)
8192       {
8193         int i;
8194         int dir = MovDir[x][y];
8195         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8196         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8197         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8198                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8199                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8200                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8201         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8202
8203         GfxAction[x][y] = ACTION_ATTACKING;
8204
8205         if (IS_PLAYER(x, y))
8206           DrawPlayerField(x, y);
8207         else
8208           TEST_DrawLevelField(x, y);
8209
8210         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8211
8212         for (i = 1; i <= 3; i++)
8213         {
8214           int xx = x + i * dx;
8215           int yy = y + i * dy;
8216           int sx = SCREENX(xx);
8217           int sy = SCREENY(yy);
8218           int flame_graphic = graphic + (i - 1);
8219
8220           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8221             break;
8222
8223           if (MovDelay[x][y])
8224           {
8225             int flamed = MovingOrBlocked2Element(xx, yy);
8226
8227             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8228               Bang(xx, yy);
8229             else
8230               RemoveMovingField(xx, yy);
8231
8232             ChangeDelay[xx][yy] = 0;
8233
8234             Tile[xx][yy] = EL_FLAMES;
8235
8236             if (IN_SCR_FIELD(sx, sy))
8237             {
8238               TEST_DrawLevelFieldCrumbled(xx, yy);
8239               DrawGraphic(sx, sy, flame_graphic, frame);
8240             }
8241           }
8242           else
8243           {
8244             if (Tile[xx][yy] == EL_FLAMES)
8245               Tile[xx][yy] = EL_EMPTY;
8246             TEST_DrawLevelField(xx, yy);
8247           }
8248         }
8249       }
8250
8251       if (MovDelay[x][y])       // element still has to wait some time
8252       {
8253         PlayLevelSoundAction(x, y, ACTION_WAITING);
8254
8255         return;
8256       }
8257     }
8258
8259     // now make next step
8260
8261     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8262
8263     if (DONT_COLLIDE_WITH(element) &&
8264         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8265         !PLAYER_ENEMY_PROTECTED(newx, newy))
8266     {
8267       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8268
8269       return;
8270     }
8271
8272     else if (CAN_MOVE_INTO_ACID(element) &&
8273              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8274              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8275              (MovDir[x][y] == MV_DOWN ||
8276               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8277     {
8278       SplashAcid(newx, newy);
8279       Store[x][y] = EL_ACID;
8280     }
8281     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8282     {
8283       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8284           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8285           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8286           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8287       {
8288         RemoveField(x, y);
8289         TEST_DrawLevelField(x, y);
8290
8291         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8292         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8293           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8294
8295         game.friends_still_needed--;
8296         if (!game.friends_still_needed &&
8297             !game.GameOver &&
8298             game.all_players_gone)
8299           LevelSolved();
8300
8301         return;
8302       }
8303       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8304       {
8305         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8306           TEST_DrawLevelField(newx, newy);
8307         else
8308           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8309       }
8310       else if (!IS_FREE(newx, newy))
8311       {
8312         GfxAction[x][y] = ACTION_WAITING;
8313
8314         if (IS_PLAYER(x, y))
8315           DrawPlayerField(x, y);
8316         else
8317           TEST_DrawLevelField(x, y);
8318
8319         return;
8320       }
8321     }
8322     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8323     {
8324       if (IS_FOOD_PIG(Tile[newx][newy]))
8325       {
8326         if (IS_MOVING(newx, newy))
8327           RemoveMovingField(newx, newy);
8328         else
8329         {
8330           Tile[newx][newy] = EL_EMPTY;
8331           TEST_DrawLevelField(newx, newy);
8332         }
8333
8334         PlayLevelSound(x, y, SND_PIG_DIGGING);
8335       }
8336       else if (!IS_FREE(newx, newy))
8337       {
8338         if (IS_PLAYER(x, y))
8339           DrawPlayerField(x, y);
8340         else
8341           TEST_DrawLevelField(x, y);
8342
8343         return;
8344       }
8345     }
8346     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8347     {
8348       if (Store[x][y] != EL_EMPTY)
8349       {
8350         boolean can_clone = FALSE;
8351         int xx, yy;
8352
8353         // check if element to clone is still there
8354         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8355         {
8356           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8357           {
8358             can_clone = TRUE;
8359
8360             break;
8361           }
8362         }
8363
8364         // cannot clone or target field not free anymore -- do not clone
8365         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8366           Store[x][y] = EL_EMPTY;
8367       }
8368
8369       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8370       {
8371         if (IS_MV_DIAGONAL(MovDir[x][y]))
8372         {
8373           int diagonal_move_dir = MovDir[x][y];
8374           int stored = Store[x][y];
8375           int change_delay = 8;
8376           int graphic;
8377
8378           // android is moving diagonally
8379
8380           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8381
8382           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8383           GfxElement[x][y] = EL_EMC_ANDROID;
8384           GfxAction[x][y] = ACTION_SHRINKING;
8385           GfxDir[x][y] = diagonal_move_dir;
8386           ChangeDelay[x][y] = change_delay;
8387
8388           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8389                                    GfxDir[x][y]);
8390
8391           DrawLevelGraphicAnimation(x, y, graphic);
8392           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8393
8394           if (Tile[newx][newy] == EL_ACID)
8395           {
8396             SplashAcid(newx, newy);
8397
8398             return;
8399           }
8400
8401           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8402
8403           Store[newx][newy] = EL_EMC_ANDROID;
8404           GfxElement[newx][newy] = EL_EMC_ANDROID;
8405           GfxAction[newx][newy] = ACTION_GROWING;
8406           GfxDir[newx][newy] = diagonal_move_dir;
8407           ChangeDelay[newx][newy] = change_delay;
8408
8409           graphic = el_act_dir2img(GfxElement[newx][newy],
8410                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8411
8412           DrawLevelGraphicAnimation(newx, newy, graphic);
8413           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8414
8415           return;
8416         }
8417         else
8418         {
8419           Tile[newx][newy] = EL_EMPTY;
8420           TEST_DrawLevelField(newx, newy);
8421
8422           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8423         }
8424       }
8425       else if (!IS_FREE(newx, newy))
8426       {
8427         return;
8428       }
8429     }
8430     else if (IS_CUSTOM_ELEMENT(element) &&
8431              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8432     {
8433       if (!DigFieldByCE(newx, newy, element))
8434         return;
8435
8436       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8437       {
8438         RunnerVisit[x][y] = FrameCounter;
8439         PlayerVisit[x][y] /= 8;         // expire player visit path
8440       }
8441     }
8442     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8443     {
8444       if (!IS_FREE(newx, newy))
8445       {
8446         if (IS_PLAYER(x, y))
8447           DrawPlayerField(x, y);
8448         else
8449           TEST_DrawLevelField(x, y);
8450
8451         return;
8452       }
8453       else
8454       {
8455         boolean wanna_flame = !RND(10);
8456         int dx = newx - x, dy = newy - y;
8457         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8458         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8459         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8460                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8461         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8462                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8463
8464         if ((wanna_flame ||
8465              IS_CLASSIC_ENEMY(element1) ||
8466              IS_CLASSIC_ENEMY(element2)) &&
8467             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8468             element1 != EL_FLAMES && element2 != EL_FLAMES)
8469         {
8470           ResetGfxAnimation(x, y);
8471           GfxAction[x][y] = ACTION_ATTACKING;
8472
8473           if (IS_PLAYER(x, y))
8474             DrawPlayerField(x, y);
8475           else
8476             TEST_DrawLevelField(x, y);
8477
8478           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8479
8480           MovDelay[x][y] = 50;
8481
8482           Tile[newx][newy] = EL_FLAMES;
8483           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8484             Tile[newx1][newy1] = EL_FLAMES;
8485           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8486             Tile[newx2][newy2] = EL_FLAMES;
8487
8488           return;
8489         }
8490       }
8491     }
8492     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8493              Tile[newx][newy] == EL_DIAMOND)
8494     {
8495       if (IS_MOVING(newx, newy))
8496         RemoveMovingField(newx, newy);
8497       else
8498       {
8499         Tile[newx][newy] = EL_EMPTY;
8500         TEST_DrawLevelField(newx, newy);
8501       }
8502
8503       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8504     }
8505     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8506              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8507     {
8508       if (AmoebaNr[newx][newy])
8509       {
8510         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8511         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8512             Tile[newx][newy] == EL_BD_AMOEBA)
8513           AmoebaCnt[AmoebaNr[newx][newy]]--;
8514       }
8515
8516       if (IS_MOVING(newx, newy))
8517       {
8518         RemoveMovingField(newx, newy);
8519       }
8520       else
8521       {
8522         Tile[newx][newy] = EL_EMPTY;
8523         TEST_DrawLevelField(newx, newy);
8524       }
8525
8526       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8527     }
8528     else if ((element == EL_PACMAN || element == EL_MOLE)
8529              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8530     {
8531       if (AmoebaNr[newx][newy])
8532       {
8533         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8534         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8535             Tile[newx][newy] == EL_BD_AMOEBA)
8536           AmoebaCnt[AmoebaNr[newx][newy]]--;
8537       }
8538
8539       if (element == EL_MOLE)
8540       {
8541         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8542         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8543
8544         ResetGfxAnimation(x, y);
8545         GfxAction[x][y] = ACTION_DIGGING;
8546         TEST_DrawLevelField(x, y);
8547
8548         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8549
8550         return;                         // wait for shrinking amoeba
8551       }
8552       else      // element == EL_PACMAN
8553       {
8554         Tile[newx][newy] = EL_EMPTY;
8555         TEST_DrawLevelField(newx, newy);
8556         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8557       }
8558     }
8559     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8560              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8561               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8562     {
8563       // wait for shrinking amoeba to completely disappear
8564       return;
8565     }
8566     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8567     {
8568       // object was running against a wall
8569
8570       TurnRound(x, y);
8571
8572       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8573         DrawLevelElementAnimation(x, y, element);
8574
8575       if (DONT_TOUCH(element))
8576         TestIfBadThingTouchesPlayer(x, y);
8577
8578       return;
8579     }
8580
8581     InitMovingField(x, y, MovDir[x][y]);
8582
8583     PlayLevelSoundAction(x, y, ACTION_MOVING);
8584   }
8585
8586   if (MovDir[x][y])
8587     ContinueMoving(x, y);
8588 }
8589
8590 void ContinueMoving(int x, int y)
8591 {
8592   int element = Tile[x][y];
8593   struct ElementInfo *ei = &element_info[element];
8594   int direction = MovDir[x][y];
8595   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8596   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8597   int newx = x + dx, newy = y + dy;
8598   int stored = Store[x][y];
8599   int stored_new = Store[newx][newy];
8600   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8601   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8602   boolean last_line = (newy == lev_fieldy - 1);
8603   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8604
8605   if (pushed_by_player)         // special case: moving object pushed by player
8606   {
8607     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8608   }
8609   else if (use_step_delay)      // special case: moving object has step delay
8610   {
8611     if (!MovDelay[x][y])
8612       MovPos[x][y] += getElementMoveStepsize(x, y);
8613
8614     if (MovDelay[x][y])
8615       MovDelay[x][y]--;
8616     else
8617       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8618
8619     if (MovDelay[x][y])
8620     {
8621       TEST_DrawLevelField(x, y);
8622
8623       return;   // element is still waiting
8624     }
8625   }
8626   else                          // normal case: generically moving object
8627   {
8628     MovPos[x][y] += getElementMoveStepsize(x, y);
8629   }
8630
8631   if (ABS(MovPos[x][y]) < TILEX)
8632   {
8633     TEST_DrawLevelField(x, y);
8634
8635     return;     // element is still moving
8636   }
8637
8638   // element reached destination field
8639
8640   Tile[x][y] = EL_EMPTY;
8641   Tile[newx][newy] = element;
8642   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8643
8644   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8645   {
8646     element = Tile[newx][newy] = EL_ACID;
8647   }
8648   else if (element == EL_MOLE)
8649   {
8650     Tile[x][y] = EL_SAND;
8651
8652     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8653   }
8654   else if (element == EL_QUICKSAND_FILLING)
8655   {
8656     element = Tile[newx][newy] = get_next_element(element);
8657     Store[newx][newy] = Store[x][y];
8658   }
8659   else if (element == EL_QUICKSAND_EMPTYING)
8660   {
8661     Tile[x][y] = get_next_element(element);
8662     element = Tile[newx][newy] = Store[x][y];
8663   }
8664   else if (element == EL_QUICKSAND_FAST_FILLING)
8665   {
8666     element = Tile[newx][newy] = get_next_element(element);
8667     Store[newx][newy] = Store[x][y];
8668   }
8669   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8670   {
8671     Tile[x][y] = get_next_element(element);
8672     element = Tile[newx][newy] = Store[x][y];
8673   }
8674   else if (element == EL_MAGIC_WALL_FILLING)
8675   {
8676     element = Tile[newx][newy] = get_next_element(element);
8677     if (!game.magic_wall_active)
8678       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8679     Store[newx][newy] = Store[x][y];
8680   }
8681   else if (element == EL_MAGIC_WALL_EMPTYING)
8682   {
8683     Tile[x][y] = get_next_element(element);
8684     if (!game.magic_wall_active)
8685       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8686     element = Tile[newx][newy] = Store[x][y];
8687
8688     InitField(newx, newy, FALSE);
8689   }
8690   else if (element == EL_BD_MAGIC_WALL_FILLING)
8691   {
8692     element = Tile[newx][newy] = get_next_element(element);
8693     if (!game.magic_wall_active)
8694       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8695     Store[newx][newy] = Store[x][y];
8696   }
8697   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8698   {
8699     Tile[x][y] = get_next_element(element);
8700     if (!game.magic_wall_active)
8701       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8702     element = Tile[newx][newy] = Store[x][y];
8703
8704     InitField(newx, newy, FALSE);
8705   }
8706   else if (element == EL_DC_MAGIC_WALL_FILLING)
8707   {
8708     element = Tile[newx][newy] = get_next_element(element);
8709     if (!game.magic_wall_active)
8710       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8711     Store[newx][newy] = Store[x][y];
8712   }
8713   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8714   {
8715     Tile[x][y] = get_next_element(element);
8716     if (!game.magic_wall_active)
8717       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8718     element = Tile[newx][newy] = Store[x][y];
8719
8720     InitField(newx, newy, FALSE);
8721   }
8722   else if (element == EL_AMOEBA_DROPPING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     element = Tile[newx][newy] = Store[x][y];
8726   }
8727   else if (element == EL_SOKOBAN_OBJECT)
8728   {
8729     if (Back[x][y])
8730       Tile[x][y] = Back[x][y];
8731
8732     if (Back[newx][newy])
8733       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8734
8735     Back[x][y] = Back[newx][newy] = 0;
8736   }
8737
8738   Store[x][y] = EL_EMPTY;
8739   MovPos[x][y] = 0;
8740   MovDir[x][y] = 0;
8741   MovDelay[x][y] = 0;
8742
8743   MovDelay[newx][newy] = 0;
8744
8745   if (CAN_CHANGE_OR_HAS_ACTION(element))
8746   {
8747     // copy element change control values to new field
8748     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8749     ChangePage[newx][newy]  = ChangePage[x][y];
8750     ChangeCount[newx][newy] = ChangeCount[x][y];
8751     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8752   }
8753
8754   CustomValue[newx][newy] = CustomValue[x][y];
8755
8756   ChangeDelay[x][y] = 0;
8757   ChangePage[x][y] = -1;
8758   ChangeCount[x][y] = 0;
8759   ChangeEvent[x][y] = -1;
8760
8761   CustomValue[x][y] = 0;
8762
8763   // copy animation control values to new field
8764   GfxFrame[newx][newy]  = GfxFrame[x][y];
8765   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8766   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8767   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8768
8769   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8770
8771   // some elements can leave other elements behind after moving
8772   if (ei->move_leave_element != EL_EMPTY &&
8773       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8774       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8775   {
8776     int move_leave_element = ei->move_leave_element;
8777
8778     // this makes it possible to leave the removed element again
8779     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8780       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8781
8782     Tile[x][y] = move_leave_element;
8783
8784     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8785       MovDir[x][y] = direction;
8786
8787     InitField(x, y, FALSE);
8788
8789     if (GFX_CRUMBLED(Tile[x][y]))
8790       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8791
8792     if (IS_PLAYER_ELEMENT(move_leave_element))
8793       RelocatePlayer(x, y, move_leave_element);
8794   }
8795
8796   // do this after checking for left-behind element
8797   ResetGfxAnimation(x, y);      // reset animation values for old field
8798
8799   if (!CAN_MOVE(element) ||
8800       (CAN_FALL(element) && direction == MV_DOWN &&
8801        (element == EL_SPRING ||
8802         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8803         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8804     GfxDir[x][y] = MovDir[newx][newy] = 0;
8805
8806   TEST_DrawLevelField(x, y);
8807   TEST_DrawLevelField(newx, newy);
8808
8809   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8810
8811   // prevent pushed element from moving on in pushed direction
8812   if (pushed_by_player && CAN_MOVE(element) &&
8813       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8814       !(element_info[element].move_pattern & direction))
8815     TurnRound(newx, newy);
8816
8817   // prevent elements on conveyor belt from moving on in last direction
8818   if (pushed_by_conveyor && CAN_FALL(element) &&
8819       direction & MV_HORIZONTAL)
8820     MovDir[newx][newy] = 0;
8821
8822   if (!pushed_by_player)
8823   {
8824     int nextx = newx + dx, nexty = newy + dy;
8825     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8826
8827     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8828
8829     if (CAN_FALL(element) && direction == MV_DOWN)
8830       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8831
8832     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8833       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8834
8835     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8836       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8837   }
8838
8839   if (DONT_TOUCH(element))      // object may be nasty to player or others
8840   {
8841     TestIfBadThingTouchesPlayer(newx, newy);
8842     TestIfBadThingTouchesFriend(newx, newy);
8843
8844     if (!IS_CUSTOM_ELEMENT(element))
8845       TestIfBadThingTouchesOtherBadThing(newx, newy);
8846   }
8847   else if (element == EL_PENGUIN)
8848     TestIfFriendTouchesBadThing(newx, newy);
8849
8850   if (DONT_GET_HIT_BY(element))
8851   {
8852     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8853   }
8854
8855   // give the player one last chance (one more frame) to move away
8856   if (CAN_FALL(element) && direction == MV_DOWN &&
8857       (last_line || (!IS_FREE(x, newy + 1) &&
8858                      (!IS_PLAYER(x, newy + 1) ||
8859                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8860     Impact(x, newy);
8861
8862   if (pushed_by_player && !game.use_change_when_pushing_bug)
8863   {
8864     int push_side = MV_DIR_OPPOSITE(direction);
8865     struct PlayerInfo *player = PLAYERINFO(x, y);
8866
8867     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8868                                player->index_bit, push_side);
8869     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8870                                         player->index_bit, push_side);
8871   }
8872
8873   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8874     MovDelay[newx][newy] = 1;
8875
8876   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8877
8878   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8879   TestIfElementHitsCustomElement(newx, newy, direction);
8880   TestIfPlayerTouchesCustomElement(newx, newy);
8881   TestIfElementTouchesCustomElement(newx, newy);
8882
8883   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8884       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8885     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8886                              MV_DIR_OPPOSITE(direction));
8887 }
8888
8889 int AmoebaNeighbourNr(int ax, int ay)
8890 {
8891   int i;
8892   int element = Tile[ax][ay];
8893   int group_nr = 0;
8894   static int xy[4][2] =
8895   {
8896     { 0, -1 },
8897     { -1, 0 },
8898     { +1, 0 },
8899     { 0, +1 }
8900   };
8901
8902   for (i = 0; i < NUM_DIRECTIONS; i++)
8903   {
8904     int x = ax + xy[i][0];
8905     int y = ay + xy[i][1];
8906
8907     if (!IN_LEV_FIELD(x, y))
8908       continue;
8909
8910     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8911       group_nr = AmoebaNr[x][y];
8912   }
8913
8914   return group_nr;
8915 }
8916
8917 static void AmoebaMerge(int ax, int ay)
8918 {
8919   int i, x, y, xx, yy;
8920   int new_group_nr = AmoebaNr[ax][ay];
8921   static int xy[4][2] =
8922   {
8923     { 0, -1 },
8924     { -1, 0 },
8925     { +1, 0 },
8926     { 0, +1 }
8927   };
8928
8929   if (new_group_nr == 0)
8930     return;
8931
8932   for (i = 0; i < NUM_DIRECTIONS; i++)
8933   {
8934     x = ax + xy[i][0];
8935     y = ay + xy[i][1];
8936
8937     if (!IN_LEV_FIELD(x, y))
8938       continue;
8939
8940     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8941          Tile[x][y] == EL_BD_AMOEBA ||
8942          Tile[x][y] == EL_AMOEBA_DEAD) &&
8943         AmoebaNr[x][y] != new_group_nr)
8944     {
8945       int old_group_nr = AmoebaNr[x][y];
8946
8947       if (old_group_nr == 0)
8948         return;
8949
8950       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8951       AmoebaCnt[old_group_nr] = 0;
8952       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8953       AmoebaCnt2[old_group_nr] = 0;
8954
8955       SCAN_PLAYFIELD(xx, yy)
8956       {
8957         if (AmoebaNr[xx][yy] == old_group_nr)
8958           AmoebaNr[xx][yy] = new_group_nr;
8959       }
8960     }
8961   }
8962 }
8963
8964 void AmoebaToDiamond(int ax, int ay)
8965 {
8966   int i, x, y;
8967
8968   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8969   {
8970     int group_nr = AmoebaNr[ax][ay];
8971
8972 #ifdef DEBUG
8973     if (group_nr == 0)
8974     {
8975       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8976       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8977
8978       return;
8979     }
8980 #endif
8981
8982     SCAN_PLAYFIELD(x, y)
8983     {
8984       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8985       {
8986         AmoebaNr[x][y] = 0;
8987         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8988       }
8989     }
8990
8991     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8992                             SND_AMOEBA_TURNING_TO_GEM :
8993                             SND_AMOEBA_TURNING_TO_ROCK));
8994     Bang(ax, ay);
8995   }
8996   else
8997   {
8998     static int xy[4][2] =
8999     {
9000       { 0, -1 },
9001       { -1, 0 },
9002       { +1, 0 },
9003       { 0, +1 }
9004     };
9005
9006     for (i = 0; i < NUM_DIRECTIONS; i++)
9007     {
9008       x = ax + xy[i][0];
9009       y = ay + xy[i][1];
9010
9011       if (!IN_LEV_FIELD(x, y))
9012         continue;
9013
9014       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9015       {
9016         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9017                               SND_AMOEBA_TURNING_TO_GEM :
9018                               SND_AMOEBA_TURNING_TO_ROCK));
9019         Bang(x, y);
9020       }
9021     }
9022   }
9023 }
9024
9025 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9026 {
9027   int x, y;
9028   int group_nr = AmoebaNr[ax][ay];
9029   boolean done = FALSE;
9030
9031 #ifdef DEBUG
9032   if (group_nr == 0)
9033   {
9034     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9035     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9036
9037     return;
9038   }
9039 #endif
9040
9041   SCAN_PLAYFIELD(x, y)
9042   {
9043     if (AmoebaNr[x][y] == group_nr &&
9044         (Tile[x][y] == EL_AMOEBA_DEAD ||
9045          Tile[x][y] == EL_BD_AMOEBA ||
9046          Tile[x][y] == EL_AMOEBA_GROWING))
9047     {
9048       AmoebaNr[x][y] = 0;
9049       Tile[x][y] = new_element;
9050       InitField(x, y, FALSE);
9051       TEST_DrawLevelField(x, y);
9052       done = TRUE;
9053     }
9054   }
9055
9056   if (done)
9057     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9058                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9059                             SND_BD_AMOEBA_TURNING_TO_GEM));
9060 }
9061
9062 static void AmoebaGrowing(int x, int y)
9063 {
9064   static unsigned int sound_delay = 0;
9065   static unsigned int sound_delay_value = 0;
9066
9067   if (!MovDelay[x][y])          // start new growing cycle
9068   {
9069     MovDelay[x][y] = 7;
9070
9071     if (DelayReached(&sound_delay, sound_delay_value))
9072     {
9073       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9074       sound_delay_value = 30;
9075     }
9076   }
9077
9078   if (MovDelay[x][y])           // wait some time before growing bigger
9079   {
9080     MovDelay[x][y]--;
9081     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9082     {
9083       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9084                                            6 - MovDelay[x][y]);
9085
9086       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9087     }
9088
9089     if (!MovDelay[x][y])
9090     {
9091       Tile[x][y] = Store[x][y];
9092       Store[x][y] = 0;
9093       TEST_DrawLevelField(x, y);
9094     }
9095   }
9096 }
9097
9098 static void AmoebaShrinking(int x, int y)
9099 {
9100   static unsigned int sound_delay = 0;
9101   static unsigned int sound_delay_value = 0;
9102
9103   if (!MovDelay[x][y])          // start new shrinking cycle
9104   {
9105     MovDelay[x][y] = 7;
9106
9107     if (DelayReached(&sound_delay, sound_delay_value))
9108       sound_delay_value = 30;
9109   }
9110
9111   if (MovDelay[x][y])           // wait some time before shrinking
9112   {
9113     MovDelay[x][y]--;
9114     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9115     {
9116       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9117                                            6 - MovDelay[x][y]);
9118
9119       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9120     }
9121
9122     if (!MovDelay[x][y])
9123     {
9124       Tile[x][y] = EL_EMPTY;
9125       TEST_DrawLevelField(x, y);
9126
9127       // don't let mole enter this field in this cycle;
9128       // (give priority to objects falling to this field from above)
9129       Stop[x][y] = TRUE;
9130     }
9131   }
9132 }
9133
9134 static void AmoebaReproduce(int ax, int ay)
9135 {
9136   int i;
9137   int element = Tile[ax][ay];
9138   int graphic = el2img(element);
9139   int newax = ax, neway = ay;
9140   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9141   static int xy[4][2] =
9142   {
9143     { 0, -1 },
9144     { -1, 0 },
9145     { +1, 0 },
9146     { 0, +1 }
9147   };
9148
9149   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9150   {
9151     Tile[ax][ay] = EL_AMOEBA_DEAD;
9152     TEST_DrawLevelField(ax, ay);
9153     return;
9154   }
9155
9156   if (IS_ANIMATED(graphic))
9157     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9158
9159   if (!MovDelay[ax][ay])        // start making new amoeba field
9160     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9161
9162   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9163   {
9164     MovDelay[ax][ay]--;
9165     if (MovDelay[ax][ay])
9166       return;
9167   }
9168
9169   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9170   {
9171     int start = RND(4);
9172     int x = ax + xy[start][0];
9173     int y = ay + xy[start][1];
9174
9175     if (!IN_LEV_FIELD(x, y))
9176       return;
9177
9178     if (IS_FREE(x, y) ||
9179         CAN_GROW_INTO(Tile[x][y]) ||
9180         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9181         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9182     {
9183       newax = x;
9184       neway = y;
9185     }
9186
9187     if (newax == ax && neway == ay)
9188       return;
9189   }
9190   else                          // normal or "filled" (BD style) amoeba
9191   {
9192     int start = RND(4);
9193     boolean waiting_for_player = FALSE;
9194
9195     for (i = 0; i < NUM_DIRECTIONS; i++)
9196     {
9197       int j = (start + i) % 4;
9198       int x = ax + xy[j][0];
9199       int y = ay + xy[j][1];
9200
9201       if (!IN_LEV_FIELD(x, y))
9202         continue;
9203
9204       if (IS_FREE(x, y) ||
9205           CAN_GROW_INTO(Tile[x][y]) ||
9206           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9207           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9208       {
9209         newax = x;
9210         neway = y;
9211         break;
9212       }
9213       else if (IS_PLAYER(x, y))
9214         waiting_for_player = TRUE;
9215     }
9216
9217     if (newax == ax && neway == ay)             // amoeba cannot grow
9218     {
9219       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9220       {
9221         Tile[ax][ay] = EL_AMOEBA_DEAD;
9222         TEST_DrawLevelField(ax, ay);
9223         AmoebaCnt[AmoebaNr[ax][ay]]--;
9224
9225         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9226         {
9227           if (element == EL_AMOEBA_FULL)
9228             AmoebaToDiamond(ax, ay);
9229           else if (element == EL_BD_AMOEBA)
9230             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9231         }
9232       }
9233       return;
9234     }
9235     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9236     {
9237       // amoeba gets larger by growing in some direction
9238
9239       int new_group_nr = AmoebaNr[ax][ay];
9240
9241 #ifdef DEBUG
9242   if (new_group_nr == 0)
9243   {
9244     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9245           newax, neway);
9246     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9247
9248     return;
9249   }
9250 #endif
9251
9252       AmoebaNr[newax][neway] = new_group_nr;
9253       AmoebaCnt[new_group_nr]++;
9254       AmoebaCnt2[new_group_nr]++;
9255
9256       // if amoeba touches other amoeba(s) after growing, unify them
9257       AmoebaMerge(newax, neway);
9258
9259       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9260       {
9261         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9262         return;
9263       }
9264     }
9265   }
9266
9267   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9268       (neway == lev_fieldy - 1 && newax != ax))
9269   {
9270     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9271     Store[newax][neway] = element;
9272   }
9273   else if (neway == ay || element == EL_EMC_DRIPPER)
9274   {
9275     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9276
9277     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9278   }
9279   else
9280   {
9281     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9282     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9283     Store[ax][ay] = EL_AMOEBA_DROP;
9284     ContinueMoving(ax, ay);
9285     return;
9286   }
9287
9288   TEST_DrawLevelField(newax, neway);
9289 }
9290
9291 static void Life(int ax, int ay)
9292 {
9293   int x1, y1, x2, y2;
9294   int life_time = 40;
9295   int element = Tile[ax][ay];
9296   int graphic = el2img(element);
9297   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9298                          level.biomaze);
9299   boolean changed = FALSE;
9300
9301   if (IS_ANIMATED(graphic))
9302     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9303
9304   if (Stop[ax][ay])
9305     return;
9306
9307   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9308     MovDelay[ax][ay] = life_time;
9309
9310   if (MovDelay[ax][ay])         // wait some time before next cycle
9311   {
9312     MovDelay[ax][ay]--;
9313     if (MovDelay[ax][ay])
9314       return;
9315   }
9316
9317   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9318   {
9319     int xx = ax+x1, yy = ay+y1;
9320     int old_element = Tile[xx][yy];
9321     int num_neighbours = 0;
9322
9323     if (!IN_LEV_FIELD(xx, yy))
9324       continue;
9325
9326     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9327     {
9328       int x = xx+x2, y = yy+y2;
9329
9330       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9331         continue;
9332
9333       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9334       boolean is_neighbour = FALSE;
9335
9336       if (level.use_life_bugs)
9337         is_neighbour =
9338           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9339            (IS_FREE(x, y)                             &&  Stop[x][y]));
9340       else
9341         is_neighbour =
9342           (Last[x][y] == element || is_player_cell);
9343
9344       if (is_neighbour)
9345         num_neighbours++;
9346     }
9347
9348     boolean is_free = FALSE;
9349
9350     if (level.use_life_bugs)
9351       is_free = (IS_FREE(xx, yy));
9352     else
9353       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9354
9355     if (xx == ax && yy == ay)           // field in the middle
9356     {
9357       if (num_neighbours < life_parameter[0] ||
9358           num_neighbours > life_parameter[1])
9359       {
9360         Tile[xx][yy] = EL_EMPTY;
9361         if (Tile[xx][yy] != old_element)
9362           TEST_DrawLevelField(xx, yy);
9363         Stop[xx][yy] = TRUE;
9364         changed = TRUE;
9365       }
9366     }
9367     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9368     {                                   // free border field
9369       if (num_neighbours >= life_parameter[2] &&
9370           num_neighbours <= life_parameter[3])
9371       {
9372         Tile[xx][yy] = element;
9373         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9374         if (Tile[xx][yy] != old_element)
9375           TEST_DrawLevelField(xx, yy);
9376         Stop[xx][yy] = TRUE;
9377         changed = TRUE;
9378       }
9379     }
9380   }
9381
9382   if (changed)
9383     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9384                    SND_GAME_OF_LIFE_GROWING);
9385 }
9386
9387 static void InitRobotWheel(int x, int y)
9388 {
9389   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9390 }
9391
9392 static void RunRobotWheel(int x, int y)
9393 {
9394   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9395 }
9396
9397 static void StopRobotWheel(int x, int y)
9398 {
9399   if (game.robot_wheel_x == x &&
9400       game.robot_wheel_y == y)
9401   {
9402     game.robot_wheel_x = -1;
9403     game.robot_wheel_y = -1;
9404     game.robot_wheel_active = FALSE;
9405   }
9406 }
9407
9408 static void InitTimegateWheel(int x, int y)
9409 {
9410   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9411 }
9412
9413 static void RunTimegateWheel(int x, int y)
9414 {
9415   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9416 }
9417
9418 static void InitMagicBallDelay(int x, int y)
9419 {
9420   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9421 }
9422
9423 static void ActivateMagicBall(int bx, int by)
9424 {
9425   int x, y;
9426
9427   if (level.ball_random)
9428   {
9429     int pos_border = RND(8);    // select one of the eight border elements
9430     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9431     int xx = pos_content % 3;
9432     int yy = pos_content / 3;
9433
9434     x = bx - 1 + xx;
9435     y = by - 1 + yy;
9436
9437     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9438       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9439   }
9440   else
9441   {
9442     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9443     {
9444       int xx = x - bx + 1;
9445       int yy = y - by + 1;
9446
9447       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9448         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9449     }
9450   }
9451
9452   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9453 }
9454
9455 static void CheckExit(int x, int y)
9456 {
9457   if (game.gems_still_needed > 0 ||
9458       game.sokoban_fields_still_needed > 0 ||
9459       game.sokoban_objects_still_needed > 0 ||
9460       game.lights_still_needed > 0)
9461   {
9462     int element = Tile[x][y];
9463     int graphic = el2img(element);
9464
9465     if (IS_ANIMATED(graphic))
9466       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9467
9468     return;
9469   }
9470
9471   // do not re-open exit door closed after last player
9472   if (game.all_players_gone)
9473     return;
9474
9475   Tile[x][y] = EL_EXIT_OPENING;
9476
9477   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9478 }
9479
9480 static void CheckExitEM(int x, int y)
9481 {
9482   if (game.gems_still_needed > 0 ||
9483       game.sokoban_fields_still_needed > 0 ||
9484       game.sokoban_objects_still_needed > 0 ||
9485       game.lights_still_needed > 0)
9486   {
9487     int element = Tile[x][y];
9488     int graphic = el2img(element);
9489
9490     if (IS_ANIMATED(graphic))
9491       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9492
9493     return;
9494   }
9495
9496   // do not re-open exit door closed after last player
9497   if (game.all_players_gone)
9498     return;
9499
9500   Tile[x][y] = EL_EM_EXIT_OPENING;
9501
9502   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9503 }
9504
9505 static void CheckExitSteel(int x, int y)
9506 {
9507   if (game.gems_still_needed > 0 ||
9508       game.sokoban_fields_still_needed > 0 ||
9509       game.sokoban_objects_still_needed > 0 ||
9510       game.lights_still_needed > 0)
9511   {
9512     int element = Tile[x][y];
9513     int graphic = el2img(element);
9514
9515     if (IS_ANIMATED(graphic))
9516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9517
9518     return;
9519   }
9520
9521   // do not re-open exit door closed after last player
9522   if (game.all_players_gone)
9523     return;
9524
9525   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9526
9527   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9528 }
9529
9530 static void CheckExitSteelEM(int x, int y)
9531 {
9532   if (game.gems_still_needed > 0 ||
9533       game.sokoban_fields_still_needed > 0 ||
9534       game.sokoban_objects_still_needed > 0 ||
9535       game.lights_still_needed > 0)
9536   {
9537     int element = Tile[x][y];
9538     int graphic = el2img(element);
9539
9540     if (IS_ANIMATED(graphic))
9541       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9542
9543     return;
9544   }
9545
9546   // do not re-open exit door closed after last player
9547   if (game.all_players_gone)
9548     return;
9549
9550   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9551
9552   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9553 }
9554
9555 static void CheckExitSP(int x, int y)
9556 {
9557   if (game.gems_still_needed > 0)
9558   {
9559     int element = Tile[x][y];
9560     int graphic = el2img(element);
9561
9562     if (IS_ANIMATED(graphic))
9563       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9564
9565     return;
9566   }
9567
9568   // do not re-open exit door closed after last player
9569   if (game.all_players_gone)
9570     return;
9571
9572   Tile[x][y] = EL_SP_EXIT_OPENING;
9573
9574   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9575 }
9576
9577 static void CloseAllOpenTimegates(void)
9578 {
9579   int x, y;
9580
9581   SCAN_PLAYFIELD(x, y)
9582   {
9583     int element = Tile[x][y];
9584
9585     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9586     {
9587       Tile[x][y] = EL_TIMEGATE_CLOSING;
9588
9589       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9590     }
9591   }
9592 }
9593
9594 static void DrawTwinkleOnField(int x, int y)
9595 {
9596   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9597     return;
9598
9599   if (Tile[x][y] == EL_BD_DIAMOND)
9600     return;
9601
9602   if (MovDelay[x][y] == 0)      // next animation frame
9603     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9604
9605   if (MovDelay[x][y] != 0)      // wait some time before next frame
9606   {
9607     MovDelay[x][y]--;
9608
9609     DrawLevelElementAnimation(x, y, Tile[x][y]);
9610
9611     if (MovDelay[x][y] != 0)
9612     {
9613       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9614                                            10 - MovDelay[x][y]);
9615
9616       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9617     }
9618   }
9619 }
9620
9621 static void MauerWaechst(int x, int y)
9622 {
9623   int delay = 6;
9624
9625   if (!MovDelay[x][y])          // next animation frame
9626     MovDelay[x][y] = 3 * delay;
9627
9628   if (MovDelay[x][y])           // wait some time before next frame
9629   {
9630     MovDelay[x][y]--;
9631
9632     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9633     {
9634       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9635       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9636
9637       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9638     }
9639
9640     if (!MovDelay[x][y])
9641     {
9642       if (MovDir[x][y] == MV_LEFT)
9643       {
9644         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9645           TEST_DrawLevelField(x - 1, y);
9646       }
9647       else if (MovDir[x][y] == MV_RIGHT)
9648       {
9649         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9650           TEST_DrawLevelField(x + 1, y);
9651       }
9652       else if (MovDir[x][y] == MV_UP)
9653       {
9654         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9655           TEST_DrawLevelField(x, y - 1);
9656       }
9657       else
9658       {
9659         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9660           TEST_DrawLevelField(x, y + 1);
9661       }
9662
9663       Tile[x][y] = Store[x][y];
9664       Store[x][y] = 0;
9665       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9666       TEST_DrawLevelField(x, y);
9667     }
9668   }
9669 }
9670
9671 static void MauerAbleger(int ax, int ay)
9672 {
9673   int element = Tile[ax][ay];
9674   int graphic = el2img(element);
9675   boolean oben_frei = FALSE, unten_frei = FALSE;
9676   boolean links_frei = FALSE, rechts_frei = FALSE;
9677   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9678   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9679   boolean new_wall = FALSE;
9680
9681   if (IS_ANIMATED(graphic))
9682     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9683
9684   if (!MovDelay[ax][ay])        // start building new wall
9685     MovDelay[ax][ay] = 6;
9686
9687   if (MovDelay[ax][ay])         // wait some time before building new wall
9688   {
9689     MovDelay[ax][ay]--;
9690     if (MovDelay[ax][ay])
9691       return;
9692   }
9693
9694   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9695     oben_frei = TRUE;
9696   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9697     unten_frei = TRUE;
9698   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9699     links_frei = TRUE;
9700   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9701     rechts_frei = TRUE;
9702
9703   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9704       element == EL_EXPANDABLE_WALL_ANY)
9705   {
9706     if (oben_frei)
9707     {
9708       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9709       Store[ax][ay-1] = element;
9710       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9711       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9712         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9713                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9714       new_wall = TRUE;
9715     }
9716     if (unten_frei)
9717     {
9718       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9719       Store[ax][ay+1] = element;
9720       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9721       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9722         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9723                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9724       new_wall = TRUE;
9725     }
9726   }
9727
9728   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9729       element == EL_EXPANDABLE_WALL_ANY ||
9730       element == EL_EXPANDABLE_WALL ||
9731       element == EL_BD_EXPANDABLE_WALL)
9732   {
9733     if (links_frei)
9734     {
9735       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9736       Store[ax-1][ay] = element;
9737       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9738       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9739         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9740                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9741       new_wall = TRUE;
9742     }
9743
9744     if (rechts_frei)
9745     {
9746       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9747       Store[ax+1][ay] = element;
9748       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9749       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9750         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9751                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9752       new_wall = TRUE;
9753     }
9754   }
9755
9756   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9757     TEST_DrawLevelField(ax, ay);
9758
9759   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9760     oben_massiv = TRUE;
9761   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9762     unten_massiv = TRUE;
9763   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9764     links_massiv = TRUE;
9765   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9766     rechts_massiv = TRUE;
9767
9768   if (((oben_massiv && unten_massiv) ||
9769        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9770        element == EL_EXPANDABLE_WALL) &&
9771       ((links_massiv && rechts_massiv) ||
9772        element == EL_EXPANDABLE_WALL_VERTICAL))
9773     Tile[ax][ay] = EL_WALL;
9774
9775   if (new_wall)
9776     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9777 }
9778
9779 static void MauerAblegerStahl(int ax, int ay)
9780 {
9781   int element = Tile[ax][ay];
9782   int graphic = el2img(element);
9783   boolean oben_frei = FALSE, unten_frei = FALSE;
9784   boolean links_frei = FALSE, rechts_frei = FALSE;
9785   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9786   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9787   boolean new_wall = FALSE;
9788
9789   if (IS_ANIMATED(graphic))
9790     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9791
9792   if (!MovDelay[ax][ay])        // start building new wall
9793     MovDelay[ax][ay] = 6;
9794
9795   if (MovDelay[ax][ay])         // wait some time before building new wall
9796   {
9797     MovDelay[ax][ay]--;
9798     if (MovDelay[ax][ay])
9799       return;
9800   }
9801
9802   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9803     oben_frei = TRUE;
9804   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9805     unten_frei = TRUE;
9806   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9807     links_frei = TRUE;
9808   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9809     rechts_frei = TRUE;
9810
9811   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9812       element == EL_EXPANDABLE_STEELWALL_ANY)
9813   {
9814     if (oben_frei)
9815     {
9816       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9817       Store[ax][ay-1] = element;
9818       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9819       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9820         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9821                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9822       new_wall = TRUE;
9823     }
9824     if (unten_frei)
9825     {
9826       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9827       Store[ax][ay+1] = element;
9828       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9829       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9830         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9831                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9832       new_wall = TRUE;
9833     }
9834   }
9835
9836   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9837       element == EL_EXPANDABLE_STEELWALL_ANY)
9838   {
9839     if (links_frei)
9840     {
9841       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9842       Store[ax-1][ay] = element;
9843       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9844       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9845         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9846                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9847       new_wall = TRUE;
9848     }
9849
9850     if (rechts_frei)
9851     {
9852       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9853       Store[ax+1][ay] = element;
9854       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9855       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9856         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9857                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9858       new_wall = TRUE;
9859     }
9860   }
9861
9862   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9863     oben_massiv = TRUE;
9864   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9865     unten_massiv = TRUE;
9866   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9867     links_massiv = TRUE;
9868   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9869     rechts_massiv = TRUE;
9870
9871   if (((oben_massiv && unten_massiv) ||
9872        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9873       ((links_massiv && rechts_massiv) ||
9874        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9875     Tile[ax][ay] = EL_STEELWALL;
9876
9877   if (new_wall)
9878     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9879 }
9880
9881 static void CheckForDragon(int x, int y)
9882 {
9883   int i, j;
9884   boolean dragon_found = FALSE;
9885   static int xy[4][2] =
9886   {
9887     { 0, -1 },
9888     { -1, 0 },
9889     { +1, 0 },
9890     { 0, +1 }
9891   };
9892
9893   for (i = 0; i < NUM_DIRECTIONS; i++)
9894   {
9895     for (j = 0; j < 4; j++)
9896     {
9897       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9898
9899       if (IN_LEV_FIELD(xx, yy) &&
9900           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9901       {
9902         if (Tile[xx][yy] == EL_DRAGON)
9903           dragon_found = TRUE;
9904       }
9905       else
9906         break;
9907     }
9908   }
9909
9910   if (!dragon_found)
9911   {
9912     for (i = 0; i < NUM_DIRECTIONS; i++)
9913     {
9914       for (j = 0; j < 3; j++)
9915       {
9916         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9917   
9918         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9919         {
9920           Tile[xx][yy] = EL_EMPTY;
9921           TEST_DrawLevelField(xx, yy);
9922         }
9923         else
9924           break;
9925       }
9926     }
9927   }
9928 }
9929
9930 static void InitBuggyBase(int x, int y)
9931 {
9932   int element = Tile[x][y];
9933   int activating_delay = FRAMES_PER_SECOND / 4;
9934
9935   ChangeDelay[x][y] =
9936     (element == EL_SP_BUGGY_BASE ?
9937      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9938      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9939      activating_delay :
9940      element == EL_SP_BUGGY_BASE_ACTIVE ?
9941      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9942 }
9943
9944 static void WarnBuggyBase(int x, int y)
9945 {
9946   int i;
9947   static int xy[4][2] =
9948   {
9949     { 0, -1 },
9950     { -1, 0 },
9951     { +1, 0 },
9952     { 0, +1 }
9953   };
9954
9955   for (i = 0; i < NUM_DIRECTIONS; i++)
9956   {
9957     int xx = x + xy[i][0];
9958     int yy = y + xy[i][1];
9959
9960     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9961     {
9962       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9963
9964       break;
9965     }
9966   }
9967 }
9968
9969 static void InitTrap(int x, int y)
9970 {
9971   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9972 }
9973
9974 static void ActivateTrap(int x, int y)
9975 {
9976   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9977 }
9978
9979 static void ChangeActiveTrap(int x, int y)
9980 {
9981   int graphic = IMG_TRAP_ACTIVE;
9982
9983   // if new animation frame was drawn, correct crumbled sand border
9984   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9985     TEST_DrawLevelFieldCrumbled(x, y);
9986 }
9987
9988 static int getSpecialActionElement(int element, int number, int base_element)
9989 {
9990   return (element != EL_EMPTY ? element :
9991           number != -1 ? base_element + number - 1 :
9992           EL_EMPTY);
9993 }
9994
9995 static int getModifiedActionNumber(int value_old, int operator, int operand,
9996                                    int value_min, int value_max)
9997 {
9998   int value_new = (operator == CA_MODE_SET      ? operand :
9999                    operator == CA_MODE_ADD      ? value_old + operand :
10000                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10001                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10002                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10003                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10004                    value_old);
10005
10006   return (value_new < value_min ? value_min :
10007           value_new > value_max ? value_max :
10008           value_new);
10009 }
10010
10011 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10012 {
10013   struct ElementInfo *ei = &element_info[element];
10014   struct ElementChangeInfo *change = &ei->change_page[page];
10015   int target_element = change->target_element;
10016   int action_type = change->action_type;
10017   int action_mode = change->action_mode;
10018   int action_arg = change->action_arg;
10019   int action_element = change->action_element;
10020   int i;
10021
10022   if (!change->has_action)
10023     return;
10024
10025   // ---------- determine action paramater values -----------------------------
10026
10027   int level_time_value =
10028     (level.time > 0 ? TimeLeft :
10029      TimePlayed);
10030
10031   int action_arg_element_raw =
10032     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10033      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10034      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10035      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10036      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10037      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10038      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10039      EL_EMPTY);
10040   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10041
10042   int action_arg_direction =
10043     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10044      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10045      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10046      change->actual_trigger_side :
10047      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10048      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10049      MV_NONE);
10050
10051   int action_arg_number_min =
10052     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10053      CA_ARG_MIN);
10054
10055   int action_arg_number_max =
10056     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10057      action_type == CA_SET_LEVEL_GEMS ? 999 :
10058      action_type == CA_SET_LEVEL_TIME ? 9999 :
10059      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10060      action_type == CA_SET_CE_VALUE ? 9999 :
10061      action_type == CA_SET_CE_SCORE ? 9999 :
10062      CA_ARG_MAX);
10063
10064   int action_arg_number_reset =
10065     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10066      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10067      action_type == CA_SET_LEVEL_TIME ? level.time :
10068      action_type == CA_SET_LEVEL_SCORE ? 0 :
10069      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10070      action_type == CA_SET_CE_SCORE ? 0 :
10071      0);
10072
10073   int action_arg_number =
10074     (action_arg <= CA_ARG_MAX ? action_arg :
10075      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10076      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10077      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10078      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10079      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10080      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10081      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10082      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10083      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10084      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10085      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10086      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10087      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10088      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10089      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10090      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10091      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10092      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10093      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10094      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10095      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10096      -1);
10097
10098   int action_arg_number_old =
10099     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10100      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10101      action_type == CA_SET_LEVEL_SCORE ? game.score :
10102      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10103      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10104      0);
10105
10106   int action_arg_number_new =
10107     getModifiedActionNumber(action_arg_number_old,
10108                             action_mode, action_arg_number,
10109                             action_arg_number_min, action_arg_number_max);
10110
10111   int trigger_player_bits =
10112     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10113      change->actual_trigger_player_bits : change->trigger_player);
10114
10115   int action_arg_player_bits =
10116     (action_arg >= CA_ARG_PLAYER_1 &&
10117      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10118      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10119      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10120      PLAYER_BITS_ANY);
10121
10122   // ---------- execute action  -----------------------------------------------
10123
10124   switch (action_type)
10125   {
10126     case CA_NO_ACTION:
10127     {
10128       return;
10129     }
10130
10131     // ---------- level actions  ----------------------------------------------
10132
10133     case CA_RESTART_LEVEL:
10134     {
10135       game.restart_level = TRUE;
10136
10137       break;
10138     }
10139
10140     case CA_SHOW_ENVELOPE:
10141     {
10142       int element = getSpecialActionElement(action_arg_element,
10143                                             action_arg_number, EL_ENVELOPE_1);
10144
10145       if (IS_ENVELOPE(element))
10146         local_player->show_envelope = element;
10147
10148       break;
10149     }
10150
10151     case CA_SET_LEVEL_TIME:
10152     {
10153       if (level.time > 0)       // only modify limited time value
10154       {
10155         TimeLeft = action_arg_number_new;
10156
10157         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10158
10159         DisplayGameControlValues();
10160
10161         if (!TimeLeft && setup.time_limit)
10162           for (i = 0; i < MAX_PLAYERS; i++)
10163             KillPlayer(&stored_player[i]);
10164       }
10165
10166       break;
10167     }
10168
10169     case CA_SET_LEVEL_SCORE:
10170     {
10171       game.score = action_arg_number_new;
10172
10173       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10174
10175       DisplayGameControlValues();
10176
10177       break;
10178     }
10179
10180     case CA_SET_LEVEL_GEMS:
10181     {
10182       game.gems_still_needed = action_arg_number_new;
10183
10184       game.snapshot.collected_item = TRUE;
10185
10186       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10187
10188       DisplayGameControlValues();
10189
10190       break;
10191     }
10192
10193     case CA_SET_LEVEL_WIND:
10194     {
10195       game.wind_direction = action_arg_direction;
10196
10197       break;
10198     }
10199
10200     case CA_SET_LEVEL_RANDOM_SEED:
10201     {
10202       // ensure that setting a new random seed while playing is predictable
10203       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10204
10205       break;
10206     }
10207
10208     // ---------- player actions  ---------------------------------------------
10209
10210     case CA_MOVE_PLAYER:
10211     case CA_MOVE_PLAYER_NEW:
10212     {
10213       // automatically move to the next field in specified direction
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215         if (trigger_player_bits & (1 << i))
10216           if (action_type == CA_MOVE_PLAYER ||
10217               stored_player[i].MovPos == 0)
10218             stored_player[i].programmed_action = action_arg_direction;
10219
10220       break;
10221     }
10222
10223     case CA_EXIT_PLAYER:
10224     {
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226         if (action_arg_player_bits & (1 << i))
10227           ExitPlayer(&stored_player[i]);
10228
10229       if (game.players_still_needed == 0)
10230         LevelSolved();
10231
10232       break;
10233     }
10234
10235     case CA_KILL_PLAYER:
10236     {
10237       for (i = 0; i < MAX_PLAYERS; i++)
10238         if (action_arg_player_bits & (1 << i))
10239           KillPlayer(&stored_player[i]);
10240
10241       break;
10242     }
10243
10244     case CA_SET_PLAYER_KEYS:
10245     {
10246       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10247       int element = getSpecialActionElement(action_arg_element,
10248                                             action_arg_number, EL_KEY_1);
10249
10250       if (IS_KEY(element))
10251       {
10252         for (i = 0; i < MAX_PLAYERS; i++)
10253         {
10254           if (trigger_player_bits & (1 << i))
10255           {
10256             stored_player[i].key[KEY_NR(element)] = key_state;
10257
10258             DrawGameDoorValues();
10259           }
10260         }
10261       }
10262
10263       break;
10264     }
10265
10266     case CA_SET_PLAYER_SPEED:
10267     {
10268       for (i = 0; i < MAX_PLAYERS; i++)
10269       {
10270         if (trigger_player_bits & (1 << i))
10271         {
10272           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10273
10274           if (action_arg == CA_ARG_SPEED_FASTER &&
10275               stored_player[i].cannot_move)
10276           {
10277             action_arg_number = STEPSIZE_VERY_SLOW;
10278           }
10279           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10280                    action_arg == CA_ARG_SPEED_FASTER)
10281           {
10282             action_arg_number = 2;
10283             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10284                            CA_MODE_MULTIPLY);
10285           }
10286           else if (action_arg == CA_ARG_NUMBER_RESET)
10287           {
10288             action_arg_number = level.initial_player_stepsize[i];
10289           }
10290
10291           move_stepsize =
10292             getModifiedActionNumber(move_stepsize,
10293                                     action_mode,
10294                                     action_arg_number,
10295                                     action_arg_number_min,
10296                                     action_arg_number_max);
10297
10298           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10299         }
10300       }
10301
10302       break;
10303     }
10304
10305     case CA_SET_PLAYER_SHIELD:
10306     {
10307       for (i = 0; i < MAX_PLAYERS; i++)
10308       {
10309         if (trigger_player_bits & (1 << i))
10310         {
10311           if (action_arg == CA_ARG_SHIELD_OFF)
10312           {
10313             stored_player[i].shield_normal_time_left = 0;
10314             stored_player[i].shield_deadly_time_left = 0;
10315           }
10316           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10317           {
10318             stored_player[i].shield_normal_time_left = 999999;
10319           }
10320           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10321           {
10322             stored_player[i].shield_normal_time_left = 999999;
10323             stored_player[i].shield_deadly_time_left = 999999;
10324           }
10325         }
10326       }
10327
10328       break;
10329     }
10330
10331     case CA_SET_PLAYER_GRAVITY:
10332     {
10333       for (i = 0; i < MAX_PLAYERS; i++)
10334       {
10335         if (trigger_player_bits & (1 << i))
10336         {
10337           stored_player[i].gravity =
10338             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10339              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10340              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10341              stored_player[i].gravity);
10342         }
10343       }
10344
10345       break;
10346     }
10347
10348     case CA_SET_PLAYER_ARTWORK:
10349     {
10350       for (i = 0; i < MAX_PLAYERS; i++)
10351       {
10352         if (trigger_player_bits & (1 << i))
10353         {
10354           int artwork_element = action_arg_element;
10355
10356           if (action_arg == CA_ARG_ELEMENT_RESET)
10357             artwork_element =
10358               (level.use_artwork_element[i] ? level.artwork_element[i] :
10359                stored_player[i].element_nr);
10360
10361           if (stored_player[i].artwork_element != artwork_element)
10362             stored_player[i].Frame = 0;
10363
10364           stored_player[i].artwork_element = artwork_element;
10365
10366           SetPlayerWaiting(&stored_player[i], FALSE);
10367
10368           // set number of special actions for bored and sleeping animation
10369           stored_player[i].num_special_action_bored =
10370             get_num_special_action(artwork_element,
10371                                    ACTION_BORING_1, ACTION_BORING_LAST);
10372           stored_player[i].num_special_action_sleeping =
10373             get_num_special_action(artwork_element,
10374                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10375         }
10376       }
10377
10378       break;
10379     }
10380
10381     case CA_SET_PLAYER_INVENTORY:
10382     {
10383       for (i = 0; i < MAX_PLAYERS; i++)
10384       {
10385         struct PlayerInfo *player = &stored_player[i];
10386         int j, k;
10387
10388         if (trigger_player_bits & (1 << i))
10389         {
10390           int inventory_element = action_arg_element;
10391
10392           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10393               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10394               action_arg == CA_ARG_ELEMENT_ACTION)
10395           {
10396             int element = inventory_element;
10397             int collect_count = element_info[element].collect_count_initial;
10398
10399             if (!IS_CUSTOM_ELEMENT(element))
10400               collect_count = 1;
10401
10402             if (collect_count == 0)
10403               player->inventory_infinite_element = element;
10404             else
10405               for (k = 0; k < collect_count; k++)
10406                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10407                   player->inventory_element[player->inventory_size++] =
10408                     element;
10409           }
10410           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10411                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10412                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10413           {
10414             if (player->inventory_infinite_element != EL_UNDEFINED &&
10415                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10416                                      action_arg_element_raw))
10417               player->inventory_infinite_element = EL_UNDEFINED;
10418
10419             for (k = 0, j = 0; j < player->inventory_size; j++)
10420             {
10421               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10422                                         action_arg_element_raw))
10423                 player->inventory_element[k++] = player->inventory_element[j];
10424             }
10425
10426             player->inventory_size = k;
10427           }
10428           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10429           {
10430             if (player->inventory_size > 0)
10431             {
10432               for (j = 0; j < player->inventory_size - 1; j++)
10433                 player->inventory_element[j] = player->inventory_element[j + 1];
10434
10435               player->inventory_size--;
10436             }
10437           }
10438           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10439           {
10440             if (player->inventory_size > 0)
10441               player->inventory_size--;
10442           }
10443           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10444           {
10445             player->inventory_infinite_element = EL_UNDEFINED;
10446             player->inventory_size = 0;
10447           }
10448           else if (action_arg == CA_ARG_INVENTORY_RESET)
10449           {
10450             player->inventory_infinite_element = EL_UNDEFINED;
10451             player->inventory_size = 0;
10452
10453             if (level.use_initial_inventory[i])
10454             {
10455               for (j = 0; j < level.initial_inventory_size[i]; j++)
10456               {
10457                 int element = level.initial_inventory_content[i][j];
10458                 int collect_count = element_info[element].collect_count_initial;
10459
10460                 if (!IS_CUSTOM_ELEMENT(element))
10461                   collect_count = 1;
10462
10463                 if (collect_count == 0)
10464                   player->inventory_infinite_element = element;
10465                 else
10466                   for (k = 0; k < collect_count; k++)
10467                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10468                       player->inventory_element[player->inventory_size++] =
10469                         element;
10470               }
10471             }
10472           }
10473         }
10474       }
10475
10476       break;
10477     }
10478
10479     // ---------- CE actions  -------------------------------------------------
10480
10481     case CA_SET_CE_VALUE:
10482     {
10483       int last_ce_value = CustomValue[x][y];
10484
10485       CustomValue[x][y] = action_arg_number_new;
10486
10487       if (CustomValue[x][y] != last_ce_value)
10488       {
10489         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10490         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10491
10492         if (CustomValue[x][y] == 0)
10493         {
10494           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10495           ChangeCount[x][y] = 0;        // allow at least one more change
10496
10497           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10498           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10499         }
10500       }
10501
10502       break;
10503     }
10504
10505     case CA_SET_CE_SCORE:
10506     {
10507       int last_ce_score = ei->collect_score;
10508
10509       ei->collect_score = action_arg_number_new;
10510
10511       if (ei->collect_score != last_ce_score)
10512       {
10513         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10514         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10515
10516         if (ei->collect_score == 0)
10517         {
10518           int xx, yy;
10519
10520           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10521           ChangeCount[x][y] = 0;        // allow at least one more change
10522
10523           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10524           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10525
10526           /*
10527             This is a very special case that seems to be a mixture between
10528             CheckElementChange() and CheckTriggeredElementChange(): while
10529             the first one only affects single elements that are triggered
10530             directly, the second one affects multiple elements in the playfield
10531             that are triggered indirectly by another element. This is a third
10532             case: Changing the CE score always affects multiple identical CEs,
10533             so every affected CE must be checked, not only the single CE for
10534             which the CE score was changed in the first place (as every instance
10535             of that CE shares the same CE score, and therefore also can change)!
10536           */
10537           SCAN_PLAYFIELD(xx, yy)
10538           {
10539             if (Tile[xx][yy] == element)
10540               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10541                                  CE_SCORE_GETS_ZERO);
10542           }
10543         }
10544       }
10545
10546       break;
10547     }
10548
10549     case CA_SET_CE_ARTWORK:
10550     {
10551       int artwork_element = action_arg_element;
10552       boolean reset_frame = FALSE;
10553       int xx, yy;
10554
10555       if (action_arg == CA_ARG_ELEMENT_RESET)
10556         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10557                            element);
10558
10559       if (ei->gfx_element != artwork_element)
10560         reset_frame = TRUE;
10561
10562       ei->gfx_element = artwork_element;
10563
10564       SCAN_PLAYFIELD(xx, yy)
10565       {
10566         if (Tile[xx][yy] == element)
10567         {
10568           if (reset_frame)
10569           {
10570             ResetGfxAnimation(xx, yy);
10571             ResetRandomAnimationValue(xx, yy);
10572           }
10573
10574           TEST_DrawLevelField(xx, yy);
10575         }
10576       }
10577
10578       break;
10579     }
10580
10581     // ---------- engine actions  ---------------------------------------------
10582
10583     case CA_SET_ENGINE_SCAN_MODE:
10584     {
10585       InitPlayfieldScanMode(action_arg);
10586
10587       break;
10588     }
10589
10590     default:
10591       break;
10592   }
10593 }
10594
10595 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10596 {
10597   int old_element = Tile[x][y];
10598   int new_element = GetElementFromGroupElement(element);
10599   int previous_move_direction = MovDir[x][y];
10600   int last_ce_value = CustomValue[x][y];
10601   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10602   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10603   boolean add_player_onto_element = (new_element_is_player &&
10604                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10605                                      IS_WALKABLE(old_element));
10606
10607   if (!add_player_onto_element)
10608   {
10609     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10610       RemoveMovingField(x, y);
10611     else
10612       RemoveField(x, y);
10613
10614     Tile[x][y] = new_element;
10615
10616     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10617       MovDir[x][y] = previous_move_direction;
10618
10619     if (element_info[new_element].use_last_ce_value)
10620       CustomValue[x][y] = last_ce_value;
10621
10622     InitField_WithBug1(x, y, FALSE);
10623
10624     new_element = Tile[x][y];   // element may have changed
10625
10626     ResetGfxAnimation(x, y);
10627     ResetRandomAnimationValue(x, y);
10628
10629     TEST_DrawLevelField(x, y);
10630
10631     if (GFX_CRUMBLED(new_element))
10632       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10633   }
10634
10635   // check if element under the player changes from accessible to unaccessible
10636   // (needed for special case of dropping element which then changes)
10637   // (must be checked after creating new element for walkable group elements)
10638   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10639       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10640   {
10641     Bang(x, y);
10642
10643     return;
10644   }
10645
10646   // "ChangeCount" not set yet to allow "entered by player" change one time
10647   if (new_element_is_player)
10648     RelocatePlayer(x, y, new_element);
10649
10650   if (is_change)
10651     ChangeCount[x][y]++;        // count number of changes in the same frame
10652
10653   TestIfBadThingTouchesPlayer(x, y);
10654   TestIfPlayerTouchesCustomElement(x, y);
10655   TestIfElementTouchesCustomElement(x, y);
10656 }
10657
10658 static void CreateField(int x, int y, int element)
10659 {
10660   CreateFieldExt(x, y, element, FALSE);
10661 }
10662
10663 static void CreateElementFromChange(int x, int y, int element)
10664 {
10665   element = GET_VALID_RUNTIME_ELEMENT(element);
10666
10667   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10668   {
10669     int old_element = Tile[x][y];
10670
10671     // prevent changed element from moving in same engine frame
10672     // unless both old and new element can either fall or move
10673     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10674         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10675       Stop[x][y] = TRUE;
10676   }
10677
10678   CreateFieldExt(x, y, element, TRUE);
10679 }
10680
10681 static boolean ChangeElement(int x, int y, int element, int page)
10682 {
10683   struct ElementInfo *ei = &element_info[element];
10684   struct ElementChangeInfo *change = &ei->change_page[page];
10685   int ce_value = CustomValue[x][y];
10686   int ce_score = ei->collect_score;
10687   int target_element;
10688   int old_element = Tile[x][y];
10689
10690   // always use default change event to prevent running into a loop
10691   if (ChangeEvent[x][y] == -1)
10692     ChangeEvent[x][y] = CE_DELAY;
10693
10694   if (ChangeEvent[x][y] == CE_DELAY)
10695   {
10696     // reset actual trigger element, trigger player and action element
10697     change->actual_trigger_element = EL_EMPTY;
10698     change->actual_trigger_player = EL_EMPTY;
10699     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10700     change->actual_trigger_side = CH_SIDE_NONE;
10701     change->actual_trigger_ce_value = 0;
10702     change->actual_trigger_ce_score = 0;
10703   }
10704
10705   // do not change elements more than a specified maximum number of changes
10706   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10707     return FALSE;
10708
10709   ChangeCount[x][y]++;          // count number of changes in the same frame
10710
10711   if (change->explode)
10712   {
10713     Bang(x, y);
10714
10715     return TRUE;
10716   }
10717
10718   if (change->use_target_content)
10719   {
10720     boolean complete_replace = TRUE;
10721     boolean can_replace[3][3];
10722     int xx, yy;
10723
10724     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10725     {
10726       boolean is_empty;
10727       boolean is_walkable;
10728       boolean is_diggable;
10729       boolean is_collectible;
10730       boolean is_removable;
10731       boolean is_destructible;
10732       int ex = x + xx - 1;
10733       int ey = y + yy - 1;
10734       int content_element = change->target_content.e[xx][yy];
10735       int e;
10736
10737       can_replace[xx][yy] = TRUE;
10738
10739       if (ex == x && ey == y)   // do not check changing element itself
10740         continue;
10741
10742       if (content_element == EL_EMPTY_SPACE)
10743       {
10744         can_replace[xx][yy] = FALSE;    // do not replace border with space
10745
10746         continue;
10747       }
10748
10749       if (!IN_LEV_FIELD(ex, ey))
10750       {
10751         can_replace[xx][yy] = FALSE;
10752         complete_replace = FALSE;
10753
10754         continue;
10755       }
10756
10757       e = Tile[ex][ey];
10758
10759       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10760         e = MovingOrBlocked2Element(ex, ey);
10761
10762       is_empty = (IS_FREE(ex, ey) ||
10763                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10764
10765       is_walkable     = (is_empty || IS_WALKABLE(e));
10766       is_diggable     = (is_empty || IS_DIGGABLE(e));
10767       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10768       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10769       is_removable    = (is_diggable || is_collectible);
10770
10771       can_replace[xx][yy] =
10772         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10773           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10774           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10775           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10776           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10777           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10778          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10779
10780       if (!can_replace[xx][yy])
10781         complete_replace = FALSE;
10782     }
10783
10784     if (!change->only_if_complete || complete_replace)
10785     {
10786       boolean something_has_changed = FALSE;
10787
10788       if (change->only_if_complete && change->use_random_replace &&
10789           RND(100) < change->random_percentage)
10790         return FALSE;
10791
10792       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10793       {
10794         int ex = x + xx - 1;
10795         int ey = y + yy - 1;
10796         int content_element;
10797
10798         if (can_replace[xx][yy] && (!change->use_random_replace ||
10799                                     RND(100) < change->random_percentage))
10800         {
10801           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10802             RemoveMovingField(ex, ey);
10803
10804           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10805
10806           content_element = change->target_content.e[xx][yy];
10807           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10808                                               ce_value, ce_score);
10809
10810           CreateElementFromChange(ex, ey, target_element);
10811
10812           something_has_changed = TRUE;
10813
10814           // for symmetry reasons, freeze newly created border elements
10815           if (ex != x || ey != y)
10816             Stop[ex][ey] = TRUE;        // no more moving in this frame
10817         }
10818       }
10819
10820       if (something_has_changed)
10821       {
10822         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10823         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10824       }
10825     }
10826   }
10827   else
10828   {
10829     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10830                                         ce_value, ce_score);
10831
10832     if (element == EL_DIAGONAL_GROWING ||
10833         element == EL_DIAGONAL_SHRINKING)
10834     {
10835       target_element = Store[x][y];
10836
10837       Store[x][y] = EL_EMPTY;
10838     }
10839
10840     // special case: element changes to player (and may be kept if walkable)
10841     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10842       CreateElementFromChange(x, y, EL_EMPTY);
10843
10844     CreateElementFromChange(x, y, target_element);
10845
10846     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10847     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10848   }
10849
10850   // this uses direct change before indirect change
10851   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10852
10853   return TRUE;
10854 }
10855
10856 static void HandleElementChange(int x, int y, int page)
10857 {
10858   int element = MovingOrBlocked2Element(x, y);
10859   struct ElementInfo *ei = &element_info[element];
10860   struct ElementChangeInfo *change = &ei->change_page[page];
10861   boolean handle_action_before_change = FALSE;
10862
10863 #ifdef DEBUG
10864   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10865       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10866   {
10867     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10868           x, y, element, element_info[element].token_name);
10869     Debug("game:playing:HandleElementChange", "This should never happen!");
10870   }
10871 #endif
10872
10873   // this can happen with classic bombs on walkable, changing elements
10874   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10875   {
10876     return;
10877   }
10878
10879   if (ChangeDelay[x][y] == 0)           // initialize element change
10880   {
10881     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10882
10883     if (change->can_change)
10884     {
10885       // !!! not clear why graphic animation should be reset at all here !!!
10886       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10887       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10888
10889       /*
10890         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10891
10892         When using an animation frame delay of 1 (this only happens with
10893         "sp_zonk.moving.left/right" in the classic graphics), the default
10894         (non-moving) animation shows wrong animation frames (while the
10895         moving animation, like "sp_zonk.moving.left/right", is correct,
10896         so this graphical bug never shows up with the classic graphics).
10897         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10898         be drawn instead of the correct frames 0,1,2,3. This is caused by
10899         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10900         an element change: First when the change delay ("ChangeDelay[][]")
10901         counter has reached zero after decrementing, then a second time in
10902         the next frame (after "GfxFrame[][]" was already incremented) when
10903         "ChangeDelay[][]" is reset to the initial delay value again.
10904
10905         This causes frame 0 to be drawn twice, while the last frame won't
10906         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10907
10908         As some animations may already be cleverly designed around this bug
10909         (at least the "Snake Bite" snake tail animation does this), it cannot
10910         simply be fixed here without breaking such existing animations.
10911         Unfortunately, it cannot easily be detected if a graphics set was
10912         designed "before" or "after" the bug was fixed. As a workaround,
10913         a new graphics set option "game.graphics_engine_version" was added
10914         to be able to specify the game's major release version for which the
10915         graphics set was designed, which can then be used to decide if the
10916         bugfix should be used (version 4 and above) or not (version 3 or
10917         below, or if no version was specified at all, as with old sets).
10918
10919         (The wrong/fixed animation frames can be tested with the test level set
10920         "test_gfxframe" and level "000", which contains a specially prepared
10921         custom element at level position (x/y) == (11/9) which uses the zonk
10922         animation mentioned above. Using "game.graphics_engine_version: 4"
10923         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10924         This can also be seen from the debug output for this test element.)
10925       */
10926
10927       // when a custom element is about to change (for example by change delay),
10928       // do not reset graphic animation when the custom element is moving
10929       if (game.graphics_engine_version < 4 &&
10930           !IS_MOVING(x, y))
10931       {
10932         ResetGfxAnimation(x, y);
10933         ResetRandomAnimationValue(x, y);
10934       }
10935
10936       if (change->pre_change_function)
10937         change->pre_change_function(x, y);
10938     }
10939   }
10940
10941   ChangeDelay[x][y]--;
10942
10943   if (ChangeDelay[x][y] != 0)           // continue element change
10944   {
10945     if (change->can_change)
10946     {
10947       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10948
10949       if (IS_ANIMATED(graphic))
10950         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10951
10952       if (change->change_function)
10953         change->change_function(x, y);
10954     }
10955   }
10956   else                                  // finish element change
10957   {
10958     if (ChangePage[x][y] != -1)         // remember page from delayed change
10959     {
10960       page = ChangePage[x][y];
10961       ChangePage[x][y] = -1;
10962
10963       change = &ei->change_page[page];
10964     }
10965
10966     if (IS_MOVING(x, y))                // never change a running system ;-)
10967     {
10968       ChangeDelay[x][y] = 1;            // try change after next move step
10969       ChangePage[x][y] = page;          // remember page to use for change
10970
10971       return;
10972     }
10973
10974     // special case: set new level random seed before changing element
10975     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10976       handle_action_before_change = TRUE;
10977
10978     if (change->has_action && handle_action_before_change)
10979       ExecuteCustomElementAction(x, y, element, page);
10980
10981     if (change->can_change)
10982     {
10983       if (ChangeElement(x, y, element, page))
10984       {
10985         if (change->post_change_function)
10986           change->post_change_function(x, y);
10987       }
10988     }
10989
10990     if (change->has_action && !handle_action_before_change)
10991       ExecuteCustomElementAction(x, y, element, page);
10992   }
10993 }
10994
10995 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10996                                               int trigger_element,
10997                                               int trigger_event,
10998                                               int trigger_player,
10999                                               int trigger_side,
11000                                               int trigger_page)
11001 {
11002   boolean change_done_any = FALSE;
11003   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11004   int i;
11005
11006   if (!(trigger_events[trigger_element][trigger_event]))
11007     return FALSE;
11008
11009   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11010
11011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11012   {
11013     int element = EL_CUSTOM_START + i;
11014     boolean change_done = FALSE;
11015     int p;
11016
11017     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11018         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11019       continue;
11020
11021     for (p = 0; p < element_info[element].num_change_pages; p++)
11022     {
11023       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11024
11025       if (change->can_change_or_has_action &&
11026           change->has_event[trigger_event] &&
11027           change->trigger_side & trigger_side &&
11028           change->trigger_player & trigger_player &&
11029           change->trigger_page & trigger_page_bits &&
11030           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11031       {
11032         change->actual_trigger_element = trigger_element;
11033         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11034         change->actual_trigger_player_bits = trigger_player;
11035         change->actual_trigger_side = trigger_side;
11036         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11037         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11038
11039         if ((change->can_change && !change_done) || change->has_action)
11040         {
11041           int x, y;
11042
11043           SCAN_PLAYFIELD(x, y)
11044           {
11045             if (Tile[x][y] == element)
11046             {
11047               if (change->can_change && !change_done)
11048               {
11049                 // if element already changed in this frame, not only prevent
11050                 // another element change (checked in ChangeElement()), but
11051                 // also prevent additional element actions for this element
11052
11053                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11054                     !level.use_action_after_change_bug)
11055                   continue;
11056
11057                 ChangeDelay[x][y] = 1;
11058                 ChangeEvent[x][y] = trigger_event;
11059
11060                 HandleElementChange(x, y, p);
11061               }
11062               else if (change->has_action)
11063               {
11064                 // if element already changed in this frame, not only prevent
11065                 // another element change (checked in ChangeElement()), but
11066                 // also prevent additional element actions for this element
11067
11068                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11069                     !level.use_action_after_change_bug)
11070                   continue;
11071
11072                 ExecuteCustomElementAction(x, y, element, p);
11073                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11074               }
11075             }
11076           }
11077
11078           if (change->can_change)
11079           {
11080             change_done = TRUE;
11081             change_done_any = TRUE;
11082           }
11083         }
11084       }
11085     }
11086   }
11087
11088   RECURSION_LOOP_DETECTION_END();
11089
11090   return change_done_any;
11091 }
11092
11093 static boolean CheckElementChangeExt(int x, int y,
11094                                      int element,
11095                                      int trigger_element,
11096                                      int trigger_event,
11097                                      int trigger_player,
11098                                      int trigger_side)
11099 {
11100   boolean change_done = FALSE;
11101   int p;
11102
11103   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11104       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11105     return FALSE;
11106
11107   if (Tile[x][y] == EL_BLOCKED)
11108   {
11109     Blocked2Moving(x, y, &x, &y);
11110     element = Tile[x][y];
11111   }
11112
11113   // check if element has already changed or is about to change after moving
11114   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11115        Tile[x][y] != element) ||
11116
11117       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11118        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11119         ChangePage[x][y] != -1)))
11120     return FALSE;
11121
11122   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11123
11124   for (p = 0; p < element_info[element].num_change_pages; p++)
11125   {
11126     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11127
11128     /* check trigger element for all events where the element that is checked
11129        for changing interacts with a directly adjacent element -- this is
11130        different to element changes that affect other elements to change on the
11131        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11132     boolean check_trigger_element =
11133       (trigger_event == CE_TOUCHING_X ||
11134        trigger_event == CE_HITTING_X ||
11135        trigger_event == CE_HIT_BY_X ||
11136        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11137
11138     if (change->can_change_or_has_action &&
11139         change->has_event[trigger_event] &&
11140         change->trigger_side & trigger_side &&
11141         change->trigger_player & trigger_player &&
11142         (!check_trigger_element ||
11143          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11144     {
11145       change->actual_trigger_element = trigger_element;
11146       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11147       change->actual_trigger_player_bits = trigger_player;
11148       change->actual_trigger_side = trigger_side;
11149       change->actual_trigger_ce_value = CustomValue[x][y];
11150       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11151
11152       // special case: trigger element not at (x,y) position for some events
11153       if (check_trigger_element)
11154       {
11155         static struct
11156         {
11157           int dx, dy;
11158         } move_xy[] =
11159           {
11160             {  0,  0 },
11161             { -1,  0 },
11162             { +1,  0 },
11163             {  0,  0 },
11164             {  0, -1 },
11165             {  0,  0 }, { 0, 0 }, { 0, 0 },
11166             {  0, +1 }
11167           };
11168
11169         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11170         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11171
11172         change->actual_trigger_ce_value = CustomValue[xx][yy];
11173         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11174       }
11175
11176       if (change->can_change && !change_done)
11177       {
11178         ChangeDelay[x][y] = 1;
11179         ChangeEvent[x][y] = trigger_event;
11180
11181         HandleElementChange(x, y, p);
11182
11183         change_done = TRUE;
11184       }
11185       else if (change->has_action)
11186       {
11187         ExecuteCustomElementAction(x, y, element, p);
11188         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11189       }
11190     }
11191   }
11192
11193   RECURSION_LOOP_DETECTION_END();
11194
11195   return change_done;
11196 }
11197
11198 static void PlayPlayerSound(struct PlayerInfo *player)
11199 {
11200   int jx = player->jx, jy = player->jy;
11201   int sound_element = player->artwork_element;
11202   int last_action = player->last_action_waiting;
11203   int action = player->action_waiting;
11204
11205   if (player->is_waiting)
11206   {
11207     if (action != last_action)
11208       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11209     else
11210       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11211   }
11212   else
11213   {
11214     if (action != last_action)
11215       StopSound(element_info[sound_element].sound[last_action]);
11216
11217     if (last_action == ACTION_SLEEPING)
11218       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11219   }
11220 }
11221
11222 static void PlayAllPlayersSound(void)
11223 {
11224   int i;
11225
11226   for (i = 0; i < MAX_PLAYERS; i++)
11227     if (stored_player[i].active)
11228       PlayPlayerSound(&stored_player[i]);
11229 }
11230
11231 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11232 {
11233   boolean last_waiting = player->is_waiting;
11234   int move_dir = player->MovDir;
11235
11236   player->dir_waiting = move_dir;
11237   player->last_action_waiting = player->action_waiting;
11238
11239   if (is_waiting)
11240   {
11241     if (!last_waiting)          // not waiting -> waiting
11242     {
11243       player->is_waiting = TRUE;
11244
11245       player->frame_counter_bored =
11246         FrameCounter +
11247         game.player_boring_delay_fixed +
11248         GetSimpleRandom(game.player_boring_delay_random);
11249       player->frame_counter_sleeping =
11250         FrameCounter +
11251         game.player_sleeping_delay_fixed +
11252         GetSimpleRandom(game.player_sleeping_delay_random);
11253
11254       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11255     }
11256
11257     if (game.player_sleeping_delay_fixed +
11258         game.player_sleeping_delay_random > 0 &&
11259         player->anim_delay_counter == 0 &&
11260         player->post_delay_counter == 0 &&
11261         FrameCounter >= player->frame_counter_sleeping)
11262       player->is_sleeping = TRUE;
11263     else if (game.player_boring_delay_fixed +
11264              game.player_boring_delay_random > 0 &&
11265              FrameCounter >= player->frame_counter_bored)
11266       player->is_bored = TRUE;
11267
11268     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11269                               player->is_bored ? ACTION_BORING :
11270                               ACTION_WAITING);
11271
11272     if (player->is_sleeping && player->use_murphy)
11273     {
11274       // special case for sleeping Murphy when leaning against non-free tile
11275
11276       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11277           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11278            !IS_MOVING(player->jx - 1, player->jy)))
11279         move_dir = MV_LEFT;
11280       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11281                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11282                 !IS_MOVING(player->jx + 1, player->jy)))
11283         move_dir = MV_RIGHT;
11284       else
11285         player->is_sleeping = FALSE;
11286
11287       player->dir_waiting = move_dir;
11288     }
11289
11290     if (player->is_sleeping)
11291     {
11292       if (player->num_special_action_sleeping > 0)
11293       {
11294         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11295         {
11296           int last_special_action = player->special_action_sleeping;
11297           int num_special_action = player->num_special_action_sleeping;
11298           int special_action =
11299             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11300              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11301              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11302              last_special_action + 1 : ACTION_SLEEPING);
11303           int special_graphic =
11304             el_act_dir2img(player->artwork_element, special_action, move_dir);
11305
11306           player->anim_delay_counter =
11307             graphic_info[special_graphic].anim_delay_fixed +
11308             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11309           player->post_delay_counter =
11310             graphic_info[special_graphic].post_delay_fixed +
11311             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11312
11313           player->special_action_sleeping = special_action;
11314         }
11315
11316         if (player->anim_delay_counter > 0)
11317         {
11318           player->action_waiting = player->special_action_sleeping;
11319           player->anim_delay_counter--;
11320         }
11321         else if (player->post_delay_counter > 0)
11322         {
11323           player->post_delay_counter--;
11324         }
11325       }
11326     }
11327     else if (player->is_bored)
11328     {
11329       if (player->num_special_action_bored > 0)
11330       {
11331         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11332         {
11333           int special_action =
11334             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11335           int special_graphic =
11336             el_act_dir2img(player->artwork_element, special_action, move_dir);
11337
11338           player->anim_delay_counter =
11339             graphic_info[special_graphic].anim_delay_fixed +
11340             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11341           player->post_delay_counter =
11342             graphic_info[special_graphic].post_delay_fixed +
11343             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11344
11345           player->special_action_bored = special_action;
11346         }
11347
11348         if (player->anim_delay_counter > 0)
11349         {
11350           player->action_waiting = player->special_action_bored;
11351           player->anim_delay_counter--;
11352         }
11353         else if (player->post_delay_counter > 0)
11354         {
11355           player->post_delay_counter--;
11356         }
11357       }
11358     }
11359   }
11360   else if (last_waiting)        // waiting -> not waiting
11361   {
11362     player->is_waiting = FALSE;
11363     player->is_bored = FALSE;
11364     player->is_sleeping = FALSE;
11365
11366     player->frame_counter_bored = -1;
11367     player->frame_counter_sleeping = -1;
11368
11369     player->anim_delay_counter = 0;
11370     player->post_delay_counter = 0;
11371
11372     player->dir_waiting = player->MovDir;
11373     player->action_waiting = ACTION_DEFAULT;
11374
11375     player->special_action_bored = ACTION_DEFAULT;
11376     player->special_action_sleeping = ACTION_DEFAULT;
11377   }
11378 }
11379
11380 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11381 {
11382   if ((!player->is_moving  && player->was_moving) ||
11383       (player->MovPos == 0 && player->was_moving) ||
11384       (player->is_snapping && !player->was_snapping) ||
11385       (player->is_dropping && !player->was_dropping))
11386   {
11387     if (!CheckSaveEngineSnapshotToList())
11388       return;
11389
11390     player->was_moving = FALSE;
11391     player->was_snapping = TRUE;
11392     player->was_dropping = TRUE;
11393   }
11394   else
11395   {
11396     if (player->is_moving)
11397       player->was_moving = TRUE;
11398
11399     if (!player->is_snapping)
11400       player->was_snapping = FALSE;
11401
11402     if (!player->is_dropping)
11403       player->was_dropping = FALSE;
11404   }
11405
11406   static struct MouseActionInfo mouse_action_last = { 0 };
11407   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11408   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11409
11410   if (new_released)
11411     CheckSaveEngineSnapshotToList();
11412
11413   mouse_action_last = mouse_action;
11414 }
11415
11416 static void CheckSingleStepMode(struct PlayerInfo *player)
11417 {
11418   if (tape.single_step && tape.recording && !tape.pausing)
11419   {
11420     // as it is called "single step mode", just return to pause mode when the
11421     // player stopped moving after one tile (or never starts moving at all)
11422     // (reverse logic needed here in case single step mode used in team mode)
11423     if (player->is_moving ||
11424         player->is_pushing ||
11425         player->is_dropping_pressed ||
11426         player->effective_mouse_action.button)
11427       game.enter_single_step_mode = FALSE;
11428   }
11429
11430   CheckSaveEngineSnapshot(player);
11431 }
11432
11433 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11434 {
11435   int left      = player_action & JOY_LEFT;
11436   int right     = player_action & JOY_RIGHT;
11437   int up        = player_action & JOY_UP;
11438   int down      = player_action & JOY_DOWN;
11439   int button1   = player_action & JOY_BUTTON_1;
11440   int button2   = player_action & JOY_BUTTON_2;
11441   int dx        = (left ? -1 : right ? 1 : 0);
11442   int dy        = (up   ? -1 : down  ? 1 : 0);
11443
11444   if (!player->active || tape.pausing)
11445     return 0;
11446
11447   if (player_action)
11448   {
11449     if (button1)
11450       SnapField(player, dx, dy);
11451     else
11452     {
11453       if (button2)
11454         DropElement(player);
11455
11456       MovePlayer(player, dx, dy);
11457     }
11458
11459     CheckSingleStepMode(player);
11460
11461     SetPlayerWaiting(player, FALSE);
11462
11463     return player_action;
11464   }
11465   else
11466   {
11467     // no actions for this player (no input at player's configured device)
11468
11469     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11470     SnapField(player, 0, 0);
11471     CheckGravityMovementWhenNotMoving(player);
11472
11473     if (player->MovPos == 0)
11474       SetPlayerWaiting(player, TRUE);
11475
11476     if (player->MovPos == 0)    // needed for tape.playing
11477       player->is_moving = FALSE;
11478
11479     player->is_dropping = FALSE;
11480     player->is_dropping_pressed = FALSE;
11481     player->drop_pressed_delay = 0;
11482
11483     CheckSingleStepMode(player);
11484
11485     return 0;
11486   }
11487 }
11488
11489 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11490                                          byte *tape_action)
11491 {
11492   if (!tape.use_mouse_actions)
11493     return;
11494
11495   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11496   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11497   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11498 }
11499
11500 static void SetTapeActionFromMouseAction(byte *tape_action,
11501                                          struct MouseActionInfo *mouse_action)
11502 {
11503   if (!tape.use_mouse_actions)
11504     return;
11505
11506   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11507   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11508   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11509 }
11510
11511 static void CheckLevelSolved(void)
11512 {
11513   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11514   {
11515     if (game_em.level_solved &&
11516         !game_em.game_over)                             // game won
11517     {
11518       LevelSolved();
11519
11520       game_em.game_over = TRUE;
11521
11522       game.all_players_gone = TRUE;
11523     }
11524
11525     if (game_em.game_over)                              // game lost
11526       game.all_players_gone = TRUE;
11527   }
11528   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11529   {
11530     if (game_sp.level_solved &&
11531         !game_sp.game_over)                             // game won
11532     {
11533       LevelSolved();
11534
11535       game_sp.game_over = TRUE;
11536
11537       game.all_players_gone = TRUE;
11538     }
11539
11540     if (game_sp.game_over)                              // game lost
11541       game.all_players_gone = TRUE;
11542   }
11543   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11544   {
11545     if (game_mm.level_solved &&
11546         !game_mm.game_over)                             // game won
11547     {
11548       LevelSolved();
11549
11550       game_mm.game_over = TRUE;
11551
11552       game.all_players_gone = TRUE;
11553     }
11554
11555     if (game_mm.game_over)                              // game lost
11556       game.all_players_gone = TRUE;
11557   }
11558 }
11559
11560 static void CheckLevelTime(void)
11561 {
11562   int i;
11563
11564   if (TimeFrames >= FRAMES_PER_SECOND)
11565   {
11566     TimeFrames = 0;
11567     TapeTime++;
11568
11569     for (i = 0; i < MAX_PLAYERS; i++)
11570     {
11571       struct PlayerInfo *player = &stored_player[i];
11572
11573       if (SHIELD_ON(player))
11574       {
11575         player->shield_normal_time_left--;
11576
11577         if (player->shield_deadly_time_left > 0)
11578           player->shield_deadly_time_left--;
11579       }
11580     }
11581
11582     if (!game.LevelSolved && !level.use_step_counter)
11583     {
11584       TimePlayed++;
11585
11586       if (TimeLeft > 0)
11587       {
11588         TimeLeft--;
11589
11590         if (TimeLeft <= 10 && setup.time_limit)
11591           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11592
11593         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11594            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11595
11596         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11597
11598         if (!TimeLeft && setup.time_limit)
11599         {
11600           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11601             game_em.lev->killed_out_of_time = TRUE;
11602           else
11603             for (i = 0; i < MAX_PLAYERS; i++)
11604               KillPlayer(&stored_player[i]);
11605         }
11606       }
11607       else if (game.no_time_limit && !game.all_players_gone)
11608       {
11609         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11610       }
11611
11612       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11613     }
11614
11615     if (tape.recording || tape.playing)
11616       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11617   }
11618
11619   if (tape.recording || tape.playing)
11620     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11621
11622   UpdateAndDisplayGameControlValues();
11623 }
11624
11625 void AdvanceFrameAndPlayerCounters(int player_nr)
11626 {
11627   int i;
11628
11629   // advance frame counters (global frame counter and time frame counter)
11630   FrameCounter++;
11631   TimeFrames++;
11632
11633   // advance player counters (counters for move delay, move animation etc.)
11634   for (i = 0; i < MAX_PLAYERS; i++)
11635   {
11636     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11637     int move_delay_value = stored_player[i].move_delay_value;
11638     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11639
11640     if (!advance_player_counters)       // not all players may be affected
11641       continue;
11642
11643     if (move_frames == 0)       // less than one move per game frame
11644     {
11645       int stepsize = TILEX / move_delay_value;
11646       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11647       int count = (stored_player[i].is_moving ?
11648                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11649
11650       if (count % delay == 0)
11651         move_frames = 1;
11652     }
11653
11654     stored_player[i].Frame += move_frames;
11655
11656     if (stored_player[i].MovPos != 0)
11657       stored_player[i].StepFrame += move_frames;
11658
11659     if (stored_player[i].move_delay > 0)
11660       stored_player[i].move_delay--;
11661
11662     // due to bugs in previous versions, counter must count up, not down
11663     if (stored_player[i].push_delay != -1)
11664       stored_player[i].push_delay++;
11665
11666     if (stored_player[i].drop_delay > 0)
11667       stored_player[i].drop_delay--;
11668
11669     if (stored_player[i].is_dropping_pressed)
11670       stored_player[i].drop_pressed_delay++;
11671   }
11672 }
11673
11674 void StartGameActions(boolean init_network_game, boolean record_tape,
11675                       int random_seed)
11676 {
11677   unsigned int new_random_seed = InitRND(random_seed);
11678
11679   if (record_tape)
11680     TapeStartRecording(new_random_seed);
11681
11682   if (init_network_game)
11683   {
11684     SendToServer_LevelFile();
11685     SendToServer_StartPlaying();
11686
11687     return;
11688   }
11689
11690   InitGame();
11691 }
11692
11693 static void GameActionsExt(void)
11694 {
11695 #if 0
11696   static unsigned int game_frame_delay = 0;
11697 #endif
11698   unsigned int game_frame_delay_value;
11699   byte *recorded_player_action;
11700   byte summarized_player_action = 0;
11701   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11702   int i;
11703
11704   // detect endless loops, caused by custom element programming
11705   if (recursion_loop_detected && recursion_loop_depth == 0)
11706   {
11707     char *message = getStringCat3("Internal Error! Element ",
11708                                   EL_NAME(recursion_loop_element),
11709                                   " caused endless loop! Quit the game?");
11710
11711     Warn("element '%s' caused endless loop in game engine",
11712          EL_NAME(recursion_loop_element));
11713
11714     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11715
11716     recursion_loop_detected = FALSE;    // if game should be continued
11717
11718     free(message);
11719
11720     return;
11721   }
11722
11723   if (game.restart_level)
11724     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11725
11726   CheckLevelSolved();
11727
11728   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11729     GameWon();
11730
11731   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11732     TapeStop();
11733
11734   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11735     return;
11736
11737   game_frame_delay_value =
11738     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11739
11740   if (tape.playing && tape.warp_forward && !tape.pausing)
11741     game_frame_delay_value = 0;
11742
11743   SetVideoFrameDelay(game_frame_delay_value);
11744
11745   // (de)activate virtual buttons depending on current game status
11746   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11747   {
11748     if (game.all_players_gone)  // if no players there to be controlled anymore
11749       SetOverlayActive(FALSE);
11750     else if (!tape.playing)     // if game continues after tape stopped playing
11751       SetOverlayActive(TRUE);
11752   }
11753
11754 #if 0
11755 #if 0
11756   // ---------- main game synchronization point ----------
11757
11758   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11759
11760   Debug("game:playing:skip", "skip == %d", skip);
11761
11762 #else
11763   // ---------- main game synchronization point ----------
11764
11765   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11766 #endif
11767 #endif
11768
11769   if (network_playing && !network_player_action_received)
11770   {
11771     // try to get network player actions in time
11772
11773     // last chance to get network player actions without main loop delay
11774     HandleNetworking();
11775
11776     // game was quit by network peer
11777     if (game_status != GAME_MODE_PLAYING)
11778       return;
11779
11780     // check if network player actions still missing and game still running
11781     if (!network_player_action_received && !checkGameEnded())
11782       return;           // failed to get network player actions in time
11783
11784     // do not yet reset "network_player_action_received" (for tape.pausing)
11785   }
11786
11787   if (tape.pausing)
11788     return;
11789
11790   // at this point we know that we really continue executing the game
11791
11792   network_player_action_received = FALSE;
11793
11794   // when playing tape, read previously recorded player input from tape data
11795   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11796
11797   local_player->effective_mouse_action = local_player->mouse_action;
11798
11799   if (recorded_player_action != NULL)
11800     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11801                                  recorded_player_action);
11802
11803   // TapePlayAction() may return NULL when toggling to "pause before death"
11804   if (tape.pausing)
11805     return;
11806
11807   if (tape.set_centered_player)
11808   {
11809     game.centered_player_nr_next = tape.centered_player_nr_next;
11810     game.set_centered_player = TRUE;
11811   }
11812
11813   for (i = 0; i < MAX_PLAYERS; i++)
11814   {
11815     summarized_player_action |= stored_player[i].action;
11816
11817     if (!network_playing && (game.team_mode || tape.playing))
11818       stored_player[i].effective_action = stored_player[i].action;
11819   }
11820
11821   if (network_playing && !checkGameEnded())
11822     SendToServer_MovePlayer(summarized_player_action);
11823
11824   // summarize all actions at local players mapped input device position
11825   // (this allows using different input devices in single player mode)
11826   if (!network.enabled && !game.team_mode)
11827     stored_player[map_player_action[local_player->index_nr]].effective_action =
11828       summarized_player_action;
11829
11830   // summarize all actions at centered player in local team mode
11831   if (tape.recording &&
11832       setup.team_mode && !network.enabled &&
11833       setup.input_on_focus &&
11834       game.centered_player_nr != -1)
11835   {
11836     for (i = 0; i < MAX_PLAYERS; i++)
11837       stored_player[map_player_action[i]].effective_action =
11838         (i == game.centered_player_nr ? summarized_player_action : 0);
11839   }
11840
11841   if (recorded_player_action != NULL)
11842     for (i = 0; i < MAX_PLAYERS; i++)
11843       stored_player[i].effective_action = recorded_player_action[i];
11844
11845   for (i = 0; i < MAX_PLAYERS; i++)
11846   {
11847     tape_action[i] = stored_player[i].effective_action;
11848
11849     /* (this may happen in the RND game engine if a player was not present on
11850        the playfield on level start, but appeared later from a custom element */
11851     if (setup.team_mode &&
11852         tape.recording &&
11853         tape_action[i] &&
11854         !tape.player_participates[i])
11855       tape.player_participates[i] = TRUE;
11856   }
11857
11858   SetTapeActionFromMouseAction(tape_action,
11859                                &local_player->effective_mouse_action);
11860
11861   // only record actions from input devices, but not programmed actions
11862   if (tape.recording)
11863     TapeRecordAction(tape_action);
11864
11865   // remember if game was played (especially after tape stopped playing)
11866   if (!tape.playing && summarized_player_action)
11867     game.GamePlayed = TRUE;
11868
11869 #if USE_NEW_PLAYER_ASSIGNMENTS
11870   // !!! also map player actions in single player mode !!!
11871   // if (game.team_mode)
11872   if (1)
11873   {
11874     byte mapped_action[MAX_PLAYERS];
11875
11876 #if DEBUG_PLAYER_ACTIONS
11877     for (i = 0; i < MAX_PLAYERS; i++)
11878       DebugContinued("", "%d, ", stored_player[i].effective_action);
11879 #endif
11880
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11883
11884     for (i = 0; i < MAX_PLAYERS; i++)
11885       stored_player[i].effective_action = mapped_action[i];
11886
11887 #if DEBUG_PLAYER_ACTIONS
11888     DebugContinued("", "=> ");
11889     for (i = 0; i < MAX_PLAYERS; i++)
11890       DebugContinued("", "%d, ", stored_player[i].effective_action);
11891     DebugContinued("game:playing:player", "\n");
11892 #endif
11893   }
11894 #if DEBUG_PLAYER_ACTIONS
11895   else
11896   {
11897     for (i = 0; i < MAX_PLAYERS; i++)
11898       DebugContinued("", "%d, ", stored_player[i].effective_action);
11899     DebugContinued("game:playing:player", "\n");
11900   }
11901 #endif
11902 #endif
11903
11904   for (i = 0; i < MAX_PLAYERS; i++)
11905   {
11906     // allow engine snapshot in case of changed movement attempt
11907     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11908         (stored_player[i].effective_action & KEY_MOTION))
11909       game.snapshot.changed_action = TRUE;
11910
11911     // allow engine snapshot in case of snapping/dropping attempt
11912     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11913         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11914       game.snapshot.changed_action = TRUE;
11915
11916     game.snapshot.last_action[i] = stored_player[i].effective_action;
11917   }
11918
11919   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11920   {
11921     GameActions_EM_Main();
11922   }
11923   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11924   {
11925     GameActions_SP_Main();
11926   }
11927   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11928   {
11929     GameActions_MM_Main();
11930   }
11931   else
11932   {
11933     GameActions_RND_Main();
11934   }
11935
11936   BlitScreenToBitmap(backbuffer);
11937
11938   CheckLevelSolved();
11939   CheckLevelTime();
11940
11941   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11942
11943   if (global.show_frames_per_second)
11944   {
11945     static unsigned int fps_counter = 0;
11946     static int fps_frames = 0;
11947     unsigned int fps_delay_ms = Counter() - fps_counter;
11948
11949     fps_frames++;
11950
11951     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11952     {
11953       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11954
11955       fps_frames = 0;
11956       fps_counter = Counter();
11957
11958       // always draw FPS to screen after FPS value was updated
11959       redraw_mask |= REDRAW_FPS;
11960     }
11961
11962     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11963     if (GetDrawDeactivationMask() == REDRAW_NONE)
11964       redraw_mask |= REDRAW_FPS;
11965   }
11966 }
11967
11968 static void GameActions_CheckSaveEngineSnapshot(void)
11969 {
11970   if (!game.snapshot.save_snapshot)
11971     return;
11972
11973   // clear flag for saving snapshot _before_ saving snapshot
11974   game.snapshot.save_snapshot = FALSE;
11975
11976   SaveEngineSnapshotToList();
11977 }
11978
11979 void GameActions(void)
11980 {
11981   GameActionsExt();
11982
11983   GameActions_CheckSaveEngineSnapshot();
11984 }
11985
11986 void GameActions_EM_Main(void)
11987 {
11988   byte effective_action[MAX_PLAYERS];
11989   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11990   int i;
11991
11992   for (i = 0; i < MAX_PLAYERS; i++)
11993     effective_action[i] = stored_player[i].effective_action;
11994
11995   GameActions_EM(effective_action, warp_mode);
11996 }
11997
11998 void GameActions_SP_Main(void)
11999 {
12000   byte effective_action[MAX_PLAYERS];
12001   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12002   int i;
12003
12004   for (i = 0; i < MAX_PLAYERS; i++)
12005     effective_action[i] = stored_player[i].effective_action;
12006
12007   GameActions_SP(effective_action, warp_mode);
12008
12009   for (i = 0; i < MAX_PLAYERS; i++)
12010   {
12011     if (stored_player[i].force_dropping)
12012       stored_player[i].action |= KEY_BUTTON_DROP;
12013
12014     stored_player[i].force_dropping = FALSE;
12015   }
12016 }
12017
12018 void GameActions_MM_Main(void)
12019 {
12020   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12021
12022   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12023 }
12024
12025 void GameActions_RND_Main(void)
12026 {
12027   GameActions_RND();
12028 }
12029
12030 void GameActions_RND(void)
12031 {
12032   static struct MouseActionInfo mouse_action_last = { 0 };
12033   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12034   int magic_wall_x = 0, magic_wall_y = 0;
12035   int i, x, y, element, graphic, last_gfx_frame;
12036
12037   InitPlayfieldScanModeVars();
12038
12039   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12040   {
12041     SCAN_PLAYFIELD(x, y)
12042     {
12043       ChangeCount[x][y] = 0;
12044       ChangeEvent[x][y] = -1;
12045     }
12046   }
12047
12048   if (game.set_centered_player)
12049   {
12050     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12051
12052     // switching to "all players" only possible if all players fit to screen
12053     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12054     {
12055       game.centered_player_nr_next = game.centered_player_nr;
12056       game.set_centered_player = FALSE;
12057     }
12058
12059     // do not switch focus to non-existing (or non-active) player
12060     if (game.centered_player_nr_next >= 0 &&
12061         !stored_player[game.centered_player_nr_next].active)
12062     {
12063       game.centered_player_nr_next = game.centered_player_nr;
12064       game.set_centered_player = FALSE;
12065     }
12066   }
12067
12068   if (game.set_centered_player &&
12069       ScreenMovPos == 0)        // screen currently aligned at tile position
12070   {
12071     int sx, sy;
12072
12073     if (game.centered_player_nr_next == -1)
12074     {
12075       setScreenCenteredToAllPlayers(&sx, &sy);
12076     }
12077     else
12078     {
12079       sx = stored_player[game.centered_player_nr_next].jx;
12080       sy = stored_player[game.centered_player_nr_next].jy;
12081     }
12082
12083     game.centered_player_nr = game.centered_player_nr_next;
12084     game.set_centered_player = FALSE;
12085
12086     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12087     DrawGameDoorValues();
12088   }
12089
12090   // check single step mode (set flag and clear again if any player is active)
12091   game.enter_single_step_mode =
12092     (tape.single_step && tape.recording && !tape.pausing);
12093
12094   for (i = 0; i < MAX_PLAYERS; i++)
12095   {
12096     int actual_player_action = stored_player[i].effective_action;
12097
12098 #if 1
12099     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12100        - rnd_equinox_tetrachloride 048
12101        - rnd_equinox_tetrachloride_ii 096
12102        - rnd_emanuel_schmieg 002
12103        - doctor_sloan_ww 001, 020
12104     */
12105     if (stored_player[i].MovPos == 0)
12106       CheckGravityMovement(&stored_player[i]);
12107 #endif
12108
12109     // overwrite programmed action with tape action
12110     if (stored_player[i].programmed_action)
12111       actual_player_action = stored_player[i].programmed_action;
12112
12113     PlayerActions(&stored_player[i], actual_player_action);
12114
12115     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12116   }
12117
12118   // single step pause mode may already have been toggled by "ScrollPlayer()"
12119   if (game.enter_single_step_mode && !tape.pausing)
12120     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12121
12122   ScrollScreen(NULL, SCROLL_GO_ON);
12123
12124   /* for backwards compatibility, the following code emulates a fixed bug that
12125      occured when pushing elements (causing elements that just made their last
12126      pushing step to already (if possible) make their first falling step in the
12127      same game frame, which is bad); this code is also needed to use the famous
12128      "spring push bug" which is used in older levels and might be wanted to be
12129      used also in newer levels, but in this case the buggy pushing code is only
12130      affecting the "spring" element and no other elements */
12131
12132   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12133   {
12134     for (i = 0; i < MAX_PLAYERS; i++)
12135     {
12136       struct PlayerInfo *player = &stored_player[i];
12137       int x = player->jx;
12138       int y = player->jy;
12139
12140       if (player->active && player->is_pushing && player->is_moving &&
12141           IS_MOVING(x, y) &&
12142           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12143            Tile[x][y] == EL_SPRING))
12144       {
12145         ContinueMoving(x, y);
12146
12147         // continue moving after pushing (this is actually a bug)
12148         if (!IS_MOVING(x, y))
12149           Stop[x][y] = FALSE;
12150       }
12151     }
12152   }
12153
12154   SCAN_PLAYFIELD(x, y)
12155   {
12156     Last[x][y] = Tile[x][y];
12157
12158     ChangeCount[x][y] = 0;
12159     ChangeEvent[x][y] = -1;
12160
12161     // this must be handled before main playfield loop
12162     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12163     {
12164       MovDelay[x][y]--;
12165       if (MovDelay[x][y] <= 0)
12166         RemoveField(x, y);
12167     }
12168
12169     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12170     {
12171       MovDelay[x][y]--;
12172       if (MovDelay[x][y] <= 0)
12173       {
12174         int element = Store[x][y];
12175         int move_direction = MovDir[x][y];
12176         int player_index_bit = Store2[x][y];
12177
12178         Store[x][y] = 0;
12179         Store2[x][y] = 0;
12180
12181         RemoveField(x, y);
12182         TEST_DrawLevelField(x, y);
12183
12184         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12185
12186         if (IS_ENVELOPE(element))
12187           local_player->show_envelope = element;
12188       }
12189     }
12190
12191 #if DEBUG
12192     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12193     {
12194       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12195             x, y);
12196       Debug("game:playing:GameActions_RND", "This should never happen!");
12197
12198       ChangePage[x][y] = -1;
12199     }
12200 #endif
12201
12202     Stop[x][y] = FALSE;
12203     if (WasJustMoving[x][y] > 0)
12204       WasJustMoving[x][y]--;
12205     if (WasJustFalling[x][y] > 0)
12206       WasJustFalling[x][y]--;
12207     if (CheckCollision[x][y] > 0)
12208       CheckCollision[x][y]--;
12209     if (CheckImpact[x][y] > 0)
12210       CheckImpact[x][y]--;
12211
12212     GfxFrame[x][y]++;
12213
12214     /* reset finished pushing action (not done in ContinueMoving() to allow
12215        continuous pushing animation for elements with zero push delay) */
12216     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12217     {
12218       ResetGfxAnimation(x, y);
12219       TEST_DrawLevelField(x, y);
12220     }
12221
12222 #if DEBUG
12223     if (IS_BLOCKED(x, y))
12224     {
12225       int oldx, oldy;
12226
12227       Blocked2Moving(x, y, &oldx, &oldy);
12228       if (!IS_MOVING(oldx, oldy))
12229       {
12230         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12231         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12232         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12233         Debug("game:playing:GameActions_RND", "This should never happen!");
12234       }
12235     }
12236 #endif
12237   }
12238
12239   if (mouse_action.button)
12240   {
12241     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12242     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12243
12244     x = mouse_action.lx;
12245     y = mouse_action.ly;
12246     element = Tile[x][y];
12247
12248     if (new_button)
12249     {
12250       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12251       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12252                                          ch_button);
12253     }
12254
12255     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12256     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12257                                        ch_button);
12258   }
12259
12260   SCAN_PLAYFIELD(x, y)
12261   {
12262     element = Tile[x][y];
12263     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12264     last_gfx_frame = GfxFrame[x][y];
12265
12266     ResetGfxFrame(x, y);
12267
12268     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12269       DrawLevelGraphicAnimation(x, y, graphic);
12270
12271     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12272         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12273       ResetRandomAnimationValue(x, y);
12274
12275     SetRandomAnimationValue(x, y);
12276
12277     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12278
12279     if (IS_INACTIVE(element))
12280     {
12281       if (IS_ANIMATED(graphic))
12282         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12283
12284       continue;
12285     }
12286
12287     // this may take place after moving, so 'element' may have changed
12288     if (IS_CHANGING(x, y) &&
12289         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12290     {
12291       int page = element_info[element].event_page_nr[CE_DELAY];
12292
12293       HandleElementChange(x, y, page);
12294
12295       element = Tile[x][y];
12296       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12297     }
12298
12299     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12300     {
12301       StartMoving(x, y);
12302
12303       element = Tile[x][y];
12304       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12305
12306       if (IS_ANIMATED(graphic) &&
12307           !IS_MOVING(x, y) &&
12308           !Stop[x][y])
12309         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310
12311       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12312         TEST_DrawTwinkleOnField(x, y);
12313     }
12314     else if (element == EL_ACID)
12315     {
12316       if (!Stop[x][y])
12317         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12318     }
12319     else if ((element == EL_EXIT_OPEN ||
12320               element == EL_EM_EXIT_OPEN ||
12321               element == EL_SP_EXIT_OPEN ||
12322               element == EL_STEEL_EXIT_OPEN ||
12323               element == EL_EM_STEEL_EXIT_OPEN ||
12324               element == EL_SP_TERMINAL ||
12325               element == EL_SP_TERMINAL_ACTIVE ||
12326               element == EL_EXTRA_TIME ||
12327               element == EL_SHIELD_NORMAL ||
12328               element == EL_SHIELD_DEADLY) &&
12329              IS_ANIMATED(graphic))
12330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12331     else if (IS_MOVING(x, y))
12332       ContinueMoving(x, y);
12333     else if (IS_ACTIVE_BOMB(element))
12334       CheckDynamite(x, y);
12335     else if (element == EL_AMOEBA_GROWING)
12336       AmoebaGrowing(x, y);
12337     else if (element == EL_AMOEBA_SHRINKING)
12338       AmoebaShrinking(x, y);
12339
12340 #if !USE_NEW_AMOEBA_CODE
12341     else if (IS_AMOEBALIVE(element))
12342       AmoebaReproduce(x, y);
12343 #endif
12344
12345     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12346       Life(x, y);
12347     else if (element == EL_EXIT_CLOSED)
12348       CheckExit(x, y);
12349     else if (element == EL_EM_EXIT_CLOSED)
12350       CheckExitEM(x, y);
12351     else if (element == EL_STEEL_EXIT_CLOSED)
12352       CheckExitSteel(x, y);
12353     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12354       CheckExitSteelEM(x, y);
12355     else if (element == EL_SP_EXIT_CLOSED)
12356       CheckExitSP(x, y);
12357     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12358              element == EL_EXPANDABLE_STEELWALL_GROWING)
12359       MauerWaechst(x, y);
12360     else if (element == EL_EXPANDABLE_WALL ||
12361              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12362              element == EL_EXPANDABLE_WALL_VERTICAL ||
12363              element == EL_EXPANDABLE_WALL_ANY ||
12364              element == EL_BD_EXPANDABLE_WALL)
12365       MauerAbleger(x, y);
12366     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12367              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12368              element == EL_EXPANDABLE_STEELWALL_ANY)
12369       MauerAblegerStahl(x, y);
12370     else if (element == EL_FLAMES)
12371       CheckForDragon(x, y);
12372     else if (element == EL_EXPLOSION)
12373       ; // drawing of correct explosion animation is handled separately
12374     else if (element == EL_ELEMENT_SNAPPING ||
12375              element == EL_DIAGONAL_SHRINKING ||
12376              element == EL_DIAGONAL_GROWING)
12377     {
12378       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12379
12380       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12381     }
12382     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12383       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12384
12385     if (IS_BELT_ACTIVE(element))
12386       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12387
12388     if (game.magic_wall_active)
12389     {
12390       int jx = local_player->jx, jy = local_player->jy;
12391
12392       // play the element sound at the position nearest to the player
12393       if ((element == EL_MAGIC_WALL_FULL ||
12394            element == EL_MAGIC_WALL_ACTIVE ||
12395            element == EL_MAGIC_WALL_EMPTYING ||
12396            element == EL_BD_MAGIC_WALL_FULL ||
12397            element == EL_BD_MAGIC_WALL_ACTIVE ||
12398            element == EL_BD_MAGIC_WALL_EMPTYING ||
12399            element == EL_DC_MAGIC_WALL_FULL ||
12400            element == EL_DC_MAGIC_WALL_ACTIVE ||
12401            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12402           ABS(x - jx) + ABS(y - jy) <
12403           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12404       {
12405         magic_wall_x = x;
12406         magic_wall_y = y;
12407       }
12408     }
12409   }
12410
12411 #if USE_NEW_AMOEBA_CODE
12412   // new experimental amoeba growth stuff
12413   if (!(FrameCounter % 8))
12414   {
12415     static unsigned int random = 1684108901;
12416
12417     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12418     {
12419       x = RND(lev_fieldx);
12420       y = RND(lev_fieldy);
12421       element = Tile[x][y];
12422
12423       if (!IS_PLAYER(x,y) &&
12424           (element == EL_EMPTY ||
12425            CAN_GROW_INTO(element) ||
12426            element == EL_QUICKSAND_EMPTY ||
12427            element == EL_QUICKSAND_FAST_EMPTY ||
12428            element == EL_ACID_SPLASH_LEFT ||
12429            element == EL_ACID_SPLASH_RIGHT))
12430       {
12431         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12432             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12433             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12434             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12435           Tile[x][y] = EL_AMOEBA_DROP;
12436       }
12437
12438       random = random * 129 + 1;
12439     }
12440   }
12441 #endif
12442
12443   game.explosions_delayed = FALSE;
12444
12445   SCAN_PLAYFIELD(x, y)
12446   {
12447     element = Tile[x][y];
12448
12449     if (ExplodeField[x][y])
12450       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12451     else if (element == EL_EXPLOSION)
12452       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12453
12454     ExplodeField[x][y] = EX_TYPE_NONE;
12455   }
12456
12457   game.explosions_delayed = TRUE;
12458
12459   if (game.magic_wall_active)
12460   {
12461     if (!(game.magic_wall_time_left % 4))
12462     {
12463       int element = Tile[magic_wall_x][magic_wall_y];
12464
12465       if (element == EL_BD_MAGIC_WALL_FULL ||
12466           element == EL_BD_MAGIC_WALL_ACTIVE ||
12467           element == EL_BD_MAGIC_WALL_EMPTYING)
12468         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12469       else if (element == EL_DC_MAGIC_WALL_FULL ||
12470                element == EL_DC_MAGIC_WALL_ACTIVE ||
12471                element == EL_DC_MAGIC_WALL_EMPTYING)
12472         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12473       else
12474         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12475     }
12476
12477     if (game.magic_wall_time_left > 0)
12478     {
12479       game.magic_wall_time_left--;
12480
12481       if (!game.magic_wall_time_left)
12482       {
12483         SCAN_PLAYFIELD(x, y)
12484         {
12485           element = Tile[x][y];
12486
12487           if (element == EL_MAGIC_WALL_ACTIVE ||
12488               element == EL_MAGIC_WALL_FULL)
12489           {
12490             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12491             TEST_DrawLevelField(x, y);
12492           }
12493           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12494                    element == EL_BD_MAGIC_WALL_FULL)
12495           {
12496             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12497             TEST_DrawLevelField(x, y);
12498           }
12499           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12500                    element == EL_DC_MAGIC_WALL_FULL)
12501           {
12502             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12503             TEST_DrawLevelField(x, y);
12504           }
12505         }
12506
12507         game.magic_wall_active = FALSE;
12508       }
12509     }
12510   }
12511
12512   if (game.light_time_left > 0)
12513   {
12514     game.light_time_left--;
12515
12516     if (game.light_time_left == 0)
12517       RedrawAllLightSwitchesAndInvisibleElements();
12518   }
12519
12520   if (game.timegate_time_left > 0)
12521   {
12522     game.timegate_time_left--;
12523
12524     if (game.timegate_time_left == 0)
12525       CloseAllOpenTimegates();
12526   }
12527
12528   if (game.lenses_time_left > 0)
12529   {
12530     game.lenses_time_left--;
12531
12532     if (game.lenses_time_left == 0)
12533       RedrawAllInvisibleElementsForLenses();
12534   }
12535
12536   if (game.magnify_time_left > 0)
12537   {
12538     game.magnify_time_left--;
12539
12540     if (game.magnify_time_left == 0)
12541       RedrawAllInvisibleElementsForMagnifier();
12542   }
12543
12544   for (i = 0; i < MAX_PLAYERS; i++)
12545   {
12546     struct PlayerInfo *player = &stored_player[i];
12547
12548     if (SHIELD_ON(player))
12549     {
12550       if (player->shield_deadly_time_left)
12551         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12552       else if (player->shield_normal_time_left)
12553         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12554     }
12555   }
12556
12557 #if USE_DELAYED_GFX_REDRAW
12558   SCAN_PLAYFIELD(x, y)
12559   {
12560     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12561     {
12562       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12563          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12564
12565       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12566         DrawLevelField(x, y);
12567
12568       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12569         DrawLevelFieldCrumbled(x, y);
12570
12571       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12572         DrawLevelFieldCrumbledNeighbours(x, y);
12573
12574       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12575         DrawTwinkleOnField(x, y);
12576     }
12577
12578     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12579   }
12580 #endif
12581
12582   DrawAllPlayers();
12583   PlayAllPlayersSound();
12584
12585   for (i = 0; i < MAX_PLAYERS; i++)
12586   {
12587     struct PlayerInfo *player = &stored_player[i];
12588
12589     if (player->show_envelope != 0 && (!player->active ||
12590                                        player->MovPos == 0))
12591     {
12592       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12593
12594       player->show_envelope = 0;
12595     }
12596   }
12597
12598   // use random number generator in every frame to make it less predictable
12599   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12600     RND(1);
12601
12602   mouse_action_last = mouse_action;
12603 }
12604
12605 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12606 {
12607   int min_x = x, min_y = y, max_x = x, max_y = y;
12608   int scr_fieldx = getScreenFieldSizeX();
12609   int scr_fieldy = getScreenFieldSizeY();
12610   int i;
12611
12612   for (i = 0; i < MAX_PLAYERS; i++)
12613   {
12614     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12615
12616     if (!stored_player[i].active || &stored_player[i] == player)
12617       continue;
12618
12619     min_x = MIN(min_x, jx);
12620     min_y = MIN(min_y, jy);
12621     max_x = MAX(max_x, jx);
12622     max_y = MAX(max_y, jy);
12623   }
12624
12625   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12626 }
12627
12628 static boolean AllPlayersInVisibleScreen(void)
12629 {
12630   int i;
12631
12632   for (i = 0; i < MAX_PLAYERS; i++)
12633   {
12634     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12635
12636     if (!stored_player[i].active)
12637       continue;
12638
12639     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12640       return FALSE;
12641   }
12642
12643   return TRUE;
12644 }
12645
12646 void ScrollLevel(int dx, int dy)
12647 {
12648   int scroll_offset = 2 * TILEX_VAR;
12649   int x, y;
12650
12651   BlitBitmap(drawto_field, drawto_field,
12652              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12653              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12654              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12655              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12656              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12657              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12658
12659   if (dx != 0)
12660   {
12661     x = (dx == 1 ? BX1 : BX2);
12662     for (y = BY1; y <= BY2; y++)
12663       DrawScreenField(x, y);
12664   }
12665
12666   if (dy != 0)
12667   {
12668     y = (dy == 1 ? BY1 : BY2);
12669     for (x = BX1; x <= BX2; x++)
12670       DrawScreenField(x, y);
12671   }
12672
12673   redraw_mask |= REDRAW_FIELD;
12674 }
12675
12676 static boolean canFallDown(struct PlayerInfo *player)
12677 {
12678   int jx = player->jx, jy = player->jy;
12679
12680   return (IN_LEV_FIELD(jx, jy + 1) &&
12681           (IS_FREE(jx, jy + 1) ||
12682            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12683           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12684           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12685 }
12686
12687 static boolean canPassField(int x, int y, int move_dir)
12688 {
12689   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12690   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12691   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12692   int nextx = x + dx;
12693   int nexty = y + dy;
12694   int element = Tile[x][y];
12695
12696   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12697           !CAN_MOVE(element) &&
12698           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12699           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12700           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12701 }
12702
12703 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12704 {
12705   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12706   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12707   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12708   int newx = x + dx;
12709   int newy = y + dy;
12710
12711   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12712           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12713           (IS_DIGGABLE(Tile[newx][newy]) ||
12714            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12715            canPassField(newx, newy, move_dir)));
12716 }
12717
12718 static void CheckGravityMovement(struct PlayerInfo *player)
12719 {
12720   if (player->gravity && !player->programmed_action)
12721   {
12722     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12723     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12724     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12725     int jx = player->jx, jy = player->jy;
12726     boolean player_is_moving_to_valid_field =
12727       (!player_is_snapping &&
12728        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12729         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12730     boolean player_can_fall_down = canFallDown(player);
12731
12732     if (player_can_fall_down &&
12733         !player_is_moving_to_valid_field)
12734       player->programmed_action = MV_DOWN;
12735   }
12736 }
12737
12738 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12739 {
12740   return CheckGravityMovement(player);
12741
12742   if (player->gravity && !player->programmed_action)
12743   {
12744     int jx = player->jx, jy = player->jy;
12745     boolean field_under_player_is_free =
12746       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12747     boolean player_is_standing_on_valid_field =
12748       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12749        (IS_WALKABLE(Tile[jx][jy]) &&
12750         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12751
12752     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12753       player->programmed_action = MV_DOWN;
12754   }
12755 }
12756
12757 /*
12758   MovePlayerOneStep()
12759   -----------------------------------------------------------------------------
12760   dx, dy:               direction (non-diagonal) to try to move the player to
12761   real_dx, real_dy:     direction as read from input device (can be diagonal)
12762 */
12763
12764 boolean MovePlayerOneStep(struct PlayerInfo *player,
12765                           int dx, int dy, int real_dx, int real_dy)
12766 {
12767   int jx = player->jx, jy = player->jy;
12768   int new_jx = jx + dx, new_jy = jy + dy;
12769   int can_move;
12770   boolean player_can_move = !player->cannot_move;
12771
12772   if (!player->active || (!dx && !dy))
12773     return MP_NO_ACTION;
12774
12775   player->MovDir = (dx < 0 ? MV_LEFT :
12776                     dx > 0 ? MV_RIGHT :
12777                     dy < 0 ? MV_UP :
12778                     dy > 0 ? MV_DOWN :  MV_NONE);
12779
12780   if (!IN_LEV_FIELD(new_jx, new_jy))
12781     return MP_NO_ACTION;
12782
12783   if (!player_can_move)
12784   {
12785     if (player->MovPos == 0)
12786     {
12787       player->is_moving = FALSE;
12788       player->is_digging = FALSE;
12789       player->is_collecting = FALSE;
12790       player->is_snapping = FALSE;
12791       player->is_pushing = FALSE;
12792     }
12793   }
12794
12795   if (!network.enabled && game.centered_player_nr == -1 &&
12796       !AllPlayersInSight(player, new_jx, new_jy))
12797     return MP_NO_ACTION;
12798
12799   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12800   if (can_move != MP_MOVING)
12801     return can_move;
12802
12803   // check if DigField() has caused relocation of the player
12804   if (player->jx != jx || player->jy != jy)
12805     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12806
12807   StorePlayer[jx][jy] = 0;
12808   player->last_jx = jx;
12809   player->last_jy = jy;
12810   player->jx = new_jx;
12811   player->jy = new_jy;
12812   StorePlayer[new_jx][new_jy] = player->element_nr;
12813
12814   if (player->move_delay_value_next != -1)
12815   {
12816     player->move_delay_value = player->move_delay_value_next;
12817     player->move_delay_value_next = -1;
12818   }
12819
12820   player->MovPos =
12821     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12822
12823   player->step_counter++;
12824
12825   PlayerVisit[jx][jy] = FrameCounter;
12826
12827   player->is_moving = TRUE;
12828
12829 #if 1
12830   // should better be called in MovePlayer(), but this breaks some tapes
12831   ScrollPlayer(player, SCROLL_INIT);
12832 #endif
12833
12834   return MP_MOVING;
12835 }
12836
12837 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12838 {
12839   int jx = player->jx, jy = player->jy;
12840   int old_jx = jx, old_jy = jy;
12841   int moved = MP_NO_ACTION;
12842
12843   if (!player->active)
12844     return FALSE;
12845
12846   if (!dx && !dy)
12847   {
12848     if (player->MovPos == 0)
12849     {
12850       player->is_moving = FALSE;
12851       player->is_digging = FALSE;
12852       player->is_collecting = FALSE;
12853       player->is_snapping = FALSE;
12854       player->is_pushing = FALSE;
12855     }
12856
12857     return FALSE;
12858   }
12859
12860   if (player->move_delay > 0)
12861     return FALSE;
12862
12863   player->move_delay = -1;              // set to "uninitialized" value
12864
12865   // store if player is automatically moved to next field
12866   player->is_auto_moving = (player->programmed_action != MV_NONE);
12867
12868   // remove the last programmed player action
12869   player->programmed_action = 0;
12870
12871   if (player->MovPos)
12872   {
12873     // should only happen if pre-1.2 tape recordings are played
12874     // this is only for backward compatibility
12875
12876     int original_move_delay_value = player->move_delay_value;
12877
12878 #if DEBUG
12879     Debug("game:playing:MovePlayer",
12880           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12881           tape.counter);
12882 #endif
12883
12884     // scroll remaining steps with finest movement resolution
12885     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12886
12887     while (player->MovPos)
12888     {
12889       ScrollPlayer(player, SCROLL_GO_ON);
12890       ScrollScreen(NULL, SCROLL_GO_ON);
12891
12892       AdvanceFrameAndPlayerCounters(player->index_nr);
12893
12894       DrawAllPlayers();
12895       BackToFront_WithFrameDelay(0);
12896     }
12897
12898     player->move_delay_value = original_move_delay_value;
12899   }
12900
12901   player->is_active = FALSE;
12902
12903   if (player->last_move_dir & MV_HORIZONTAL)
12904   {
12905     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12906       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12907   }
12908   else
12909   {
12910     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12911       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12912   }
12913
12914   if (!moved && !player->is_active)
12915   {
12916     player->is_moving = FALSE;
12917     player->is_digging = FALSE;
12918     player->is_collecting = FALSE;
12919     player->is_snapping = FALSE;
12920     player->is_pushing = FALSE;
12921   }
12922
12923   jx = player->jx;
12924   jy = player->jy;
12925
12926   if (moved & MP_MOVING && !ScreenMovPos &&
12927       (player->index_nr == game.centered_player_nr ||
12928        game.centered_player_nr == -1))
12929   {
12930     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12931
12932     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12933     {
12934       // actual player has left the screen -- scroll in that direction
12935       if (jx != old_jx)         // player has moved horizontally
12936         scroll_x += (jx - old_jx);
12937       else                      // player has moved vertically
12938         scroll_y += (jy - old_jy);
12939     }
12940     else
12941     {
12942       int offset_raw = game.scroll_delay_value;
12943
12944       if (jx != old_jx)         // player has moved horizontally
12945       {
12946         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12947         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12948         int new_scroll_x = jx - MIDPOSX + offset_x;
12949
12950         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12951             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12952           scroll_x = new_scroll_x;
12953
12954         // don't scroll over playfield boundaries
12955         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12956
12957         // don't scroll more than one field at a time
12958         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12959
12960         // don't scroll against the player's moving direction
12961         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12962             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12963           scroll_x = old_scroll_x;
12964       }
12965       else                      // player has moved vertically
12966       {
12967         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12968         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12969         int new_scroll_y = jy - MIDPOSY + offset_y;
12970
12971         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12972             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12973           scroll_y = new_scroll_y;
12974
12975         // don't scroll over playfield boundaries
12976         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12977
12978         // don't scroll more than one field at a time
12979         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12980
12981         // don't scroll against the player's moving direction
12982         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12983             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12984           scroll_y = old_scroll_y;
12985       }
12986     }
12987
12988     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12989     {
12990       if (!network.enabled && game.centered_player_nr == -1 &&
12991           !AllPlayersInVisibleScreen())
12992       {
12993         scroll_x = old_scroll_x;
12994         scroll_y = old_scroll_y;
12995       }
12996       else
12997       {
12998         ScrollScreen(player, SCROLL_INIT);
12999         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13000       }
13001     }
13002   }
13003
13004   player->StepFrame = 0;
13005
13006   if (moved & MP_MOVING)
13007   {
13008     if (old_jx != jx && old_jy == jy)
13009       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13010     else if (old_jx == jx && old_jy != jy)
13011       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13012
13013     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13014
13015     player->last_move_dir = player->MovDir;
13016     player->is_moving = TRUE;
13017     player->is_snapping = FALSE;
13018     player->is_switching = FALSE;
13019     player->is_dropping = FALSE;
13020     player->is_dropping_pressed = FALSE;
13021     player->drop_pressed_delay = 0;
13022
13023 #if 0
13024     // should better be called here than above, but this breaks some tapes
13025     ScrollPlayer(player, SCROLL_INIT);
13026 #endif
13027   }
13028   else
13029   {
13030     CheckGravityMovementWhenNotMoving(player);
13031
13032     player->is_moving = FALSE;
13033
13034     /* at this point, the player is allowed to move, but cannot move right now
13035        (e.g. because of something blocking the way) -- ensure that the player
13036        is also allowed to move in the next frame (in old versions before 3.1.1,
13037        the player was forced to wait again for eight frames before next try) */
13038
13039     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13040       player->move_delay = 0;   // allow direct movement in the next frame
13041   }
13042
13043   if (player->move_delay == -1)         // not yet initialized by DigField()
13044     player->move_delay = player->move_delay_value;
13045
13046   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13047   {
13048     TestIfPlayerTouchesBadThing(jx, jy);
13049     TestIfPlayerTouchesCustomElement(jx, jy);
13050   }
13051
13052   if (!player->active)
13053     RemovePlayer(player);
13054
13055   return moved;
13056 }
13057
13058 void ScrollPlayer(struct PlayerInfo *player, int mode)
13059 {
13060   int jx = player->jx, jy = player->jy;
13061   int last_jx = player->last_jx, last_jy = player->last_jy;
13062   int move_stepsize = TILEX / player->move_delay_value;
13063
13064   if (!player->active)
13065     return;
13066
13067   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13068     return;
13069
13070   if (mode == SCROLL_INIT)
13071   {
13072     player->actual_frame_counter = FrameCounter;
13073     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13074
13075     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13076         Tile[last_jx][last_jy] == EL_EMPTY)
13077     {
13078       int last_field_block_delay = 0;   // start with no blocking at all
13079       int block_delay_adjustment = player->block_delay_adjustment;
13080
13081       // if player blocks last field, add delay for exactly one move
13082       if (player->block_last_field)
13083       {
13084         last_field_block_delay += player->move_delay_value;
13085
13086         // when blocking enabled, prevent moving up despite gravity
13087         if (player->gravity && player->MovDir == MV_UP)
13088           block_delay_adjustment = -1;
13089       }
13090
13091       // add block delay adjustment (also possible when not blocking)
13092       last_field_block_delay += block_delay_adjustment;
13093
13094       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13095       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13096     }
13097
13098     if (player->MovPos != 0)    // player has not yet reached destination
13099       return;
13100   }
13101   else if (!FrameReached(&player->actual_frame_counter, 1))
13102     return;
13103
13104   if (player->MovPos != 0)
13105   {
13106     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13107     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13108
13109     // before DrawPlayer() to draw correct player graphic for this case
13110     if (player->MovPos == 0)
13111       CheckGravityMovement(player);
13112   }
13113
13114   if (player->MovPos == 0)      // player reached destination field
13115   {
13116     if (player->move_delay_reset_counter > 0)
13117     {
13118       player->move_delay_reset_counter--;
13119
13120       if (player->move_delay_reset_counter == 0)
13121       {
13122         // continue with normal speed after quickly moving through gate
13123         HALVE_PLAYER_SPEED(player);
13124
13125         // be able to make the next move without delay
13126         player->move_delay = 0;
13127       }
13128     }
13129
13130     player->last_jx = jx;
13131     player->last_jy = jy;
13132
13133     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13134         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13135         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13136         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13137         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13138         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13139         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13140         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13141     {
13142       ExitPlayer(player);
13143
13144       if (game.players_still_needed == 0 &&
13145           (game.friends_still_needed == 0 ||
13146            IS_SP_ELEMENT(Tile[jx][jy])))
13147         LevelSolved();
13148     }
13149
13150     // this breaks one level: "machine", level 000
13151     {
13152       int move_direction = player->MovDir;
13153       int enter_side = MV_DIR_OPPOSITE(move_direction);
13154       int leave_side = move_direction;
13155       int old_jx = last_jx;
13156       int old_jy = last_jy;
13157       int old_element = Tile[old_jx][old_jy];
13158       int new_element = Tile[jx][jy];
13159
13160       if (IS_CUSTOM_ELEMENT(old_element))
13161         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13162                                    CE_LEFT_BY_PLAYER,
13163                                    player->index_bit, leave_side);
13164
13165       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13166                                           CE_PLAYER_LEAVES_X,
13167                                           player->index_bit, leave_side);
13168
13169       if (IS_CUSTOM_ELEMENT(new_element))
13170         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13171                                    player->index_bit, enter_side);
13172
13173       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13174                                           CE_PLAYER_ENTERS_X,
13175                                           player->index_bit, enter_side);
13176
13177       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13178                                         CE_MOVE_OF_X, move_direction);
13179     }
13180
13181     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13182     {
13183       TestIfPlayerTouchesBadThing(jx, jy);
13184       TestIfPlayerTouchesCustomElement(jx, jy);
13185
13186       /* needed because pushed element has not yet reached its destination,
13187          so it would trigger a change event at its previous field location */
13188       if (!player->is_pushing)
13189         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13190
13191       if (level.finish_dig_collect &&
13192           (player->is_digging || player->is_collecting))
13193       {
13194         int last_element = player->last_removed_element;
13195         int move_direction = player->MovDir;
13196         int enter_side = MV_DIR_OPPOSITE(move_direction);
13197         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13198                             CE_PLAYER_COLLECTS_X);
13199
13200         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13201                                             player->index_bit, enter_side);
13202
13203         player->last_removed_element = EL_UNDEFINED;
13204       }
13205
13206       if (!player->active)
13207         RemovePlayer(player);
13208     }
13209
13210     if (level.use_step_counter)
13211     {
13212       int i;
13213
13214       TimePlayed++;
13215
13216       if (TimeLeft > 0)
13217       {
13218         TimeLeft--;
13219
13220         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13221           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13222
13223         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13224
13225         DisplayGameControlValues();
13226
13227         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13228           for (i = 0; i < MAX_PLAYERS; i++)
13229             KillPlayer(&stored_player[i]);
13230       }
13231       else if (game.no_time_limit && !game.all_players_gone)
13232       {
13233         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13234
13235         DisplayGameControlValues();
13236       }
13237     }
13238
13239     if (tape.single_step && tape.recording && !tape.pausing &&
13240         !player->programmed_action)
13241       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13242
13243     if (!player->programmed_action)
13244       CheckSaveEngineSnapshot(player);
13245   }
13246 }
13247
13248 void ScrollScreen(struct PlayerInfo *player, int mode)
13249 {
13250   static unsigned int screen_frame_counter = 0;
13251
13252   if (mode == SCROLL_INIT)
13253   {
13254     // set scrolling step size according to actual player's moving speed
13255     ScrollStepSize = TILEX / player->move_delay_value;
13256
13257     screen_frame_counter = FrameCounter;
13258     ScreenMovDir = player->MovDir;
13259     ScreenMovPos = player->MovPos;
13260     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13261     return;
13262   }
13263   else if (!FrameReached(&screen_frame_counter, 1))
13264     return;
13265
13266   if (ScreenMovPos)
13267   {
13268     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13269     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13270     redraw_mask |= REDRAW_FIELD;
13271   }
13272   else
13273     ScreenMovDir = MV_NONE;
13274 }
13275
13276 void TestIfPlayerTouchesCustomElement(int x, int y)
13277 {
13278   static int xy[4][2] =
13279   {
13280     { 0, -1 },
13281     { -1, 0 },
13282     { +1, 0 },
13283     { 0, +1 }
13284   };
13285   static int trigger_sides[4][2] =
13286   {
13287     // center side       border side
13288     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13289     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13290     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13291     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13292   };
13293   static int touch_dir[4] =
13294   {
13295     MV_LEFT | MV_RIGHT,
13296     MV_UP   | MV_DOWN,
13297     MV_UP   | MV_DOWN,
13298     MV_LEFT | MV_RIGHT
13299   };
13300   int center_element = Tile[x][y];      // should always be non-moving!
13301   int i;
13302
13303   for (i = 0; i < NUM_DIRECTIONS; i++)
13304   {
13305     int xx = x + xy[i][0];
13306     int yy = y + xy[i][1];
13307     int center_side = trigger_sides[i][0];
13308     int border_side = trigger_sides[i][1];
13309     int border_element;
13310
13311     if (!IN_LEV_FIELD(xx, yy))
13312       continue;
13313
13314     if (IS_PLAYER(x, y))                // player found at center element
13315     {
13316       struct PlayerInfo *player = PLAYERINFO(x, y);
13317
13318       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13319         border_element = Tile[xx][yy];          // may be moving!
13320       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13321         border_element = Tile[xx][yy];
13322       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13323         border_element = MovingOrBlocked2Element(xx, yy);
13324       else
13325         continue;               // center and border element do not touch
13326
13327       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13328                                  player->index_bit, border_side);
13329       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13330                                           CE_PLAYER_TOUCHES_X,
13331                                           player->index_bit, border_side);
13332
13333       {
13334         /* use player element that is initially defined in the level playfield,
13335            not the player element that corresponds to the runtime player number
13336            (example: a level that contains EL_PLAYER_3 as the only player would
13337            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13338         int player_element = PLAYERINFO(x, y)->initial_element;
13339
13340         CheckElementChangeBySide(xx, yy, border_element, player_element,
13341                                  CE_TOUCHING_X, border_side);
13342       }
13343     }
13344     else if (IS_PLAYER(xx, yy))         // player found at border element
13345     {
13346       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13347
13348       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13349       {
13350         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13351           continue;             // center and border element do not touch
13352       }
13353
13354       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13355                                  player->index_bit, center_side);
13356       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13357                                           CE_PLAYER_TOUCHES_X,
13358                                           player->index_bit, center_side);
13359
13360       {
13361         /* use player element that is initially defined in the level playfield,
13362            not the player element that corresponds to the runtime player number
13363            (example: a level that contains EL_PLAYER_3 as the only player would
13364            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13365         int player_element = PLAYERINFO(xx, yy)->initial_element;
13366
13367         CheckElementChangeBySide(x, y, center_element, player_element,
13368                                  CE_TOUCHING_X, center_side);
13369       }
13370
13371       break;
13372     }
13373   }
13374 }
13375
13376 void TestIfElementTouchesCustomElement(int x, int y)
13377 {
13378   static int xy[4][2] =
13379   {
13380     { 0, -1 },
13381     { -1, 0 },
13382     { +1, 0 },
13383     { 0, +1 }
13384   };
13385   static int trigger_sides[4][2] =
13386   {
13387     // center side      border side
13388     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13389     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13390     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13391     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13392   };
13393   static int touch_dir[4] =
13394   {
13395     MV_LEFT | MV_RIGHT,
13396     MV_UP   | MV_DOWN,
13397     MV_UP   | MV_DOWN,
13398     MV_LEFT | MV_RIGHT
13399   };
13400   boolean change_center_element = FALSE;
13401   int center_element = Tile[x][y];      // should always be non-moving!
13402   int border_element_old[NUM_DIRECTIONS];
13403   int i;
13404
13405   for (i = 0; i < NUM_DIRECTIONS; i++)
13406   {
13407     int xx = x + xy[i][0];
13408     int yy = y + xy[i][1];
13409     int border_element;
13410
13411     border_element_old[i] = -1;
13412
13413     if (!IN_LEV_FIELD(xx, yy))
13414       continue;
13415
13416     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13417       border_element = Tile[xx][yy];    // may be moving!
13418     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13419       border_element = Tile[xx][yy];
13420     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13421       border_element = MovingOrBlocked2Element(xx, yy);
13422     else
13423       continue;                 // center and border element do not touch
13424
13425     border_element_old[i] = border_element;
13426   }
13427
13428   for (i = 0; i < NUM_DIRECTIONS; i++)
13429   {
13430     int xx = x + xy[i][0];
13431     int yy = y + xy[i][1];
13432     int center_side = trigger_sides[i][0];
13433     int border_element = border_element_old[i];
13434
13435     if (border_element == -1)
13436       continue;
13437
13438     // check for change of border element
13439     CheckElementChangeBySide(xx, yy, border_element, center_element,
13440                              CE_TOUCHING_X, center_side);
13441
13442     // (center element cannot be player, so we dont have to check this here)
13443   }
13444
13445   for (i = 0; i < NUM_DIRECTIONS; i++)
13446   {
13447     int xx = x + xy[i][0];
13448     int yy = y + xy[i][1];
13449     int border_side = trigger_sides[i][1];
13450     int border_element = border_element_old[i];
13451
13452     if (border_element == -1)
13453       continue;
13454
13455     // check for change of center element (but change it only once)
13456     if (!change_center_element)
13457       change_center_element =
13458         CheckElementChangeBySide(x, y, center_element, border_element,
13459                                  CE_TOUCHING_X, border_side);
13460
13461     if (IS_PLAYER(xx, yy))
13462     {
13463       /* use player element that is initially defined in the level playfield,
13464          not the player element that corresponds to the runtime player number
13465          (example: a level that contains EL_PLAYER_3 as the only player would
13466          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13467       int player_element = PLAYERINFO(xx, yy)->initial_element;
13468
13469       CheckElementChangeBySide(x, y, center_element, player_element,
13470                                CE_TOUCHING_X, border_side);
13471     }
13472   }
13473 }
13474
13475 void TestIfElementHitsCustomElement(int x, int y, int direction)
13476 {
13477   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13478   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13479   int hitx = x + dx, hity = y + dy;
13480   int hitting_element = Tile[x][y];
13481   int touched_element;
13482
13483   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13484     return;
13485
13486   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13487                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13488
13489   if (IN_LEV_FIELD(hitx, hity))
13490   {
13491     int opposite_direction = MV_DIR_OPPOSITE(direction);
13492     int hitting_side = direction;
13493     int touched_side = opposite_direction;
13494     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13495                           MovDir[hitx][hity] != direction ||
13496                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13497
13498     object_hit = TRUE;
13499
13500     if (object_hit)
13501     {
13502       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13503                                CE_HITTING_X, touched_side);
13504
13505       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13506                                CE_HIT_BY_X, hitting_side);
13507
13508       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13509                                CE_HIT_BY_SOMETHING, opposite_direction);
13510
13511       if (IS_PLAYER(hitx, hity))
13512       {
13513         /* use player element that is initially defined in the level playfield,
13514            not the player element that corresponds to the runtime player number
13515            (example: a level that contains EL_PLAYER_3 as the only player would
13516            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13517         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13518
13519         CheckElementChangeBySide(x, y, hitting_element, player_element,
13520                                  CE_HITTING_X, touched_side);
13521       }
13522     }
13523   }
13524
13525   // "hitting something" is also true when hitting the playfield border
13526   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13527                            CE_HITTING_SOMETHING, direction);
13528 }
13529
13530 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13531 {
13532   int i, kill_x = -1, kill_y = -1;
13533
13534   int bad_element = -1;
13535   static int test_xy[4][2] =
13536   {
13537     { 0, -1 },
13538     { -1, 0 },
13539     { +1, 0 },
13540     { 0, +1 }
13541   };
13542   static int test_dir[4] =
13543   {
13544     MV_UP,
13545     MV_LEFT,
13546     MV_RIGHT,
13547     MV_DOWN
13548   };
13549
13550   for (i = 0; i < NUM_DIRECTIONS; i++)
13551   {
13552     int test_x, test_y, test_move_dir, test_element;
13553
13554     test_x = good_x + test_xy[i][0];
13555     test_y = good_y + test_xy[i][1];
13556
13557     if (!IN_LEV_FIELD(test_x, test_y))
13558       continue;
13559
13560     test_move_dir =
13561       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13562
13563     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13564
13565     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13566        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13567     */
13568     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13569         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13570     {
13571       kill_x = test_x;
13572       kill_y = test_y;
13573       bad_element = test_element;
13574
13575       break;
13576     }
13577   }
13578
13579   if (kill_x != -1 || kill_y != -1)
13580   {
13581     if (IS_PLAYER(good_x, good_y))
13582     {
13583       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13584
13585       if (player->shield_deadly_time_left > 0 &&
13586           !IS_INDESTRUCTIBLE(bad_element))
13587         Bang(kill_x, kill_y);
13588       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13589         KillPlayer(player);
13590     }
13591     else
13592       Bang(good_x, good_y);
13593   }
13594 }
13595
13596 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13597 {
13598   int i, kill_x = -1, kill_y = -1;
13599   int bad_element = Tile[bad_x][bad_y];
13600   static int test_xy[4][2] =
13601   {
13602     { 0, -1 },
13603     { -1, 0 },
13604     { +1, 0 },
13605     { 0, +1 }
13606   };
13607   static int touch_dir[4] =
13608   {
13609     MV_LEFT | MV_RIGHT,
13610     MV_UP   | MV_DOWN,
13611     MV_UP   | MV_DOWN,
13612     MV_LEFT | MV_RIGHT
13613   };
13614   static int test_dir[4] =
13615   {
13616     MV_UP,
13617     MV_LEFT,
13618     MV_RIGHT,
13619     MV_DOWN
13620   };
13621
13622   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13623     return;
13624
13625   for (i = 0; i < NUM_DIRECTIONS; i++)
13626   {
13627     int test_x, test_y, test_move_dir, test_element;
13628
13629     test_x = bad_x + test_xy[i][0];
13630     test_y = bad_y + test_xy[i][1];
13631
13632     if (!IN_LEV_FIELD(test_x, test_y))
13633       continue;
13634
13635     test_move_dir =
13636       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13637
13638     test_element = Tile[test_x][test_y];
13639
13640     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13641        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13642     */
13643     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13644         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13645     {
13646       // good thing is player or penguin that does not move away
13647       if (IS_PLAYER(test_x, test_y))
13648       {
13649         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13650
13651         if (bad_element == EL_ROBOT && player->is_moving)
13652           continue;     // robot does not kill player if he is moving
13653
13654         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13655         {
13656           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13657             continue;           // center and border element do not touch
13658         }
13659
13660         kill_x = test_x;
13661         kill_y = test_y;
13662
13663         break;
13664       }
13665       else if (test_element == EL_PENGUIN)
13666       {
13667         kill_x = test_x;
13668         kill_y = test_y;
13669
13670         break;
13671       }
13672     }
13673   }
13674
13675   if (kill_x != -1 || kill_y != -1)
13676   {
13677     if (IS_PLAYER(kill_x, kill_y))
13678     {
13679       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13680
13681       if (player->shield_deadly_time_left > 0 &&
13682           !IS_INDESTRUCTIBLE(bad_element))
13683         Bang(bad_x, bad_y);
13684       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13685         KillPlayer(player);
13686     }
13687     else
13688       Bang(kill_x, kill_y);
13689   }
13690 }
13691
13692 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13693 {
13694   int bad_element = Tile[bad_x][bad_y];
13695   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13696   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13697   int test_x = bad_x + dx, test_y = bad_y + dy;
13698   int test_move_dir, test_element;
13699   int kill_x = -1, kill_y = -1;
13700
13701   if (!IN_LEV_FIELD(test_x, test_y))
13702     return;
13703
13704   test_move_dir =
13705     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13706
13707   test_element = Tile[test_x][test_y];
13708
13709   if (test_move_dir != bad_move_dir)
13710   {
13711     // good thing can be player or penguin that does not move away
13712     if (IS_PLAYER(test_x, test_y))
13713     {
13714       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13715
13716       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13717          player as being hit when he is moving towards the bad thing, because
13718          the "get hit by" condition would be lost after the player stops) */
13719       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13720         return;         // player moves away from bad thing
13721
13722       kill_x = test_x;
13723       kill_y = test_y;
13724     }
13725     else if (test_element == EL_PENGUIN)
13726     {
13727       kill_x = test_x;
13728       kill_y = test_y;
13729     }
13730   }
13731
13732   if (kill_x != -1 || kill_y != -1)
13733   {
13734     if (IS_PLAYER(kill_x, kill_y))
13735     {
13736       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13737
13738       if (player->shield_deadly_time_left > 0 &&
13739           !IS_INDESTRUCTIBLE(bad_element))
13740         Bang(bad_x, bad_y);
13741       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13742         KillPlayer(player);
13743     }
13744     else
13745       Bang(kill_x, kill_y);
13746   }
13747 }
13748
13749 void TestIfPlayerTouchesBadThing(int x, int y)
13750 {
13751   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13752 }
13753
13754 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13755 {
13756   TestIfGoodThingHitsBadThing(x, y, move_dir);
13757 }
13758
13759 void TestIfBadThingTouchesPlayer(int x, int y)
13760 {
13761   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13762 }
13763
13764 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13765 {
13766   TestIfBadThingHitsGoodThing(x, y, move_dir);
13767 }
13768
13769 void TestIfFriendTouchesBadThing(int x, int y)
13770 {
13771   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13772 }
13773
13774 void TestIfBadThingTouchesFriend(int x, int y)
13775 {
13776   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13777 }
13778
13779 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13780 {
13781   int i, kill_x = bad_x, kill_y = bad_y;
13782   static int xy[4][2] =
13783   {
13784     { 0, -1 },
13785     { -1, 0 },
13786     { +1, 0 },
13787     { 0, +1 }
13788   };
13789
13790   for (i = 0; i < NUM_DIRECTIONS; i++)
13791   {
13792     int x, y, element;
13793
13794     x = bad_x + xy[i][0];
13795     y = bad_y + xy[i][1];
13796     if (!IN_LEV_FIELD(x, y))
13797       continue;
13798
13799     element = Tile[x][y];
13800     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13801         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13802     {
13803       kill_x = x;
13804       kill_y = y;
13805       break;
13806     }
13807   }
13808
13809   if (kill_x != bad_x || kill_y != bad_y)
13810     Bang(bad_x, bad_y);
13811 }
13812
13813 void KillPlayer(struct PlayerInfo *player)
13814 {
13815   int jx = player->jx, jy = player->jy;
13816
13817   if (!player->active)
13818     return;
13819
13820 #if 0
13821   Debug("game:playing:KillPlayer",
13822         "0: killed == %d, active == %d, reanimated == %d",
13823         player->killed, player->active, player->reanimated);
13824 #endif
13825
13826   /* the following code was introduced to prevent an infinite loop when calling
13827      -> Bang()
13828      -> CheckTriggeredElementChangeExt()
13829      -> ExecuteCustomElementAction()
13830      -> KillPlayer()
13831      -> (infinitely repeating the above sequence of function calls)
13832      which occurs when killing the player while having a CE with the setting
13833      "kill player X when explosion of <player X>"; the solution using a new
13834      field "player->killed" was chosen for backwards compatibility, although
13835      clever use of the fields "player->active" etc. would probably also work */
13836 #if 1
13837   if (player->killed)
13838     return;
13839 #endif
13840
13841   player->killed = TRUE;
13842
13843   // remove accessible field at the player's position
13844   Tile[jx][jy] = EL_EMPTY;
13845
13846   // deactivate shield (else Bang()/Explode() would not work right)
13847   player->shield_normal_time_left = 0;
13848   player->shield_deadly_time_left = 0;
13849
13850 #if 0
13851   Debug("game:playing:KillPlayer",
13852         "1: killed == %d, active == %d, reanimated == %d",
13853         player->killed, player->active, player->reanimated);
13854 #endif
13855
13856   Bang(jx, jy);
13857
13858 #if 0
13859   Debug("game:playing:KillPlayer",
13860         "2: killed == %d, active == %d, reanimated == %d",
13861         player->killed, player->active, player->reanimated);
13862 #endif
13863
13864   if (player->reanimated)       // killed player may have been reanimated
13865     player->killed = player->reanimated = FALSE;
13866   else
13867     BuryPlayer(player);
13868 }
13869
13870 static void KillPlayerUnlessEnemyProtected(int x, int y)
13871 {
13872   if (!PLAYER_ENEMY_PROTECTED(x, y))
13873     KillPlayer(PLAYERINFO(x, y));
13874 }
13875
13876 static void KillPlayerUnlessExplosionProtected(int x, int y)
13877 {
13878   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13879     KillPlayer(PLAYERINFO(x, y));
13880 }
13881
13882 void BuryPlayer(struct PlayerInfo *player)
13883 {
13884   int jx = player->jx, jy = player->jy;
13885
13886   if (!player->active)
13887     return;
13888
13889   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13890   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13891
13892   RemovePlayer(player);
13893
13894   player->buried = TRUE;
13895
13896   if (game.all_players_gone)
13897     game.GameOver = TRUE;
13898 }
13899
13900 void RemovePlayer(struct PlayerInfo *player)
13901 {
13902   int jx = player->jx, jy = player->jy;
13903   int i, found = FALSE;
13904
13905   player->present = FALSE;
13906   player->active = FALSE;
13907
13908   // required for some CE actions (even if the player is not active anymore)
13909   player->MovPos = 0;
13910
13911   if (!ExplodeField[jx][jy])
13912     StorePlayer[jx][jy] = 0;
13913
13914   if (player->is_moving)
13915     TEST_DrawLevelField(player->last_jx, player->last_jy);
13916
13917   for (i = 0; i < MAX_PLAYERS; i++)
13918     if (stored_player[i].active)
13919       found = TRUE;
13920
13921   if (!found)
13922   {
13923     game.all_players_gone = TRUE;
13924     game.GameOver = TRUE;
13925   }
13926
13927   game.exit_x = game.robot_wheel_x = jx;
13928   game.exit_y = game.robot_wheel_y = jy;
13929 }
13930
13931 void ExitPlayer(struct PlayerInfo *player)
13932 {
13933   DrawPlayer(player);   // needed here only to cleanup last field
13934   RemovePlayer(player);
13935
13936   if (game.players_still_needed > 0)
13937     game.players_still_needed--;
13938 }
13939
13940 static void SetFieldForSnapping(int x, int y, int element, int direction,
13941                                 int player_index_bit)
13942 {
13943   struct ElementInfo *ei = &element_info[element];
13944   int direction_bit = MV_DIR_TO_BIT(direction);
13945   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13946   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13947                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13948
13949   Tile[x][y] = EL_ELEMENT_SNAPPING;
13950   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13951   MovDir[x][y] = direction;
13952   Store[x][y] = element;
13953   Store2[x][y] = player_index_bit;
13954
13955   ResetGfxAnimation(x, y);
13956
13957   GfxElement[x][y] = element;
13958   GfxAction[x][y] = action;
13959   GfxDir[x][y] = direction;
13960   GfxFrame[x][y] = -1;
13961 }
13962
13963 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13964                                    int player_index_bit)
13965 {
13966   TestIfElementTouchesCustomElement(x, y);      // for empty space
13967
13968   if (level.finish_dig_collect)
13969   {
13970     int dig_side = MV_DIR_OPPOSITE(direction);
13971
13972     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13973                                         player_index_bit, dig_side);
13974   }
13975 }
13976
13977 /*
13978   =============================================================================
13979   checkDiagonalPushing()
13980   -----------------------------------------------------------------------------
13981   check if diagonal input device direction results in pushing of object
13982   (by checking if the alternative direction is walkable, diggable, ...)
13983   =============================================================================
13984 */
13985
13986 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13987                                     int x, int y, int real_dx, int real_dy)
13988 {
13989   int jx, jy, dx, dy, xx, yy;
13990
13991   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13992     return TRUE;
13993
13994   // diagonal direction: check alternative direction
13995   jx = player->jx;
13996   jy = player->jy;
13997   dx = x - jx;
13998   dy = y - jy;
13999   xx = jx + (dx == 0 ? real_dx : 0);
14000   yy = jy + (dy == 0 ? real_dy : 0);
14001
14002   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14003 }
14004
14005 /*
14006   =============================================================================
14007   DigField()
14008   -----------------------------------------------------------------------------
14009   x, y:                 field next to player (non-diagonal) to try to dig to
14010   real_dx, real_dy:     direction as read from input device (can be diagonal)
14011   =============================================================================
14012 */
14013
14014 static int DigField(struct PlayerInfo *player,
14015                     int oldx, int oldy, int x, int y,
14016                     int real_dx, int real_dy, int mode)
14017 {
14018   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14019   boolean player_was_pushing = player->is_pushing;
14020   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14021   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14022   int jx = oldx, jy = oldy;
14023   int dx = x - jx, dy = y - jy;
14024   int nextx = x + dx, nexty = y + dy;
14025   int move_direction = (dx == -1 ? MV_LEFT  :
14026                         dx == +1 ? MV_RIGHT :
14027                         dy == -1 ? MV_UP    :
14028                         dy == +1 ? MV_DOWN  : MV_NONE);
14029   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14030   int dig_side = MV_DIR_OPPOSITE(move_direction);
14031   int old_element = Tile[jx][jy];
14032   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14033   int collect_count;
14034
14035   if (is_player)                // function can also be called by EL_PENGUIN
14036   {
14037     if (player->MovPos == 0)
14038     {
14039       player->is_digging = FALSE;
14040       player->is_collecting = FALSE;
14041     }
14042
14043     if (player->MovPos == 0)    // last pushing move finished
14044       player->is_pushing = FALSE;
14045
14046     if (mode == DF_NO_PUSH)     // player just stopped pushing
14047     {
14048       player->is_switching = FALSE;
14049       player->push_delay = -1;
14050
14051       return MP_NO_ACTION;
14052     }
14053   }
14054
14055   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14056     old_element = Back[jx][jy];
14057
14058   // in case of element dropped at player position, check background
14059   else if (Back[jx][jy] != EL_EMPTY &&
14060            game.engine_version >= VERSION_IDENT(2,2,0,0))
14061     old_element = Back[jx][jy];
14062
14063   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14064     return MP_NO_ACTION;        // field has no opening in this direction
14065
14066   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14067     return MP_NO_ACTION;        // field has no opening in this direction
14068
14069   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14070   {
14071     SplashAcid(x, y);
14072
14073     Tile[jx][jy] = player->artwork_element;
14074     InitMovingField(jx, jy, MV_DOWN);
14075     Store[jx][jy] = EL_ACID;
14076     ContinueMoving(jx, jy);
14077     BuryPlayer(player);
14078
14079     return MP_DONT_RUN_INTO;
14080   }
14081
14082   if (player_can_move && DONT_RUN_INTO(element))
14083   {
14084     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14085
14086     return MP_DONT_RUN_INTO;
14087   }
14088
14089   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14090     return MP_NO_ACTION;
14091
14092   collect_count = element_info[element].collect_count_initial;
14093
14094   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14095     return MP_NO_ACTION;
14096
14097   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14098     player_can_move = player_can_move_or_snap;
14099
14100   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14101       game.engine_version >= VERSION_IDENT(2,2,0,0))
14102   {
14103     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14104                                player->index_bit, dig_side);
14105     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14106                                         player->index_bit, dig_side);
14107
14108     if (element == EL_DC_LANDMINE)
14109       Bang(x, y);
14110
14111     if (Tile[x][y] != element)          // field changed by snapping
14112       return MP_ACTION;
14113
14114     return MP_NO_ACTION;
14115   }
14116
14117   if (player->gravity && is_player && !player->is_auto_moving &&
14118       canFallDown(player) && move_direction != MV_DOWN &&
14119       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14120     return MP_NO_ACTION;        // player cannot walk here due to gravity
14121
14122   if (player_can_move &&
14123       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14124   {
14125     int sound_element = SND_ELEMENT(element);
14126     int sound_action = ACTION_WALKING;
14127
14128     if (IS_RND_GATE(element))
14129     {
14130       if (!player->key[RND_GATE_NR(element)])
14131         return MP_NO_ACTION;
14132     }
14133     else if (IS_RND_GATE_GRAY(element))
14134     {
14135       if (!player->key[RND_GATE_GRAY_NR(element)])
14136         return MP_NO_ACTION;
14137     }
14138     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14139     {
14140       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14141         return MP_NO_ACTION;
14142     }
14143     else if (element == EL_EXIT_OPEN ||
14144              element == EL_EM_EXIT_OPEN ||
14145              element == EL_EM_EXIT_OPENING ||
14146              element == EL_STEEL_EXIT_OPEN ||
14147              element == EL_EM_STEEL_EXIT_OPEN ||
14148              element == EL_EM_STEEL_EXIT_OPENING ||
14149              element == EL_SP_EXIT_OPEN ||
14150              element == EL_SP_EXIT_OPENING)
14151     {
14152       sound_action = ACTION_PASSING;    // player is passing exit
14153     }
14154     else if (element == EL_EMPTY)
14155     {
14156       sound_action = ACTION_MOVING;             // nothing to walk on
14157     }
14158
14159     // play sound from background or player, whatever is available
14160     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14161       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14162     else
14163       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14164   }
14165   else if (player_can_move &&
14166            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14167   {
14168     if (!ACCESS_FROM(element, opposite_direction))
14169       return MP_NO_ACTION;      // field not accessible from this direction
14170
14171     if (CAN_MOVE(element))      // only fixed elements can be passed!
14172       return MP_NO_ACTION;
14173
14174     if (IS_EM_GATE(element))
14175     {
14176       if (!player->key[EM_GATE_NR(element)])
14177         return MP_NO_ACTION;
14178     }
14179     else if (IS_EM_GATE_GRAY(element))
14180     {
14181       if (!player->key[EM_GATE_GRAY_NR(element)])
14182         return MP_NO_ACTION;
14183     }
14184     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14185     {
14186       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14187         return MP_NO_ACTION;
14188     }
14189     else if (IS_EMC_GATE(element))
14190     {
14191       if (!player->key[EMC_GATE_NR(element)])
14192         return MP_NO_ACTION;
14193     }
14194     else if (IS_EMC_GATE_GRAY(element))
14195     {
14196       if (!player->key[EMC_GATE_GRAY_NR(element)])
14197         return MP_NO_ACTION;
14198     }
14199     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14200     {
14201       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14202         return MP_NO_ACTION;
14203     }
14204     else if (element == EL_DC_GATE_WHITE ||
14205              element == EL_DC_GATE_WHITE_GRAY ||
14206              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14207     {
14208       if (player->num_white_keys == 0)
14209         return MP_NO_ACTION;
14210
14211       player->num_white_keys--;
14212     }
14213     else if (IS_SP_PORT(element))
14214     {
14215       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14216           element == EL_SP_GRAVITY_PORT_RIGHT ||
14217           element == EL_SP_GRAVITY_PORT_UP ||
14218           element == EL_SP_GRAVITY_PORT_DOWN)
14219         player->gravity = !player->gravity;
14220       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14221                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14222                element == EL_SP_GRAVITY_ON_PORT_UP ||
14223                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14224         player->gravity = TRUE;
14225       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14226                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14227                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14228                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14229         player->gravity = FALSE;
14230     }
14231
14232     // automatically move to the next field with double speed
14233     player->programmed_action = move_direction;
14234
14235     if (player->move_delay_reset_counter == 0)
14236     {
14237       player->move_delay_reset_counter = 2;     // two double speed steps
14238
14239       DOUBLE_PLAYER_SPEED(player);
14240     }
14241
14242     PlayLevelSoundAction(x, y, ACTION_PASSING);
14243   }
14244   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14245   {
14246     RemoveField(x, y);
14247
14248     if (mode != DF_SNAP)
14249     {
14250       GfxElement[x][y] = GFX_ELEMENT(element);
14251       player->is_digging = TRUE;
14252     }
14253
14254     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14255
14256     // use old behaviour for old levels (digging)
14257     if (!level.finish_dig_collect)
14258     {
14259       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14260                                           player->index_bit, dig_side);
14261
14262       // if digging triggered player relocation, finish digging 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_COLLECTIBLE(element))
14281   {
14282     RemoveField(x, y);
14283
14284     if (is_player && mode != DF_SNAP)
14285     {
14286       GfxElement[x][y] = element;
14287       player->is_collecting = TRUE;
14288     }
14289
14290     if (element == EL_SPEED_PILL)
14291     {
14292       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14293     }
14294     else if (element == EL_EXTRA_TIME && level.time > 0)
14295     {
14296       TimeLeft += level.extra_time;
14297
14298       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14299
14300       DisplayGameControlValues();
14301     }
14302     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14303     {
14304       player->shield_normal_time_left += level.shield_normal_time;
14305       if (element == EL_SHIELD_DEADLY)
14306         player->shield_deadly_time_left += level.shield_deadly_time;
14307     }
14308     else if (element == EL_DYNAMITE ||
14309              element == EL_EM_DYNAMITE ||
14310              element == EL_SP_DISK_RED)
14311     {
14312       if (player->inventory_size < MAX_INVENTORY_SIZE)
14313         player->inventory_element[player->inventory_size++] = element;
14314
14315       DrawGameDoorValues();
14316     }
14317     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14318     {
14319       player->dynabomb_count++;
14320       player->dynabombs_left++;
14321     }
14322     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14323     {
14324       player->dynabomb_size++;
14325     }
14326     else if (element == EL_DYNABOMB_INCREASE_POWER)
14327     {
14328       player->dynabomb_xl = TRUE;
14329     }
14330     else if (IS_KEY(element))
14331     {
14332       player->key[KEY_NR(element)] = TRUE;
14333
14334       DrawGameDoorValues();
14335     }
14336     else if (element == EL_DC_KEY_WHITE)
14337     {
14338       player->num_white_keys++;
14339
14340       // display white keys?
14341       // DrawGameDoorValues();
14342     }
14343     else if (IS_ENVELOPE(element))
14344     {
14345       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14346
14347       if (!wait_for_snapping)
14348         player->show_envelope = element;
14349     }
14350     else if (element == EL_EMC_LENSES)
14351     {
14352       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14353
14354       RedrawAllInvisibleElementsForLenses();
14355     }
14356     else if (element == EL_EMC_MAGNIFIER)
14357     {
14358       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14359
14360       RedrawAllInvisibleElementsForMagnifier();
14361     }
14362     else if (IS_DROPPABLE(element) ||
14363              IS_THROWABLE(element))     // can be collected and dropped
14364     {
14365       int i;
14366
14367       if (collect_count == 0)
14368         player->inventory_infinite_element = element;
14369       else
14370         for (i = 0; i < collect_count; i++)
14371           if (player->inventory_size < MAX_INVENTORY_SIZE)
14372             player->inventory_element[player->inventory_size++] = element;
14373
14374       DrawGameDoorValues();
14375     }
14376     else if (collect_count > 0)
14377     {
14378       game.gems_still_needed -= collect_count;
14379       if (game.gems_still_needed < 0)
14380         game.gems_still_needed = 0;
14381
14382       game.snapshot.collected_item = TRUE;
14383
14384       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14385
14386       DisplayGameControlValues();
14387     }
14388
14389     RaiseScoreElement(element);
14390     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14391
14392     // use old behaviour for old levels (collecting)
14393     if (!level.finish_dig_collect && is_player)
14394     {
14395       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14396                                           player->index_bit, dig_side);
14397
14398       // if collecting triggered player relocation, finish collecting tile
14399       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14400         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14401     }
14402
14403     if (mode == DF_SNAP)
14404     {
14405       if (level.block_snap_field)
14406         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14407       else
14408         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14409
14410       // use old behaviour for old levels (snapping)
14411       if (!level.finish_dig_collect)
14412         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14413                                             player->index_bit, dig_side);
14414     }
14415   }
14416   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14417   {
14418     if (mode == DF_SNAP && element != EL_BD_ROCK)
14419       return MP_NO_ACTION;
14420
14421     if (CAN_FALL(element) && dy)
14422       return MP_NO_ACTION;
14423
14424     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14425         !(element == EL_SPRING && level.use_spring_bug))
14426       return MP_NO_ACTION;
14427
14428     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14429         ((move_direction & MV_VERTICAL &&
14430           ((element_info[element].move_pattern & MV_LEFT &&
14431             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14432            (element_info[element].move_pattern & MV_RIGHT &&
14433             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14434          (move_direction & MV_HORIZONTAL &&
14435           ((element_info[element].move_pattern & MV_UP &&
14436             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14437            (element_info[element].move_pattern & MV_DOWN &&
14438             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14439       return MP_NO_ACTION;
14440
14441     // do not push elements already moving away faster than player
14442     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14443         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14444       return MP_NO_ACTION;
14445
14446     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14447     {
14448       if (player->push_delay_value == -1 || !player_was_pushing)
14449         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14450     }
14451     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14452     {
14453       if (player->push_delay_value == -1)
14454         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14455     }
14456     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14457     {
14458       if (!player->is_pushing)
14459         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14460     }
14461
14462     player->is_pushing = TRUE;
14463     player->is_active = TRUE;
14464
14465     if (!(IN_LEV_FIELD(nextx, nexty) &&
14466           (IS_FREE(nextx, nexty) ||
14467            (IS_SB_ELEMENT(element) &&
14468             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14469            (IS_CUSTOM_ELEMENT(element) &&
14470             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14471       return MP_NO_ACTION;
14472
14473     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14474       return MP_NO_ACTION;
14475
14476     if (player->push_delay == -1)       // new pushing; restart delay
14477       player->push_delay = 0;
14478
14479     if (player->push_delay < player->push_delay_value &&
14480         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14481         element != EL_SPRING && element != EL_BALLOON)
14482     {
14483       // make sure that there is no move delay before next try to push
14484       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14485         player->move_delay = 0;
14486
14487       return MP_NO_ACTION;
14488     }
14489
14490     if (IS_CUSTOM_ELEMENT(element) &&
14491         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14492     {
14493       if (!DigFieldByCE(nextx, nexty, element))
14494         return MP_NO_ACTION;
14495     }
14496
14497     if (IS_SB_ELEMENT(element))
14498     {
14499       boolean sokoban_task_solved = FALSE;
14500
14501       if (element == EL_SOKOBAN_FIELD_FULL)
14502       {
14503         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14504
14505         IncrementSokobanFieldsNeeded();
14506         IncrementSokobanObjectsNeeded();
14507       }
14508
14509       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14510       {
14511         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14512
14513         DecrementSokobanFieldsNeeded();
14514         DecrementSokobanObjectsNeeded();
14515
14516         // sokoban object was pushed from empty field to sokoban field
14517         if (Back[x][y] == EL_EMPTY)
14518           sokoban_task_solved = TRUE;
14519       }
14520
14521       Tile[x][y] = EL_SOKOBAN_OBJECT;
14522
14523       if (Back[x][y] == Back[nextx][nexty])
14524         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14525       else if (Back[x][y] != 0)
14526         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14527                                     ACTION_EMPTYING);
14528       else
14529         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14530                                     ACTION_FILLING);
14531
14532       if (sokoban_task_solved &&
14533           game.sokoban_fields_still_needed == 0 &&
14534           game.sokoban_objects_still_needed == 0 &&
14535           level.auto_exit_sokoban)
14536       {
14537         game.players_still_needed = 0;
14538
14539         LevelSolved();
14540
14541         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14542       }
14543     }
14544     else
14545       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14546
14547     InitMovingField(x, y, move_direction);
14548     GfxAction[x][y] = ACTION_PUSHING;
14549
14550     if (mode == DF_SNAP)
14551       ContinueMoving(x, y);
14552     else
14553       MovPos[x][y] = (dx != 0 ? dx : dy);
14554
14555     Pushed[x][y] = TRUE;
14556     Pushed[nextx][nexty] = TRUE;
14557
14558     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14559       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14560     else
14561       player->push_delay_value = -1;    // get new value later
14562
14563     // check for element change _after_ element has been pushed
14564     if (game.use_change_when_pushing_bug)
14565     {
14566       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14567                                  player->index_bit, dig_side);
14568       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14569                                           player->index_bit, dig_side);
14570     }
14571   }
14572   else if (IS_SWITCHABLE(element))
14573   {
14574     if (PLAYER_SWITCHING(player, x, y))
14575     {
14576       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14577                                           player->index_bit, dig_side);
14578
14579       return MP_ACTION;
14580     }
14581
14582     player->is_switching = TRUE;
14583     player->switch_x = x;
14584     player->switch_y = y;
14585
14586     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14587
14588     if (element == EL_ROBOT_WHEEL)
14589     {
14590       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14591
14592       game.robot_wheel_x = x;
14593       game.robot_wheel_y = y;
14594       game.robot_wheel_active = TRUE;
14595
14596       TEST_DrawLevelField(x, y);
14597     }
14598     else if (element == EL_SP_TERMINAL)
14599     {
14600       int xx, yy;
14601
14602       SCAN_PLAYFIELD(xx, yy)
14603       {
14604         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14605         {
14606           Bang(xx, yy);
14607         }
14608         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14609         {
14610           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14611
14612           ResetGfxAnimation(xx, yy);
14613           TEST_DrawLevelField(xx, yy);
14614         }
14615       }
14616     }
14617     else if (IS_BELT_SWITCH(element))
14618     {
14619       ToggleBeltSwitch(x, y);
14620     }
14621     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14622              element == EL_SWITCHGATE_SWITCH_DOWN ||
14623              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14624              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14625     {
14626       ToggleSwitchgateSwitch(x, y);
14627     }
14628     else if (element == EL_LIGHT_SWITCH ||
14629              element == EL_LIGHT_SWITCH_ACTIVE)
14630     {
14631       ToggleLightSwitch(x, y);
14632     }
14633     else if (element == EL_TIMEGATE_SWITCH ||
14634              element == EL_DC_TIMEGATE_SWITCH)
14635     {
14636       ActivateTimegateSwitch(x, y);
14637     }
14638     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14639              element == EL_BALLOON_SWITCH_RIGHT ||
14640              element == EL_BALLOON_SWITCH_UP    ||
14641              element == EL_BALLOON_SWITCH_DOWN  ||
14642              element == EL_BALLOON_SWITCH_NONE  ||
14643              element == EL_BALLOON_SWITCH_ANY)
14644     {
14645       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14646                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14647                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14648                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14649                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14650                              move_direction);
14651     }
14652     else if (element == EL_LAMP)
14653     {
14654       Tile[x][y] = EL_LAMP_ACTIVE;
14655       game.lights_still_needed--;
14656
14657       ResetGfxAnimation(x, y);
14658       TEST_DrawLevelField(x, y);
14659     }
14660     else if (element == EL_TIME_ORB_FULL)
14661     {
14662       Tile[x][y] = EL_TIME_ORB_EMPTY;
14663
14664       if (level.time > 0 || level.use_time_orb_bug)
14665       {
14666         TimeLeft += level.time_orb_time;
14667         game.no_time_limit = FALSE;
14668
14669         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14670
14671         DisplayGameControlValues();
14672       }
14673
14674       ResetGfxAnimation(x, y);
14675       TEST_DrawLevelField(x, y);
14676     }
14677     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14678              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14679     {
14680       int xx, yy;
14681
14682       game.ball_active = !game.ball_active;
14683
14684       SCAN_PLAYFIELD(xx, yy)
14685       {
14686         int e = Tile[xx][yy];
14687
14688         if (game.ball_active)
14689         {
14690           if (e == EL_EMC_MAGIC_BALL)
14691             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14692           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14693             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14694         }
14695         else
14696         {
14697           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14698             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14699           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14700             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14701         }
14702       }
14703     }
14704
14705     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14706                                         player->index_bit, dig_side);
14707
14708     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14709                                         player->index_bit, dig_side);
14710
14711     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14712                                         player->index_bit, dig_side);
14713
14714     return MP_ACTION;
14715   }
14716   else
14717   {
14718     if (!PLAYER_SWITCHING(player, x, y))
14719     {
14720       player->is_switching = TRUE;
14721       player->switch_x = x;
14722       player->switch_y = y;
14723
14724       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14725                                  player->index_bit, dig_side);
14726       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14727                                           player->index_bit, dig_side);
14728
14729       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14730                                  player->index_bit, dig_side);
14731       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14732                                           player->index_bit, dig_side);
14733     }
14734
14735     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14736                                player->index_bit, dig_side);
14737     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14738                                         player->index_bit, dig_side);
14739
14740     return MP_NO_ACTION;
14741   }
14742
14743   player->push_delay = -1;
14744
14745   if (is_player)                // function can also be called by EL_PENGUIN
14746   {
14747     if (Tile[x][y] != element)          // really digged/collected something
14748     {
14749       player->is_collecting = !player->is_digging;
14750       player->is_active = TRUE;
14751
14752       player->last_removed_element = element;
14753     }
14754   }
14755
14756   return MP_MOVING;
14757 }
14758
14759 static boolean DigFieldByCE(int x, int y, int digging_element)
14760 {
14761   int element = Tile[x][y];
14762
14763   if (!IS_FREE(x, y))
14764   {
14765     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14766                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14767                   ACTION_BREAKING);
14768
14769     // no element can dig solid indestructible elements
14770     if (IS_INDESTRUCTIBLE(element) &&
14771         !IS_DIGGABLE(element) &&
14772         !IS_COLLECTIBLE(element))
14773       return FALSE;
14774
14775     if (AmoebaNr[x][y] &&
14776         (element == EL_AMOEBA_FULL ||
14777          element == EL_BD_AMOEBA ||
14778          element == EL_AMOEBA_GROWING))
14779     {
14780       AmoebaCnt[AmoebaNr[x][y]]--;
14781       AmoebaCnt2[AmoebaNr[x][y]]--;
14782     }
14783
14784     if (IS_MOVING(x, y))
14785       RemoveMovingField(x, y);
14786     else
14787     {
14788       RemoveField(x, y);
14789       TEST_DrawLevelField(x, y);
14790     }
14791
14792     // if digged element was about to explode, prevent the explosion
14793     ExplodeField[x][y] = EX_TYPE_NONE;
14794
14795     PlayLevelSoundAction(x, y, action);
14796   }
14797
14798   Store[x][y] = EL_EMPTY;
14799
14800   // this makes it possible to leave the removed element again
14801   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14802     Store[x][y] = element;
14803
14804   return TRUE;
14805 }
14806
14807 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14808 {
14809   int jx = player->jx, jy = player->jy;
14810   int x = jx + dx, y = jy + dy;
14811   int snap_direction = (dx == -1 ? MV_LEFT  :
14812                         dx == +1 ? MV_RIGHT :
14813                         dy == -1 ? MV_UP    :
14814                         dy == +1 ? MV_DOWN  : MV_NONE);
14815   boolean can_continue_snapping = (level.continuous_snapping &&
14816                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14817
14818   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14819     return FALSE;
14820
14821   if (!player->active || !IN_LEV_FIELD(x, y))
14822     return FALSE;
14823
14824   if (dx && dy)
14825     return FALSE;
14826
14827   if (!dx && !dy)
14828   {
14829     if (player->MovPos == 0)
14830       player->is_pushing = FALSE;
14831
14832     player->is_snapping = FALSE;
14833
14834     if (player->MovPos == 0)
14835     {
14836       player->is_moving = FALSE;
14837       player->is_digging = FALSE;
14838       player->is_collecting = FALSE;
14839     }
14840
14841     return FALSE;
14842   }
14843
14844   // prevent snapping with already pressed snap key when not allowed
14845   if (player->is_snapping && !can_continue_snapping)
14846     return FALSE;
14847
14848   player->MovDir = snap_direction;
14849
14850   if (player->MovPos == 0)
14851   {
14852     player->is_moving = FALSE;
14853     player->is_digging = FALSE;
14854     player->is_collecting = FALSE;
14855   }
14856
14857   player->is_dropping = FALSE;
14858   player->is_dropping_pressed = FALSE;
14859   player->drop_pressed_delay = 0;
14860
14861   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14862     return FALSE;
14863
14864   player->is_snapping = TRUE;
14865   player->is_active = TRUE;
14866
14867   if (player->MovPos == 0)
14868   {
14869     player->is_moving = FALSE;
14870     player->is_digging = FALSE;
14871     player->is_collecting = FALSE;
14872   }
14873
14874   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14875     TEST_DrawLevelField(player->last_jx, player->last_jy);
14876
14877   TEST_DrawLevelField(x, y);
14878
14879   return TRUE;
14880 }
14881
14882 static boolean DropElement(struct PlayerInfo *player)
14883 {
14884   int old_element, new_element;
14885   int dropx = player->jx, dropy = player->jy;
14886   int drop_direction = player->MovDir;
14887   int drop_side = drop_direction;
14888   int drop_element = get_next_dropped_element(player);
14889
14890   /* do not drop an element on top of another element; when holding drop key
14891      pressed without moving, dropped element must move away before the next
14892      element can be dropped (this is especially important if the next element
14893      is dynamite, which can be placed on background for historical reasons) */
14894   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14895     return MP_ACTION;
14896
14897   if (IS_THROWABLE(drop_element))
14898   {
14899     dropx += GET_DX_FROM_DIR(drop_direction);
14900     dropy += GET_DY_FROM_DIR(drop_direction);
14901
14902     if (!IN_LEV_FIELD(dropx, dropy))
14903       return FALSE;
14904   }
14905
14906   old_element = Tile[dropx][dropy];     // old element at dropping position
14907   new_element = drop_element;           // default: no change when dropping
14908
14909   // check if player is active, not moving and ready to drop
14910   if (!player->active || player->MovPos || player->drop_delay > 0)
14911     return FALSE;
14912
14913   // check if player has anything that can be dropped
14914   if (new_element == EL_UNDEFINED)
14915     return FALSE;
14916
14917   // only set if player has anything that can be dropped
14918   player->is_dropping_pressed = TRUE;
14919
14920   // check if drop key was pressed long enough for EM style dynamite
14921   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14922     return FALSE;
14923
14924   // check if anything can be dropped at the current position
14925   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14926     return FALSE;
14927
14928   // collected custom elements can only be dropped on empty fields
14929   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14930     return FALSE;
14931
14932   if (old_element != EL_EMPTY)
14933     Back[dropx][dropy] = old_element;   // store old element on this field
14934
14935   ResetGfxAnimation(dropx, dropy);
14936   ResetRandomAnimationValue(dropx, dropy);
14937
14938   if (player->inventory_size > 0 ||
14939       player->inventory_infinite_element != EL_UNDEFINED)
14940   {
14941     if (player->inventory_size > 0)
14942     {
14943       player->inventory_size--;
14944
14945       DrawGameDoorValues();
14946
14947       if (new_element == EL_DYNAMITE)
14948         new_element = EL_DYNAMITE_ACTIVE;
14949       else if (new_element == EL_EM_DYNAMITE)
14950         new_element = EL_EM_DYNAMITE_ACTIVE;
14951       else if (new_element == EL_SP_DISK_RED)
14952         new_element = EL_SP_DISK_RED_ACTIVE;
14953     }
14954
14955     Tile[dropx][dropy] = new_element;
14956
14957     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14958       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14959                           el2img(Tile[dropx][dropy]), 0);
14960
14961     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14962
14963     // needed if previous element just changed to "empty" in the last frame
14964     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14965
14966     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14967                                player->index_bit, drop_side);
14968     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14969                                         CE_PLAYER_DROPS_X,
14970                                         player->index_bit, drop_side);
14971
14972     TestIfElementTouchesCustomElement(dropx, dropy);
14973   }
14974   else          // player is dropping a dyna bomb
14975   {
14976     player->dynabombs_left--;
14977
14978     Tile[dropx][dropy] = new_element;
14979
14980     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14981       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14982                           el2img(Tile[dropx][dropy]), 0);
14983
14984     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14985   }
14986
14987   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14988     InitField_WithBug1(dropx, dropy, FALSE);
14989
14990   new_element = Tile[dropx][dropy];     // element might have changed
14991
14992   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14993       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14994   {
14995     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14996       MovDir[dropx][dropy] = drop_direction;
14997
14998     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14999
15000     // do not cause impact style collision by dropping elements that can fall
15001     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15002   }
15003
15004   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15005   player->is_dropping = TRUE;
15006
15007   player->drop_pressed_delay = 0;
15008   player->is_dropping_pressed = FALSE;
15009
15010   player->drop_x = dropx;
15011   player->drop_y = dropy;
15012
15013   return TRUE;
15014 }
15015
15016 // ----------------------------------------------------------------------------
15017 // game sound playing functions
15018 // ----------------------------------------------------------------------------
15019
15020 static int *loop_sound_frame = NULL;
15021 static int *loop_sound_volume = NULL;
15022
15023 void InitPlayLevelSound(void)
15024 {
15025   int num_sounds = getSoundListSize();
15026
15027   checked_free(loop_sound_frame);
15028   checked_free(loop_sound_volume);
15029
15030   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15031   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15032 }
15033
15034 static void PlayLevelSound(int x, int y, int nr)
15035 {
15036   int sx = SCREENX(x), sy = SCREENY(y);
15037   int volume, stereo_position;
15038   int max_distance = 8;
15039   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15040
15041   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15042       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15043     return;
15044
15045   if (!IN_LEV_FIELD(x, y) ||
15046       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15047       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15048     return;
15049
15050   volume = SOUND_MAX_VOLUME;
15051
15052   if (!IN_SCR_FIELD(sx, sy))
15053   {
15054     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15055     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15056
15057     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15058   }
15059
15060   stereo_position = (SOUND_MAX_LEFT +
15061                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15062                      (SCR_FIELDX + 2 * max_distance));
15063
15064   if (IS_LOOP_SOUND(nr))
15065   {
15066     /* This assures that quieter loop sounds do not overwrite louder ones,
15067        while restarting sound volume comparison with each new game frame. */
15068
15069     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15070       return;
15071
15072     loop_sound_volume[nr] = volume;
15073     loop_sound_frame[nr] = FrameCounter;
15074   }
15075
15076   PlaySoundExt(nr, volume, stereo_position, type);
15077 }
15078
15079 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15080 {
15081   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15082                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15083                  y < LEVELY(BY1) ? LEVELY(BY1) :
15084                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15085                  sound_action);
15086 }
15087
15088 static void PlayLevelSoundAction(int x, int y, int action)
15089 {
15090   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15091 }
15092
15093 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15094 {
15095   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15096
15097   if (sound_effect != SND_UNDEFINED)
15098     PlayLevelSound(x, y, sound_effect);
15099 }
15100
15101 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15102                                               int action)
15103 {
15104   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15105
15106   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15107     PlayLevelSound(x, y, sound_effect);
15108 }
15109
15110 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15111 {
15112   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15113
15114   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15115     PlayLevelSound(x, y, sound_effect);
15116 }
15117
15118 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15119 {
15120   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15121
15122   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15123     StopSound(sound_effect);
15124 }
15125
15126 static int getLevelMusicNr(void)
15127 {
15128   if (levelset.music[level_nr] != MUS_UNDEFINED)
15129     return levelset.music[level_nr];            // from config file
15130   else
15131     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15132 }
15133
15134 static void FadeLevelSounds(void)
15135 {
15136   FadeSounds();
15137 }
15138
15139 static void FadeLevelMusic(void)
15140 {
15141   int music_nr = getLevelMusicNr();
15142   char *curr_music = getCurrentlyPlayingMusicFilename();
15143   char *next_music = getMusicInfoEntryFilename(music_nr);
15144
15145   if (!strEqual(curr_music, next_music))
15146     FadeMusic();
15147 }
15148
15149 void FadeLevelSoundsAndMusic(void)
15150 {
15151   FadeLevelSounds();
15152   FadeLevelMusic();
15153 }
15154
15155 static void PlayLevelMusic(void)
15156 {
15157   int music_nr = getLevelMusicNr();
15158   char *curr_music = getCurrentlyPlayingMusicFilename();
15159   char *next_music = getMusicInfoEntryFilename(music_nr);
15160
15161   if (!strEqual(curr_music, next_music))
15162     PlayMusicLoop(music_nr);
15163 }
15164
15165 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15166 {
15167   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15168   int offset = 0;
15169   int x = xx - offset;
15170   int y = yy - offset;
15171
15172   switch (sample)
15173   {
15174     case SOUND_blank:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15176       break;
15177
15178     case SOUND_roll:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15180       break;
15181
15182     case SOUND_stone:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15184       break;
15185
15186     case SOUND_nut:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15188       break;
15189
15190     case SOUND_crack:
15191       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15192       break;
15193
15194     case SOUND_bug:
15195       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15196       break;
15197
15198     case SOUND_tank:
15199       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15200       break;
15201
15202     case SOUND_android_clone:
15203       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15204       break;
15205
15206     case SOUND_android_move:
15207       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15208       break;
15209
15210     case SOUND_spring:
15211       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15212       break;
15213
15214     case SOUND_slurp:
15215       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15216       break;
15217
15218     case SOUND_eater:
15219       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15220       break;
15221
15222     case SOUND_eater_eat:
15223       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15224       break;
15225
15226     case SOUND_alien:
15227       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15228       break;
15229
15230     case SOUND_collect:
15231       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15232       break;
15233
15234     case SOUND_diamond:
15235       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15236       break;
15237
15238     case SOUND_squash:
15239       // !!! CHECK THIS !!!
15240 #if 1
15241       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15242 #else
15243       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15244 #endif
15245       break;
15246
15247     case SOUND_wonderfall:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15249       break;
15250
15251     case SOUND_drip:
15252       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15253       break;
15254
15255     case SOUND_push:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15257       break;
15258
15259     case SOUND_dirt:
15260       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15261       break;
15262
15263     case SOUND_acid:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15265       break;
15266
15267     case SOUND_ball:
15268       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15269       break;
15270
15271     case SOUND_slide:
15272       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15273       break;
15274
15275     case SOUND_wonder:
15276       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15277       break;
15278
15279     case SOUND_door:
15280       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15281       break;
15282
15283     case SOUND_exit_open:
15284       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15285       break;
15286
15287     case SOUND_exit_leave:
15288       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15289       break;
15290
15291     case SOUND_dynamite:
15292       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15293       break;
15294
15295     case SOUND_tick:
15296       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15297       break;
15298
15299     case SOUND_press:
15300       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15301       break;
15302
15303     case SOUND_wheel:
15304       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15305       break;
15306
15307     case SOUND_boom:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15309       break;
15310
15311     case SOUND_die:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15313       break;
15314
15315     case SOUND_time:
15316       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15317       break;
15318
15319     default:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15321       break;
15322   }
15323 }
15324
15325 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15326 {
15327   int element = map_element_SP_to_RND(element_sp);
15328   int action = map_action_SP_to_RND(action_sp);
15329   int offset = (setup.sp_show_border_elements ? 0 : 1);
15330   int x = xx - offset;
15331   int y = yy - offset;
15332
15333   PlayLevelSoundElementAction(x, y, element, action);
15334 }
15335
15336 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15337 {
15338   int element = map_element_MM_to_RND(element_mm);
15339   int action = map_action_MM_to_RND(action_mm);
15340   int offset = 0;
15341   int x = xx - offset;
15342   int y = yy - offset;
15343
15344   if (!IS_MM_ELEMENT(element))
15345     element = EL_MM_DEFAULT;
15346
15347   PlayLevelSoundElementAction(x, y, element, action);
15348 }
15349
15350 void PlaySound_MM(int sound_mm)
15351 {
15352   int sound = map_sound_MM_to_RND(sound_mm);
15353
15354   if (sound == SND_UNDEFINED)
15355     return;
15356
15357   PlaySound(sound);
15358 }
15359
15360 void PlaySoundLoop_MM(int sound_mm)
15361 {
15362   int sound = map_sound_MM_to_RND(sound_mm);
15363
15364   if (sound == SND_UNDEFINED)
15365     return;
15366
15367   PlaySoundLoop(sound);
15368 }
15369
15370 void StopSound_MM(int sound_mm)
15371 {
15372   int sound = map_sound_MM_to_RND(sound_mm);
15373
15374   if (sound == SND_UNDEFINED)
15375     return;
15376
15377   StopSound(sound);
15378 }
15379
15380 void RaiseScore(int value)
15381 {
15382   game.score += value;
15383
15384   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15385
15386   DisplayGameControlValues();
15387 }
15388
15389 void RaiseScoreElement(int element)
15390 {
15391   switch (element)
15392   {
15393     case EL_EMERALD:
15394     case EL_BD_DIAMOND:
15395     case EL_EMERALD_YELLOW:
15396     case EL_EMERALD_RED:
15397     case EL_EMERALD_PURPLE:
15398     case EL_SP_INFOTRON:
15399       RaiseScore(level.score[SC_EMERALD]);
15400       break;
15401     case EL_DIAMOND:
15402       RaiseScore(level.score[SC_DIAMOND]);
15403       break;
15404     case EL_CRYSTAL:
15405       RaiseScore(level.score[SC_CRYSTAL]);
15406       break;
15407     case EL_PEARL:
15408       RaiseScore(level.score[SC_PEARL]);
15409       break;
15410     case EL_BUG:
15411     case EL_BD_BUTTERFLY:
15412     case EL_SP_ELECTRON:
15413       RaiseScore(level.score[SC_BUG]);
15414       break;
15415     case EL_SPACESHIP:
15416     case EL_BD_FIREFLY:
15417     case EL_SP_SNIKSNAK:
15418       RaiseScore(level.score[SC_SPACESHIP]);
15419       break;
15420     case EL_YAMYAM:
15421     case EL_DARK_YAMYAM:
15422       RaiseScore(level.score[SC_YAMYAM]);
15423       break;
15424     case EL_ROBOT:
15425       RaiseScore(level.score[SC_ROBOT]);
15426       break;
15427     case EL_PACMAN:
15428       RaiseScore(level.score[SC_PACMAN]);
15429       break;
15430     case EL_NUT:
15431       RaiseScore(level.score[SC_NUT]);
15432       break;
15433     case EL_DYNAMITE:
15434     case EL_EM_DYNAMITE:
15435     case EL_SP_DISK_RED:
15436     case EL_DYNABOMB_INCREASE_NUMBER:
15437     case EL_DYNABOMB_INCREASE_SIZE:
15438     case EL_DYNABOMB_INCREASE_POWER:
15439       RaiseScore(level.score[SC_DYNAMITE]);
15440       break;
15441     case EL_SHIELD_NORMAL:
15442     case EL_SHIELD_DEADLY:
15443       RaiseScore(level.score[SC_SHIELD]);
15444       break;
15445     case EL_EXTRA_TIME:
15446       RaiseScore(level.extra_time_score);
15447       break;
15448     case EL_KEY_1:
15449     case EL_KEY_2:
15450     case EL_KEY_3:
15451     case EL_KEY_4:
15452     case EL_EM_KEY_1:
15453     case EL_EM_KEY_2:
15454     case EL_EM_KEY_3:
15455     case EL_EM_KEY_4:
15456     case EL_EMC_KEY_5:
15457     case EL_EMC_KEY_6:
15458     case EL_EMC_KEY_7:
15459     case EL_EMC_KEY_8:
15460     case EL_DC_KEY_WHITE:
15461       RaiseScore(level.score[SC_KEY]);
15462       break;
15463     default:
15464       RaiseScore(element_info[element].collect_score);
15465       break;
15466   }
15467 }
15468
15469 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15470 {
15471   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15472   {
15473     if (!quick_quit)
15474     {
15475       // prevent short reactivation of overlay buttons while closing door
15476       SetOverlayActive(FALSE);
15477
15478       // door may still be open due to skipped or envelope style request
15479       CloseDoor(DOOR_CLOSE_1);
15480     }
15481
15482     if (network.enabled)
15483       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15484     else
15485     {
15486       if (quick_quit)
15487         FadeSkipNextFadeIn();
15488
15489       SetGameStatus(GAME_MODE_MAIN);
15490
15491       DrawMainMenu();
15492     }
15493   }
15494   else          // continue playing the game
15495   {
15496     if (tape.playing && tape.deactivate_display)
15497       TapeDeactivateDisplayOff(TRUE);
15498
15499     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15500
15501     if (tape.playing && tape.deactivate_display)
15502       TapeDeactivateDisplayOn();
15503   }
15504 }
15505
15506 void RequestQuitGame(boolean escape_key_pressed)
15507 {
15508   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15509   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15510                         level_editor_test_game);
15511   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15512                           quick_quit);
15513
15514   RequestQuitGameExt(skip_request, quick_quit,
15515                      "Do you really want to quit the game?");
15516 }
15517
15518 void RequestRestartGame(char *message)
15519 {
15520   game.restart_game_message = NULL;
15521
15522   boolean has_started_game = hasStartedNetworkGame();
15523   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15524
15525   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15526   {
15527     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15528   }
15529   else
15530   {
15531     // needed in case of envelope request to close game panel
15532     CloseDoor(DOOR_CLOSE_1);
15533
15534     SetGameStatus(GAME_MODE_MAIN);
15535
15536     DrawMainMenu();
15537   }
15538 }
15539
15540 void CheckGameOver(void)
15541 {
15542   static boolean last_game_over = FALSE;
15543   static int game_over_delay = 0;
15544   int game_over_delay_value = 50;
15545   boolean game_over = checkGameFailed();
15546
15547   // do not handle game over if request dialog is already active
15548   if (game.request_active)
15549     return;
15550
15551   // do not ask to play again if game was never actually played
15552   if (!game.GamePlayed)
15553     return;
15554
15555   if (!game_over)
15556   {
15557     last_game_over = FALSE;
15558     game_over_delay = game_over_delay_value;
15559
15560     return;
15561   }
15562
15563   if (game_over_delay > 0)
15564   {
15565     game_over_delay--;
15566
15567     return;
15568   }
15569
15570   if (last_game_over != game_over)
15571     game.restart_game_message = (hasStartedNetworkGame() ?
15572                                  "Game over! Play it again?" :
15573                                  "Game over!");
15574
15575   last_game_over = game_over;
15576 }
15577
15578 boolean checkGameSolved(void)
15579 {
15580   // set for all game engines if level was solved
15581   return game.LevelSolved_GameEnd;
15582 }
15583
15584 boolean checkGameFailed(void)
15585 {
15586   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15587     return (game_em.game_over && !game_em.level_solved);
15588   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15589     return (game_sp.game_over && !game_sp.level_solved);
15590   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15591     return (game_mm.game_over && !game_mm.level_solved);
15592   else                          // GAME_ENGINE_TYPE_RND
15593     return (game.GameOver && !game.LevelSolved);
15594 }
15595
15596 boolean checkGameEnded(void)
15597 {
15598   return (checkGameSolved() || checkGameFailed());
15599 }
15600
15601
15602 // ----------------------------------------------------------------------------
15603 // random generator functions
15604 // ----------------------------------------------------------------------------
15605
15606 unsigned int InitEngineRandom_RND(int seed)
15607 {
15608   game.num_random_calls = 0;
15609
15610   return InitEngineRandom(seed);
15611 }
15612
15613 unsigned int RND(int max)
15614 {
15615   if (max > 0)
15616   {
15617     game.num_random_calls++;
15618
15619     return GetEngineRandom(max);
15620   }
15621
15622   return 0;
15623 }
15624
15625
15626 // ----------------------------------------------------------------------------
15627 // game engine snapshot handling functions
15628 // ----------------------------------------------------------------------------
15629
15630 struct EngineSnapshotInfo
15631 {
15632   // runtime values for custom element collect score
15633   int collect_score[NUM_CUSTOM_ELEMENTS];
15634
15635   // runtime values for group element choice position
15636   int choice_pos[NUM_GROUP_ELEMENTS];
15637
15638   // runtime values for belt position animations
15639   int belt_graphic[4][NUM_BELT_PARTS];
15640   int belt_anim_mode[4][NUM_BELT_PARTS];
15641 };
15642
15643 static struct EngineSnapshotInfo engine_snapshot_rnd;
15644 static char *snapshot_level_identifier = NULL;
15645 static int snapshot_level_nr = -1;
15646
15647 static void SaveEngineSnapshotValues_RND(void)
15648 {
15649   static int belt_base_active_element[4] =
15650   {
15651     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15652     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15653     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15654     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15655   };
15656   int i, j;
15657
15658   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15659   {
15660     int element = EL_CUSTOM_START + i;
15661
15662     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15663   }
15664
15665   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15666   {
15667     int element = EL_GROUP_START + i;
15668
15669     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15670   }
15671
15672   for (i = 0; i < 4; i++)
15673   {
15674     for (j = 0; j < NUM_BELT_PARTS; j++)
15675     {
15676       int element = belt_base_active_element[i] + j;
15677       int graphic = el2img(element);
15678       int anim_mode = graphic_info[graphic].anim_mode;
15679
15680       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15681       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15682     }
15683   }
15684 }
15685
15686 static void LoadEngineSnapshotValues_RND(void)
15687 {
15688   unsigned int num_random_calls = game.num_random_calls;
15689   int i, j;
15690
15691   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15692   {
15693     int element = EL_CUSTOM_START + i;
15694
15695     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15696   }
15697
15698   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15699   {
15700     int element = EL_GROUP_START + i;
15701
15702     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15703   }
15704
15705   for (i = 0; i < 4; i++)
15706   {
15707     for (j = 0; j < NUM_BELT_PARTS; j++)
15708     {
15709       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15710       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15711
15712       graphic_info[graphic].anim_mode = anim_mode;
15713     }
15714   }
15715
15716   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15717   {
15718     InitRND(tape.random_seed);
15719     for (i = 0; i < num_random_calls; i++)
15720       RND(1);
15721   }
15722
15723   if (game.num_random_calls != num_random_calls)
15724   {
15725     Error("number of random calls out of sync");
15726     Error("number of random calls should be %d", num_random_calls);
15727     Error("number of random calls is %d", game.num_random_calls);
15728
15729     Fail("this should not happen -- please debug");
15730   }
15731 }
15732
15733 void FreeEngineSnapshotSingle(void)
15734 {
15735   FreeSnapshotSingle();
15736
15737   setString(&snapshot_level_identifier, NULL);
15738   snapshot_level_nr = -1;
15739 }
15740
15741 void FreeEngineSnapshotList(void)
15742 {
15743   FreeSnapshotList();
15744 }
15745
15746 static ListNode *SaveEngineSnapshotBuffers(void)
15747 {
15748   ListNode *buffers = NULL;
15749
15750   // copy some special values to a structure better suited for the snapshot
15751
15752   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15753     SaveEngineSnapshotValues_RND();
15754   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15755     SaveEngineSnapshotValues_EM();
15756   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15757     SaveEngineSnapshotValues_SP(&buffers);
15758   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15759     SaveEngineSnapshotValues_MM(&buffers);
15760
15761   // save values stored in special snapshot structure
15762
15763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15764     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15765   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15766     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15767   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15768     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15769   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15770     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15771
15772   // save further RND engine values
15773
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15777
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15783
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15787
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15789
15790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15792
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15809   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15811
15812   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15813   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15814
15815   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15816   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15817   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15818
15819   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15820   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15821
15822   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15823   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15824   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15825   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15826   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15827
15828   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15829   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15830
15831 #if 0
15832   ListNode *node = engine_snapshot_list_rnd;
15833   int num_bytes = 0;
15834
15835   while (node != NULL)
15836   {
15837     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15838
15839     node = node->next;
15840   }
15841
15842   Debug("game:playing:SaveEngineSnapshotBuffers",
15843         "size of engine snapshot: %d bytes", num_bytes);
15844 #endif
15845
15846   return buffers;
15847 }
15848
15849 void SaveEngineSnapshotSingle(void)
15850 {
15851   ListNode *buffers = SaveEngineSnapshotBuffers();
15852
15853   // finally save all snapshot buffers to single snapshot
15854   SaveSnapshotSingle(buffers);
15855
15856   // save level identification information
15857   setString(&snapshot_level_identifier, leveldir_current->identifier);
15858   snapshot_level_nr = level_nr;
15859 }
15860
15861 boolean CheckSaveEngineSnapshotToList(void)
15862 {
15863   boolean save_snapshot =
15864     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15865      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15866       game.snapshot.changed_action) ||
15867      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15868       game.snapshot.collected_item));
15869
15870   game.snapshot.changed_action = FALSE;
15871   game.snapshot.collected_item = FALSE;
15872   game.snapshot.save_snapshot = save_snapshot;
15873
15874   return save_snapshot;
15875 }
15876
15877 void SaveEngineSnapshotToList(void)
15878 {
15879   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15880       tape.quick_resume)
15881     return;
15882
15883   ListNode *buffers = SaveEngineSnapshotBuffers();
15884
15885   // finally save all snapshot buffers to snapshot list
15886   SaveSnapshotToList(buffers);
15887 }
15888
15889 void SaveEngineSnapshotToListInitial(void)
15890 {
15891   FreeEngineSnapshotList();
15892
15893   SaveEngineSnapshotToList();
15894 }
15895
15896 static void LoadEngineSnapshotValues(void)
15897 {
15898   // restore special values from snapshot structure
15899
15900   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15901     LoadEngineSnapshotValues_RND();
15902   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15903     LoadEngineSnapshotValues_EM();
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15905     LoadEngineSnapshotValues_SP();
15906   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15907     LoadEngineSnapshotValues_MM();
15908 }
15909
15910 void LoadEngineSnapshotSingle(void)
15911 {
15912   LoadSnapshotSingle();
15913
15914   LoadEngineSnapshotValues();
15915 }
15916
15917 static void LoadEngineSnapshot_Undo(int steps)
15918 {
15919   LoadSnapshotFromList_Older(steps);
15920
15921   LoadEngineSnapshotValues();
15922 }
15923
15924 static void LoadEngineSnapshot_Redo(int steps)
15925 {
15926   LoadSnapshotFromList_Newer(steps);
15927
15928   LoadEngineSnapshotValues();
15929 }
15930
15931 boolean CheckEngineSnapshotSingle(void)
15932 {
15933   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15934           snapshot_level_nr == level_nr);
15935 }
15936
15937 boolean CheckEngineSnapshotList(void)
15938 {
15939   return CheckSnapshotList();
15940 }
15941
15942
15943 // ---------- new game button stuff -------------------------------------------
15944
15945 static struct
15946 {
15947   int graphic;
15948   struct XY *pos;
15949   int gadget_id;
15950   boolean *setup_value;
15951   boolean allowed_on_tape;
15952   boolean is_touch_button;
15953   char *infotext;
15954 } gamebutton_info[NUM_GAME_BUTTONS] =
15955 {
15956   {
15957     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15958     GAME_CTRL_ID_STOP,                          NULL,
15959     TRUE, FALSE,                                "stop game"
15960   },
15961   {
15962     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15963     GAME_CTRL_ID_PAUSE,                         NULL,
15964     TRUE, FALSE,                                "pause game"
15965   },
15966   {
15967     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15968     GAME_CTRL_ID_PLAY,                          NULL,
15969     TRUE, FALSE,                                "play game"
15970   },
15971   {
15972     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15973     GAME_CTRL_ID_UNDO,                          NULL,
15974     TRUE, FALSE,                                "undo step"
15975   },
15976   {
15977     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15978     GAME_CTRL_ID_REDO,                          NULL,
15979     TRUE, FALSE,                                "redo step"
15980   },
15981   {
15982     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15983     GAME_CTRL_ID_SAVE,                          NULL,
15984     TRUE, FALSE,                                "save game"
15985   },
15986   {
15987     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15988     GAME_CTRL_ID_PAUSE2,                        NULL,
15989     TRUE, FALSE,                                "pause game"
15990   },
15991   {
15992     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15993     GAME_CTRL_ID_LOAD,                          NULL,
15994     TRUE, FALSE,                                "load game"
15995   },
15996   {
15997     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15998     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15999     FALSE, FALSE,                               "stop game"
16000   },
16001   {
16002     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16003     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16004     FALSE, FALSE,                               "pause game"
16005   },
16006   {
16007     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16008     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16009     FALSE, FALSE,                               "play game"
16010   },
16011   {
16012     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16013     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16014     FALSE, TRUE,                                "stop game"
16015   },
16016   {
16017     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16018     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16019     FALSE, TRUE,                                "pause game"
16020   },
16021   {
16022     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16023     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16024     TRUE, FALSE,                                "background music on/off"
16025   },
16026   {
16027     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16028     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16029     TRUE, FALSE,                                "sound loops on/off"
16030   },
16031   {
16032     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16033     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16034     TRUE, FALSE,                                "normal sounds on/off"
16035   },
16036   {
16037     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16038     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16039     FALSE, FALSE,                               "background music on/off"
16040   },
16041   {
16042     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16043     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16044     FALSE, FALSE,                               "sound loops on/off"
16045   },
16046   {
16047     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16048     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16049     FALSE, FALSE,                               "normal sounds on/off"
16050   }
16051 };
16052
16053 void CreateGameButtons(void)
16054 {
16055   int i;
16056
16057   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16058   {
16059     int graphic = gamebutton_info[i].graphic;
16060     struct GraphicInfo *gfx = &graphic_info[graphic];
16061     struct XY *pos = gamebutton_info[i].pos;
16062     struct GadgetInfo *gi;
16063     int button_type;
16064     boolean checked;
16065     unsigned int event_mask;
16066     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16067     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16068     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16069     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16070     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16071     int gd_x   = gfx->src_x;
16072     int gd_y   = gfx->src_y;
16073     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16074     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16075     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16076     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16077     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16078     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16079     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16080     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16081     int id = i;
16082
16083     if (gfx->bitmap == NULL)
16084     {
16085       game_gadget[id] = NULL;
16086
16087       continue;
16088     }
16089
16090     if (id == GAME_CTRL_ID_STOP ||
16091         id == GAME_CTRL_ID_PANEL_STOP ||
16092         id == GAME_CTRL_ID_TOUCH_STOP ||
16093         id == GAME_CTRL_ID_PLAY ||
16094         id == GAME_CTRL_ID_PANEL_PLAY ||
16095         id == GAME_CTRL_ID_SAVE ||
16096         id == GAME_CTRL_ID_LOAD)
16097     {
16098       button_type = GD_TYPE_NORMAL_BUTTON;
16099       checked = FALSE;
16100       event_mask = GD_EVENT_RELEASED;
16101     }
16102     else if (id == GAME_CTRL_ID_UNDO ||
16103              id == GAME_CTRL_ID_REDO)
16104     {
16105       button_type = GD_TYPE_NORMAL_BUTTON;
16106       checked = FALSE;
16107       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16108     }
16109     else
16110     {
16111       button_type = GD_TYPE_CHECK_BUTTON;
16112       checked = (gamebutton_info[i].setup_value != NULL ?
16113                  *gamebutton_info[i].setup_value : FALSE);
16114       event_mask = GD_EVENT_PRESSED;
16115     }
16116
16117     gi = CreateGadget(GDI_CUSTOM_ID, id,
16118                       GDI_IMAGE_ID, graphic,
16119                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16120                       GDI_X, base_x + x,
16121                       GDI_Y, base_y + y,
16122                       GDI_WIDTH, gfx->width,
16123                       GDI_HEIGHT, gfx->height,
16124                       GDI_TYPE, button_type,
16125                       GDI_STATE, GD_BUTTON_UNPRESSED,
16126                       GDI_CHECKED, checked,
16127                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16128                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16129                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16130                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16131                       GDI_DIRECT_DRAW, FALSE,
16132                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16133                       GDI_EVENT_MASK, event_mask,
16134                       GDI_CALLBACK_ACTION, HandleGameButtons,
16135                       GDI_END);
16136
16137     if (gi == NULL)
16138       Fail("cannot create gadget");
16139
16140     game_gadget[id] = gi;
16141   }
16142 }
16143
16144 void FreeGameButtons(void)
16145 {
16146   int i;
16147
16148   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16149     FreeGadget(game_gadget[i]);
16150 }
16151
16152 static void UnmapGameButtonsAtSamePosition(int id)
16153 {
16154   int i;
16155
16156   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16157     if (i != id &&
16158         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16159         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16160       UnmapGadget(game_gadget[i]);
16161 }
16162
16163 static void UnmapGameButtonsAtSamePosition_All(void)
16164 {
16165   if (setup.show_load_save_buttons)
16166   {
16167     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16168     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16169     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16170   }
16171   else if (setup.show_undo_redo_buttons)
16172   {
16173     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16174     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16175     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16176   }
16177   else
16178   {
16179     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16180     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16181     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16182
16183     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16184     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16185     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16186   }
16187 }
16188
16189 void MapLoadSaveButtons(void)
16190 {
16191   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16192   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16193
16194   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16195   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16196 }
16197
16198 void MapUndoRedoButtons(void)
16199 {
16200   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16201   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16202
16203   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16204   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16205 }
16206
16207 void ModifyPauseButtons(void)
16208 {
16209   static int ids[] =
16210   {
16211     GAME_CTRL_ID_PAUSE,
16212     GAME_CTRL_ID_PAUSE2,
16213     GAME_CTRL_ID_PANEL_PAUSE,
16214     GAME_CTRL_ID_TOUCH_PAUSE,
16215     -1
16216   };
16217   int i;
16218
16219   for (i = 0; ids[i] > -1; i++)
16220     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16221 }
16222
16223 static void MapGameButtonsExt(boolean on_tape)
16224 {
16225   int i;
16226
16227   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16228     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16229       MapGadget(game_gadget[i]);
16230
16231   UnmapGameButtonsAtSamePosition_All();
16232
16233   RedrawGameButtons();
16234 }
16235
16236 static void UnmapGameButtonsExt(boolean on_tape)
16237 {
16238   int i;
16239
16240   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16241     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16242       UnmapGadget(game_gadget[i]);
16243 }
16244
16245 static void RedrawGameButtonsExt(boolean on_tape)
16246 {
16247   int i;
16248
16249   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16250     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16251       RedrawGadget(game_gadget[i]);
16252 }
16253
16254 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16255 {
16256   if (gi == NULL)
16257     return;
16258
16259   gi->checked = state;
16260 }
16261
16262 static void RedrawSoundButtonGadget(int id)
16263 {
16264   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16265              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16266              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16267              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16268              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16269              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16270              id);
16271
16272   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16273   RedrawGadget(game_gadget[id2]);
16274 }
16275
16276 void MapGameButtons(void)
16277 {
16278   MapGameButtonsExt(FALSE);
16279 }
16280
16281 void UnmapGameButtons(void)
16282 {
16283   UnmapGameButtonsExt(FALSE);
16284 }
16285
16286 void RedrawGameButtons(void)
16287 {
16288   RedrawGameButtonsExt(FALSE);
16289 }
16290
16291 void MapGameButtonsOnTape(void)
16292 {
16293   MapGameButtonsExt(TRUE);
16294 }
16295
16296 void UnmapGameButtonsOnTape(void)
16297 {
16298   UnmapGameButtonsExt(TRUE);
16299 }
16300
16301 void RedrawGameButtonsOnTape(void)
16302 {
16303   RedrawGameButtonsExt(TRUE);
16304 }
16305
16306 static void GameUndoRedoExt(void)
16307 {
16308   ClearPlayerAction();
16309
16310   tape.pausing = TRUE;
16311
16312   RedrawPlayfield();
16313   UpdateAndDisplayGameControlValues();
16314
16315   DrawCompleteVideoDisplay();
16316   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16317   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16318   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16319
16320   ModifyPauseButtons();
16321
16322   BackToFront();
16323 }
16324
16325 static void GameUndo(int steps)
16326 {
16327   if (!CheckEngineSnapshotList())
16328     return;
16329
16330   int tape_property_bits = tape.property_bits;
16331
16332   LoadEngineSnapshot_Undo(steps);
16333
16334   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16335
16336   GameUndoRedoExt();
16337 }
16338
16339 static void GameRedo(int steps)
16340 {
16341   if (!CheckEngineSnapshotList())
16342     return;
16343
16344   int tape_property_bits = tape.property_bits;
16345
16346   LoadEngineSnapshot_Redo(steps);
16347
16348   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16349
16350   GameUndoRedoExt();
16351 }
16352
16353 static void HandleGameButtonsExt(int id, int button)
16354 {
16355   static boolean game_undo_executed = FALSE;
16356   int steps = BUTTON_STEPSIZE(button);
16357   boolean handle_game_buttons =
16358     (game_status == GAME_MODE_PLAYING ||
16359      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16360
16361   if (!handle_game_buttons)
16362     return;
16363
16364   switch (id)
16365   {
16366     case GAME_CTRL_ID_STOP:
16367     case GAME_CTRL_ID_PANEL_STOP:
16368     case GAME_CTRL_ID_TOUCH_STOP:
16369       if (game_status == GAME_MODE_MAIN)
16370         break;
16371
16372       if (tape.playing)
16373         TapeStop();
16374       else
16375         RequestQuitGame(FALSE);
16376
16377       break;
16378
16379     case GAME_CTRL_ID_PAUSE:
16380     case GAME_CTRL_ID_PAUSE2:
16381     case GAME_CTRL_ID_PANEL_PAUSE:
16382     case GAME_CTRL_ID_TOUCH_PAUSE:
16383       if (network.enabled && game_status == GAME_MODE_PLAYING)
16384       {
16385         if (tape.pausing)
16386           SendToServer_ContinuePlaying();
16387         else
16388           SendToServer_PausePlaying();
16389       }
16390       else
16391         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16392
16393       game_undo_executed = FALSE;
16394
16395       break;
16396
16397     case GAME_CTRL_ID_PLAY:
16398     case GAME_CTRL_ID_PANEL_PLAY:
16399       if (game_status == GAME_MODE_MAIN)
16400       {
16401         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16402       }
16403       else if (tape.pausing)
16404       {
16405         if (network.enabled)
16406           SendToServer_ContinuePlaying();
16407         else
16408           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16409       }
16410       break;
16411
16412     case GAME_CTRL_ID_UNDO:
16413       // Important: When using "save snapshot when collecting an item" mode,
16414       // load last (current) snapshot for first "undo" after pressing "pause"
16415       // (else the last-but-one snapshot would be loaded, because the snapshot
16416       // pointer already points to the last snapshot when pressing "pause",
16417       // which is fine for "every step/move" mode, but not for "every collect")
16418       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16419           !game_undo_executed)
16420         steps--;
16421
16422       game_undo_executed = TRUE;
16423
16424       GameUndo(steps);
16425       break;
16426
16427     case GAME_CTRL_ID_REDO:
16428       GameRedo(steps);
16429       break;
16430
16431     case GAME_CTRL_ID_SAVE:
16432       TapeQuickSave();
16433       break;
16434
16435     case GAME_CTRL_ID_LOAD:
16436       TapeQuickLoad();
16437       break;
16438
16439     case SOUND_CTRL_ID_MUSIC:
16440     case SOUND_CTRL_ID_PANEL_MUSIC:
16441       if (setup.sound_music)
16442       { 
16443         setup.sound_music = FALSE;
16444
16445         FadeMusic();
16446       }
16447       else if (audio.music_available)
16448       { 
16449         setup.sound = setup.sound_music = TRUE;
16450
16451         SetAudioMode(setup.sound);
16452
16453         if (game_status == GAME_MODE_PLAYING)
16454           PlayLevelMusic();
16455       }
16456
16457       RedrawSoundButtonGadget(id);
16458
16459       break;
16460
16461     case SOUND_CTRL_ID_LOOPS:
16462     case SOUND_CTRL_ID_PANEL_LOOPS:
16463       if (setup.sound_loops)
16464         setup.sound_loops = FALSE;
16465       else if (audio.loops_available)
16466       {
16467         setup.sound = setup.sound_loops = TRUE;
16468
16469         SetAudioMode(setup.sound);
16470       }
16471
16472       RedrawSoundButtonGadget(id);
16473
16474       break;
16475
16476     case SOUND_CTRL_ID_SIMPLE:
16477     case SOUND_CTRL_ID_PANEL_SIMPLE:
16478       if (setup.sound_simple)
16479         setup.sound_simple = FALSE;
16480       else if (audio.sound_available)
16481       {
16482         setup.sound = setup.sound_simple = TRUE;
16483
16484         SetAudioMode(setup.sound);
16485       }
16486
16487       RedrawSoundButtonGadget(id);
16488
16489       break;
16490
16491     default:
16492       break;
16493   }
16494 }
16495
16496 static void HandleGameButtons(struct GadgetInfo *gi)
16497 {
16498   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16499 }
16500
16501 void HandleSoundButtonKeys(Key key)
16502 {
16503   if (key == setup.shortcut.sound_simple)
16504     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16505   else if (key == setup.shortcut.sound_loops)
16506     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16507   else if (key == setup.shortcut.sound_music)
16508     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16509 }