fixed crumbling graphics bug when player explodes after digging sand
[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 CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
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 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576   boolean restarting = (game_status == GAME_MODE_PLAYING);
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   if (restarting)
3589   {
3590     // force fading out global animations displayed during game play
3591     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3592   }
3593   else
3594   {
3595     SetGameStatus(GAME_MODE_PLAYING);
3596   }
3597
3598   if (level_editor_test_game)
3599     FadeSkipNextFadeOut();
3600   else
3601     FadeSetEnterScreen();
3602
3603   if (CheckFadeAll())
3604     fade_mask = REDRAW_ALL;
3605
3606   FadeLevelSoundsAndMusic();
3607
3608   ExpireSoundLoops(TRUE);
3609
3610   FadeOut(fade_mask);
3611
3612   if (restarting)
3613   {
3614     // force restarting global animations displayed during game play
3615     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3616
3617     SetGameStatus(GAME_MODE_PLAYING);
3618   }
3619
3620   if (level_editor_test_game)
3621     FadeSkipNextFadeIn();
3622
3623   // needed if different viewport properties defined for playing
3624   ChangeViewportPropertiesIfNeeded();
3625
3626   ClearField();
3627
3628   DrawCompleteVideoDisplay();
3629
3630   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3631
3632   InitGameEngine();
3633   InitGameControlValues();
3634
3635   if (tape.recording)
3636   {
3637     // initialize tape actions from game when recording tape
3638     tape.use_key_actions   = game.use_key_actions;
3639     tape.use_mouse_actions = game.use_mouse_actions;
3640
3641     // initialize visible playfield size when recording tape (for team mode)
3642     tape.scr_fieldx = SCR_FIELDX;
3643     tape.scr_fieldy = SCR_FIELDY;
3644   }
3645
3646   // don't play tapes over network
3647   network_playing = (network.enabled && !tape.playing);
3648
3649   for (i = 0; i < MAX_PLAYERS; i++)
3650   {
3651     struct PlayerInfo *player = &stored_player[i];
3652
3653     player->index_nr = i;
3654     player->index_bit = (1 << i);
3655     player->element_nr = EL_PLAYER_1 + i;
3656
3657     player->present = FALSE;
3658     player->active = FALSE;
3659     player->mapped = FALSE;
3660
3661     player->killed = FALSE;
3662     player->reanimated = FALSE;
3663     player->buried = FALSE;
3664
3665     player->action = 0;
3666     player->effective_action = 0;
3667     player->programmed_action = 0;
3668     player->snap_action = 0;
3669
3670     player->mouse_action.lx = 0;
3671     player->mouse_action.ly = 0;
3672     player->mouse_action.button = 0;
3673     player->mouse_action.button_hint = 0;
3674
3675     player->effective_mouse_action.lx = 0;
3676     player->effective_mouse_action.ly = 0;
3677     player->effective_mouse_action.button = 0;
3678     player->effective_mouse_action.button_hint = 0;
3679
3680     for (j = 0; j < MAX_NUM_KEYS; j++)
3681       player->key[j] = FALSE;
3682
3683     player->num_white_keys = 0;
3684
3685     player->dynabomb_count = 0;
3686     player->dynabomb_size = 1;
3687     player->dynabombs_left = 0;
3688     player->dynabomb_xl = FALSE;
3689
3690     player->MovDir = initial_move_dir;
3691     player->MovPos = 0;
3692     player->GfxPos = 0;
3693     player->GfxDir = initial_move_dir;
3694     player->GfxAction = ACTION_DEFAULT;
3695     player->Frame = 0;
3696     player->StepFrame = 0;
3697
3698     player->initial_element = player->element_nr;
3699     player->artwork_element =
3700       (level.use_artwork_element[i] ? level.artwork_element[i] :
3701        player->element_nr);
3702     player->use_murphy = FALSE;
3703
3704     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3705     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3706
3707     player->gravity = level.initial_player_gravity[i];
3708
3709     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3710
3711     player->actual_frame_counter.count = 0;
3712     player->actual_frame_counter.value = 1;
3713
3714     player->step_counter = 0;
3715
3716     player->last_move_dir = initial_move_dir;
3717
3718     player->is_active = FALSE;
3719
3720     player->is_waiting = FALSE;
3721     player->is_moving = FALSE;
3722     player->is_auto_moving = FALSE;
3723     player->is_digging = FALSE;
3724     player->is_snapping = FALSE;
3725     player->is_collecting = FALSE;
3726     player->is_pushing = FALSE;
3727     player->is_switching = FALSE;
3728     player->is_dropping = FALSE;
3729     player->is_dropping_pressed = FALSE;
3730
3731     player->is_bored = FALSE;
3732     player->is_sleeping = FALSE;
3733
3734     player->was_waiting = TRUE;
3735     player->was_moving = FALSE;
3736     player->was_snapping = FALSE;
3737     player->was_dropping = FALSE;
3738
3739     player->force_dropping = FALSE;
3740
3741     player->frame_counter_bored = -1;
3742     player->frame_counter_sleeping = -1;
3743
3744     player->anim_delay_counter = 0;
3745     player->post_delay_counter = 0;
3746
3747     player->dir_waiting = initial_move_dir;
3748     player->action_waiting = ACTION_DEFAULT;
3749     player->last_action_waiting = ACTION_DEFAULT;
3750     player->special_action_bored = ACTION_DEFAULT;
3751     player->special_action_sleeping = ACTION_DEFAULT;
3752
3753     player->switch_x = -1;
3754     player->switch_y = -1;
3755
3756     player->drop_x = -1;
3757     player->drop_y = -1;
3758
3759     player->show_envelope = 0;
3760
3761     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3762
3763     player->push_delay       = -1;      // initialized when pushing starts
3764     player->push_delay_value = game.initial_push_delay_value;
3765
3766     player->drop_delay = 0;
3767     player->drop_pressed_delay = 0;
3768
3769     player->last_jx = -1;
3770     player->last_jy = -1;
3771     player->jx = -1;
3772     player->jy = -1;
3773
3774     player->shield_normal_time_left = 0;
3775     player->shield_deadly_time_left = 0;
3776
3777     player->last_removed_element = EL_UNDEFINED;
3778
3779     player->inventory_infinite_element = EL_UNDEFINED;
3780     player->inventory_size = 0;
3781
3782     if (level.use_initial_inventory[i])
3783     {
3784       for (j = 0; j < level.initial_inventory_size[i]; j++)
3785       {
3786         int element = level.initial_inventory_content[i][j];
3787         int collect_count = element_info[element].collect_count_initial;
3788         int k;
3789
3790         if (!IS_CUSTOM_ELEMENT(element))
3791           collect_count = 1;
3792
3793         if (collect_count == 0)
3794           player->inventory_infinite_element = element;
3795         else
3796           for (k = 0; k < collect_count; k++)
3797             if (player->inventory_size < MAX_INVENTORY_SIZE)
3798               player->inventory_element[player->inventory_size++] = element;
3799       }
3800     }
3801
3802     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3803     SnapField(player, 0, 0);
3804
3805     map_player_action[i] = i;
3806   }
3807
3808   network_player_action_received = FALSE;
3809
3810   // initial null action
3811   if (network_playing)
3812     SendToServer_MovePlayer(MV_NONE);
3813
3814   FrameCounter = 0;
3815   TimeFrames = 0;
3816   TimePlayed = 0;
3817   TimeLeft = level.time;
3818   TapeTime = 0;
3819
3820   ScreenMovDir = MV_NONE;
3821   ScreenMovPos = 0;
3822   ScreenGfxPos = 0;
3823
3824   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3825
3826   game.robot_wheel_x = -1;
3827   game.robot_wheel_y = -1;
3828
3829   game.exit_x = -1;
3830   game.exit_y = -1;
3831
3832   game.all_players_gone = FALSE;
3833
3834   game.LevelSolved = FALSE;
3835   game.GameOver = FALSE;
3836
3837   game.GamePlayed = !tape.playing;
3838
3839   game.LevelSolved_GameWon = FALSE;
3840   game.LevelSolved_GameEnd = FALSE;
3841   game.LevelSolved_SaveTape = FALSE;
3842   game.LevelSolved_SaveScore = FALSE;
3843
3844   game.LevelSolved_CountingTime = 0;
3845   game.LevelSolved_CountingScore = 0;
3846   game.LevelSolved_CountingHealth = 0;
3847
3848   game.panel.active = TRUE;
3849
3850   game.no_level_time_limit = (level.time == 0);
3851   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3852
3853   game.yamyam_content_nr = 0;
3854   game.robot_wheel_active = FALSE;
3855   game.magic_wall_active = FALSE;
3856   game.magic_wall_time_left = 0;
3857   game.light_time_left = 0;
3858   game.timegate_time_left = 0;
3859   game.switchgate_pos = 0;
3860   game.wind_direction = level.wind_direction_initial;
3861
3862   game.time_final = 0;
3863   game.score_time_final = 0;
3864
3865   game.score = 0;
3866   game.score_final = 0;
3867
3868   game.health = MAX_HEALTH;
3869   game.health_final = MAX_HEALTH;
3870
3871   game.gems_still_needed = level.gems_needed;
3872   game.sokoban_fields_still_needed = 0;
3873   game.sokoban_objects_still_needed = 0;
3874   game.lights_still_needed = 0;
3875   game.players_still_needed = 0;
3876   game.friends_still_needed = 0;
3877
3878   game.lenses_time_left = 0;
3879   game.magnify_time_left = 0;
3880
3881   game.ball_active = level.ball_active_initial;
3882   game.ball_content_nr = 0;
3883
3884   game.explosions_delayed = TRUE;
3885
3886   game.envelope_active = FALSE;
3887
3888   // special case: set custom artwork setting to initial value
3889   game.use_masked_elements = game.use_masked_elements_initial;
3890
3891   for (i = 0; i < NUM_BELTS; i++)
3892   {
3893     game.belt_dir[i] = MV_NONE;
3894     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3895   }
3896
3897   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3898     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3899
3900 #if DEBUG_INIT_PLAYER
3901   DebugPrintPlayerStatus("Player status at level initialization");
3902 #endif
3903
3904   SCAN_PLAYFIELD(x, y)
3905   {
3906     Tile[x][y] = Last[x][y] = level.field[x][y];
3907     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3908     ChangeDelay[x][y] = 0;
3909     ChangePage[x][y] = -1;
3910     CustomValue[x][y] = 0;              // initialized in InitField()
3911     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3912     AmoebaNr[x][y] = 0;
3913     WasJustMoving[x][y] = 0;
3914     WasJustFalling[x][y] = 0;
3915     CheckCollision[x][y] = 0;
3916     CheckImpact[x][y] = 0;
3917     Stop[x][y] = FALSE;
3918     Pushed[x][y] = FALSE;
3919
3920     ChangeCount[x][y] = 0;
3921     ChangeEvent[x][y] = -1;
3922
3923     ExplodePhase[x][y] = 0;
3924     ExplodeDelay[x][y] = 0;
3925     ExplodeField[x][y] = EX_TYPE_NONE;
3926
3927     RunnerVisit[x][y] = 0;
3928     PlayerVisit[x][y] = 0;
3929
3930     GfxFrame[x][y] = 0;
3931     GfxRandom[x][y] = INIT_GFX_RANDOM();
3932     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3933     GfxElement[x][y] = EL_UNDEFINED;
3934     GfxElementEmpty[x][y] = EL_EMPTY;
3935     GfxAction[x][y] = ACTION_DEFAULT;
3936     GfxDir[x][y] = MV_NONE;
3937     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3938   }
3939
3940   SCAN_PLAYFIELD(x, y)
3941   {
3942     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3943       emulate_bd = FALSE;
3944     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3945       emulate_sp = FALSE;
3946
3947     InitField(x, y, TRUE);
3948
3949     ResetGfxAnimation(x, y);
3950   }
3951
3952   InitBeltMovement();
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955   {
3956     struct PlayerInfo *player = &stored_player[i];
3957
3958     // set number of special actions for bored and sleeping animation
3959     player->num_special_action_bored =
3960       get_num_special_action(player->artwork_element,
3961                              ACTION_BORING_1, ACTION_BORING_LAST);
3962     player->num_special_action_sleeping =
3963       get_num_special_action(player->artwork_element,
3964                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3965   }
3966
3967   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3968                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3969
3970   // initialize type of slippery elements
3971   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3972   {
3973     if (!IS_CUSTOM_ELEMENT(i))
3974     {
3975       // default: elements slip down either to the left or right randomly
3976       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3977
3978       // SP style elements prefer to slip down on the left side
3979       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3980         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3981
3982       // BD style elements prefer to slip down on the left side
3983       if (game.emulation == EMU_BOULDERDASH)
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985     }
3986   }
3987
3988   // initialize explosion and ignition delay
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       int num_phase = 8;
3994       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3995                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3996                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3997       int last_phase = (num_phase + 1) * delay;
3998       int half_phase = (num_phase / 2) * delay;
3999
4000       element_info[i].explosion_delay = last_phase - 1;
4001       element_info[i].ignition_delay = half_phase;
4002
4003       if (i == EL_BLACK_ORB)
4004         element_info[i].ignition_delay = 1;
4005     }
4006   }
4007
4008   // correct non-moving belts to start moving left
4009   for (i = 0; i < NUM_BELTS; i++)
4010     if (game.belt_dir[i] == MV_NONE)
4011       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4012
4013 #if USE_NEW_PLAYER_ASSIGNMENTS
4014   // use preferred player also in local single-player mode
4015   if (!network.enabled && !game.team_mode)
4016   {
4017     int new_index_nr = setup.network_player_nr;
4018
4019     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4020     {
4021       for (i = 0; i < MAX_PLAYERS; i++)
4022         stored_player[i].connected_locally = FALSE;
4023
4024       stored_player[new_index_nr].connected_locally = TRUE;
4025     }
4026   }
4027
4028   for (i = 0; i < MAX_PLAYERS; i++)
4029   {
4030     stored_player[i].connected = FALSE;
4031
4032     // in network game mode, the local player might not be the first player
4033     if (stored_player[i].connected_locally)
4034       local_player = &stored_player[i];
4035   }
4036
4037   if (!network.enabled)
4038     local_player->connected = TRUE;
4039
4040   if (tape.playing)
4041   {
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043       stored_player[i].connected = tape.player_participates[i];
4044   }
4045   else if (network.enabled)
4046   {
4047     // add team mode players connected over the network (needed for correct
4048     // assignment of player figures from level to locally playing players)
4049
4050     for (i = 0; i < MAX_PLAYERS; i++)
4051       if (stored_player[i].connected_network)
4052         stored_player[i].connected = TRUE;
4053   }
4054   else if (game.team_mode)
4055   {
4056     // try to guess locally connected team mode players (needed for correct
4057     // assignment of player figures from level to locally playing players)
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060       if (setup.input[i].use_joystick ||
4061           setup.input[i].key.left != KSYM_UNDEFINED)
4062         stored_player[i].connected = TRUE;
4063   }
4064
4065 #if DEBUG_INIT_PLAYER
4066   DebugPrintPlayerStatus("Player status after level initialization");
4067 #endif
4068
4069 #if DEBUG_INIT_PLAYER
4070   Debug("game:init:player", "Reassigning players ...");
4071 #endif
4072
4073   // check if any connected player was not found in playfield
4074   for (i = 0; i < MAX_PLAYERS; i++)
4075   {
4076     struct PlayerInfo *player = &stored_player[i];
4077
4078     if (player->connected && !player->present)
4079     {
4080       struct PlayerInfo *field_player = NULL;
4081
4082 #if DEBUG_INIT_PLAYER
4083       Debug("game:init:player",
4084             "- looking for field player for player %d ...", i + 1);
4085 #endif
4086
4087       // assign first free player found that is present in the playfield
4088
4089       // first try: look for unmapped playfield player that is not connected
4090       for (j = 0; j < MAX_PLAYERS; j++)
4091         if (field_player == NULL &&
4092             stored_player[j].present &&
4093             !stored_player[j].mapped &&
4094             !stored_player[j].connected)
4095           field_player = &stored_player[j];
4096
4097       // second try: look for *any* unmapped playfield player
4098       for (j = 0; j < MAX_PLAYERS; j++)
4099         if (field_player == NULL &&
4100             stored_player[j].present &&
4101             !stored_player[j].mapped)
4102           field_player = &stored_player[j];
4103
4104       if (field_player != NULL)
4105       {
4106         int jx = field_player->jx, jy = field_player->jy;
4107
4108 #if DEBUG_INIT_PLAYER
4109         Debug("game:init:player", "- found player %d",
4110               field_player->index_nr + 1);
4111 #endif
4112
4113         player->present = FALSE;
4114         player->active = FALSE;
4115
4116         field_player->present = TRUE;
4117         field_player->active = TRUE;
4118
4119         /*
4120         player->initial_element = field_player->initial_element;
4121         player->artwork_element = field_player->artwork_element;
4122
4123         player->block_last_field       = field_player->block_last_field;
4124         player->block_delay_adjustment = field_player->block_delay_adjustment;
4125         */
4126
4127         StorePlayer[jx][jy] = field_player->element_nr;
4128
4129         field_player->jx = field_player->last_jx = jx;
4130         field_player->jy = field_player->last_jy = jy;
4131
4132         if (local_player == player)
4133           local_player = field_player;
4134
4135         map_player_action[field_player->index_nr] = i;
4136
4137         field_player->mapped = TRUE;
4138
4139 #if DEBUG_INIT_PLAYER
4140         Debug("game:init:player", "- map_player_action[%d] == %d",
4141               field_player->index_nr + 1, i + 1);
4142 #endif
4143       }
4144     }
4145
4146     if (player->connected && player->present)
4147       player->mapped = TRUE;
4148   }
4149
4150 #if DEBUG_INIT_PLAYER
4151   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4152 #endif
4153
4154 #else
4155
4156   // check if any connected player was not found in playfield
4157   for (i = 0; i < MAX_PLAYERS; i++)
4158   {
4159     struct PlayerInfo *player = &stored_player[i];
4160
4161     if (player->connected && !player->present)
4162     {
4163       for (j = 0; j < MAX_PLAYERS; j++)
4164       {
4165         struct PlayerInfo *field_player = &stored_player[j];
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168         // assign first free player found that is present in the playfield
4169         if (field_player->present && !field_player->connected)
4170         {
4171           player->present = TRUE;
4172           player->active = TRUE;
4173
4174           field_player->present = FALSE;
4175           field_player->active = FALSE;
4176
4177           player->initial_element = field_player->initial_element;
4178           player->artwork_element = field_player->artwork_element;
4179
4180           player->block_last_field       = field_player->block_last_field;
4181           player->block_delay_adjustment = field_player->block_delay_adjustment;
4182
4183           StorePlayer[jx][jy] = player->element_nr;
4184
4185           player->jx = player->last_jx = jx;
4186           player->jy = player->last_jy = jy;
4187
4188           break;
4189         }
4190       }
4191     }
4192   }
4193 #endif
4194
4195 #if 0
4196   Debug("game:init:player", "local_player->present == %d",
4197         local_player->present);
4198 #endif
4199
4200   // set focus to local player for network games, else to all players
4201   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4202   game.centered_player_nr_next = game.centered_player_nr;
4203   game.set_centered_player = FALSE;
4204   game.set_centered_player_wrap = FALSE;
4205
4206   if (network_playing && tape.recording)
4207   {
4208     // store client dependent player focus when recording network games
4209     tape.centered_player_nr_next = game.centered_player_nr_next;
4210     tape.set_centered_player = TRUE;
4211   }
4212
4213   if (tape.playing)
4214   {
4215     // when playing a tape, eliminate all players who do not participate
4216
4217 #if USE_NEW_PLAYER_ASSIGNMENTS
4218
4219     if (!game.team_mode)
4220     {
4221       for (i = 0; i < MAX_PLAYERS; i++)
4222       {
4223         if (stored_player[i].active &&
4224             !tape.player_participates[map_player_action[i]])
4225         {
4226           struct PlayerInfo *player = &stored_player[i];
4227           int jx = player->jx, jy = player->jy;
4228
4229 #if DEBUG_INIT_PLAYER
4230           Debug("game:init:player", "Removing player %d at (%d, %d)",
4231                 i + 1, jx, jy);
4232 #endif
4233
4234           player->active = FALSE;
4235           StorePlayer[jx][jy] = 0;
4236           Tile[jx][jy] = EL_EMPTY;
4237         }
4238       }
4239     }
4240
4241 #else
4242
4243     for (i = 0; i < MAX_PLAYERS; i++)
4244     {
4245       if (stored_player[i].active &&
4246           !tape.player_participates[i])
4247       {
4248         struct PlayerInfo *player = &stored_player[i];
4249         int jx = player->jx, jy = player->jy;
4250
4251         player->active = FALSE;
4252         StorePlayer[jx][jy] = 0;
4253         Tile[jx][jy] = EL_EMPTY;
4254       }
4255     }
4256 #endif
4257   }
4258   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4259   {
4260     // when in single player mode, eliminate all but the local player
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       struct PlayerInfo *player = &stored_player[i];
4265
4266       if (player->active && player != local_player)
4267       {
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         player->present = FALSE;
4272
4273         StorePlayer[jx][jy] = 0;
4274         Tile[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277   }
4278
4279   for (i = 0; i < MAX_PLAYERS; i++)
4280     if (stored_player[i].active)
4281       game.players_still_needed++;
4282
4283   if (level.solved_by_one_player)
4284     game.players_still_needed = 1;
4285
4286   // when recording the game, store which players take part in the game
4287   if (tape.recording)
4288   {
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291       if (stored_player[i].connected)
4292         tape.player_participates[i] = TRUE;
4293 #else
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].active)
4296         tape.player_participates[i] = TRUE;
4297 #endif
4298   }
4299
4300 #if DEBUG_INIT_PLAYER
4301   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4302 #endif
4303
4304   if (BorderElement == EL_EMPTY)
4305   {
4306     SBX_Left = 0;
4307     SBX_Right = lev_fieldx - SCR_FIELDX;
4308     SBY_Upper = 0;
4309     SBY_Lower = lev_fieldy - SCR_FIELDY;
4310   }
4311   else
4312   {
4313     SBX_Left = -1;
4314     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4315     SBY_Upper = -1;
4316     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4317   }
4318
4319   if (full_lev_fieldx <= SCR_FIELDX)
4320     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4321   if (full_lev_fieldy <= SCR_FIELDY)
4322     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4323
4324   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4325     SBX_Left--;
4326   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4327     SBY_Upper--;
4328
4329   // if local player not found, look for custom element that might create
4330   // the player (make some assumptions about the right custom element)
4331   if (!local_player->present)
4332   {
4333     int start_x = 0, start_y = 0;
4334     int found_rating = 0;
4335     int found_element = EL_UNDEFINED;
4336     int player_nr = local_player->index_nr;
4337
4338     SCAN_PLAYFIELD(x, y)
4339     {
4340       int element = Tile[x][y];
4341       int content;
4342       int xx, yy;
4343       boolean is_player;
4344
4345       if (level.use_start_element[player_nr] &&
4346           level.start_element[player_nr] == element &&
4347           found_rating < 4)
4348       {
4349         start_x = x;
4350         start_y = y;
4351
4352         found_rating = 4;
4353         found_element = element;
4354       }
4355
4356       if (!IS_CUSTOM_ELEMENT(element))
4357         continue;
4358
4359       if (CAN_CHANGE(element))
4360       {
4361         for (i = 0; i < element_info[element].num_change_pages; i++)
4362         {
4363           // check for player created from custom element as single target
4364           content = element_info[element].change_page[i].target_element;
4365           is_player = IS_PLAYER_ELEMENT(content);
4366
4367           if (is_player && (found_rating < 3 ||
4368                             (found_rating == 3 && element < found_element)))
4369           {
4370             start_x = x;
4371             start_y = y;
4372
4373             found_rating = 3;
4374             found_element = element;
4375           }
4376         }
4377       }
4378
4379       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4380       {
4381         // check for player created from custom element as explosion content
4382         content = element_info[element].content.e[xx][yy];
4383         is_player = IS_PLAYER_ELEMENT(content);
4384
4385         if (is_player && (found_rating < 2 ||
4386                           (found_rating == 2 && element < found_element)))
4387         {
4388           start_x = x + xx - 1;
4389           start_y = y + yy - 1;
4390
4391           found_rating = 2;
4392           found_element = element;
4393         }
4394
4395         if (!CAN_CHANGE(element))
4396           continue;
4397
4398         for (i = 0; i < element_info[element].num_change_pages; i++)
4399         {
4400           // check for player created from custom element as extended target
4401           content =
4402             element_info[element].change_page[i].target_content.e[xx][yy];
4403
4404           is_player = IS_PLAYER_ELEMENT(content);
4405
4406           if (is_player && (found_rating < 1 ||
4407                             (found_rating == 1 && element < found_element)))
4408           {
4409             start_x = x + xx - 1;
4410             start_y = y + yy - 1;
4411
4412             found_rating = 1;
4413             found_element = element;
4414           }
4415         }
4416       }
4417     }
4418
4419     scroll_x = SCROLL_POSITION_X(start_x);
4420     scroll_y = SCROLL_POSITION_Y(start_y);
4421   }
4422   else
4423   {
4424     scroll_x = SCROLL_POSITION_X(local_player->jx);
4425     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4426   }
4427
4428   // !!! FIX THIS (START) !!!
4429   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4430   {
4431     InitGameEngine_EM();
4432   }
4433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4434   {
4435     InitGameEngine_SP();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4438   {
4439     InitGameEngine_MM();
4440   }
4441   else
4442   {
4443     DrawLevel(REDRAW_FIELD);
4444     DrawAllPlayers();
4445
4446     // after drawing the level, correct some elements
4447     if (game.timegate_time_left == 0)
4448       CloseAllOpenTimegates();
4449   }
4450
4451   // blit playfield from scroll buffer to normal back buffer for fading in
4452   BlitScreenToBitmap(backbuffer);
4453   // !!! FIX THIS (END) !!!
4454
4455   DrawMaskedBorder(fade_mask);
4456
4457   FadeIn(fade_mask);
4458
4459 #if 1
4460   // full screen redraw is required at this point in the following cases:
4461   // - special editor door undrawn when game was started from level editor
4462   // - drawing area (playfield) was changed and has to be removed completely
4463   redraw_mask = REDRAW_ALL;
4464   BackToFront();
4465 #endif
4466
4467   if (!game.restart_level)
4468   {
4469     // copy default game door content to main double buffer
4470
4471     // !!! CHECK AGAIN !!!
4472     SetPanelBackground();
4473     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4474     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4475   }
4476
4477   SetPanelBackground();
4478   SetDrawBackgroundMask(REDRAW_DOOR_1);
4479
4480   UpdateAndDisplayGameControlValues();
4481
4482   if (!game.restart_level)
4483   {
4484     UnmapGameButtons();
4485     UnmapTapeButtons();
4486
4487     FreeGameButtons();
4488     CreateGameButtons();
4489
4490     MapGameButtons();
4491     MapTapeButtons();
4492
4493     // copy actual game door content to door double buffer for OpenDoor()
4494     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4495
4496     OpenDoor(DOOR_OPEN_ALL);
4497
4498     KeyboardAutoRepeatOffUnlessAutoplay();
4499
4500 #if DEBUG_INIT_PLAYER
4501     DebugPrintPlayerStatus("Player status (final)");
4502 #endif
4503   }
4504
4505   UnmapAllGadgets();
4506
4507   MapGameButtons();
4508   MapTapeButtons();
4509
4510   if (!game.restart_level && !tape.playing)
4511   {
4512     LevelStats_incPlayed(level_nr);
4513
4514     SaveLevelSetup_SeriesInfo();
4515   }
4516
4517   game.restart_level = FALSE;
4518
4519   game.request_active = FALSE;
4520   game.request_active_or_moving = FALSE;
4521
4522   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4523     InitGameActions_MM();
4524
4525   SaveEngineSnapshotToListInitial();
4526
4527   if (!game.restart_level)
4528   {
4529     PlaySound(SND_GAME_STARTING);
4530
4531     if (setup.sound_music)
4532       PlayLevelMusic();
4533   }
4534
4535   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4536 }
4537
4538 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4539                         int actual_player_x, int actual_player_y)
4540 {
4541   // this is used for non-R'n'D game engines to update certain engine values
4542
4543   // needed to determine if sounds are played within the visible screen area
4544   scroll_x = actual_scroll_x;
4545   scroll_y = actual_scroll_y;
4546
4547   // needed to get player position for "follow finger" playing input method
4548   local_player->jx = actual_player_x;
4549   local_player->jy = actual_player_y;
4550 }
4551
4552 void InitMovDir(int x, int y)
4553 {
4554   int i, element = Tile[x][y];
4555   static int xy[4][2] =
4556   {
4557     {  0, +1 },
4558     { +1,  0 },
4559     {  0, -1 },
4560     { -1,  0 }
4561   };
4562   static int direction[3][4] =
4563   {
4564     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4565     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4566     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4567   };
4568
4569   switch (element)
4570   {
4571     case EL_BUG_RIGHT:
4572     case EL_BUG_UP:
4573     case EL_BUG_LEFT:
4574     case EL_BUG_DOWN:
4575       Tile[x][y] = EL_BUG;
4576       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4577       break;
4578
4579     case EL_SPACESHIP_RIGHT:
4580     case EL_SPACESHIP_UP:
4581     case EL_SPACESHIP_LEFT:
4582     case EL_SPACESHIP_DOWN:
4583       Tile[x][y] = EL_SPACESHIP;
4584       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4585       break;
4586
4587     case EL_BD_BUTTERFLY_RIGHT:
4588     case EL_BD_BUTTERFLY_UP:
4589     case EL_BD_BUTTERFLY_LEFT:
4590     case EL_BD_BUTTERFLY_DOWN:
4591       Tile[x][y] = EL_BD_BUTTERFLY;
4592       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4593       break;
4594
4595     case EL_BD_FIREFLY_RIGHT:
4596     case EL_BD_FIREFLY_UP:
4597     case EL_BD_FIREFLY_LEFT:
4598     case EL_BD_FIREFLY_DOWN:
4599       Tile[x][y] = EL_BD_FIREFLY;
4600       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4601       break;
4602
4603     case EL_PACMAN_RIGHT:
4604     case EL_PACMAN_UP:
4605     case EL_PACMAN_LEFT:
4606     case EL_PACMAN_DOWN:
4607       Tile[x][y] = EL_PACMAN;
4608       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4609       break;
4610
4611     case EL_YAMYAM_LEFT:
4612     case EL_YAMYAM_RIGHT:
4613     case EL_YAMYAM_UP:
4614     case EL_YAMYAM_DOWN:
4615       Tile[x][y] = EL_YAMYAM;
4616       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4617       break;
4618
4619     case EL_SP_SNIKSNAK:
4620       MovDir[x][y] = MV_UP;
4621       break;
4622
4623     case EL_SP_ELECTRON:
4624       MovDir[x][y] = MV_LEFT;
4625       break;
4626
4627     case EL_MOLE_LEFT:
4628     case EL_MOLE_RIGHT:
4629     case EL_MOLE_UP:
4630     case EL_MOLE_DOWN:
4631       Tile[x][y] = EL_MOLE;
4632       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4633       break;
4634
4635     case EL_SPRING_LEFT:
4636     case EL_SPRING_RIGHT:
4637       Tile[x][y] = EL_SPRING;
4638       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4639       break;
4640
4641     default:
4642       if (IS_CUSTOM_ELEMENT(element))
4643       {
4644         struct ElementInfo *ei = &element_info[element];
4645         int move_direction_initial = ei->move_direction_initial;
4646         int move_pattern = ei->move_pattern;
4647
4648         if (move_direction_initial == MV_START_PREVIOUS)
4649         {
4650           if (MovDir[x][y] != MV_NONE)
4651             return;
4652
4653           move_direction_initial = MV_START_AUTOMATIC;
4654         }
4655
4656         if (move_direction_initial == MV_START_RANDOM)
4657           MovDir[x][y] = 1 << RND(4);
4658         else if (move_direction_initial & MV_ANY_DIRECTION)
4659           MovDir[x][y] = move_direction_initial;
4660         else if (move_pattern == MV_ALL_DIRECTIONS ||
4661                  move_pattern == MV_TURNING_LEFT ||
4662                  move_pattern == MV_TURNING_RIGHT ||
4663                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4664                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4665                  move_pattern == MV_TURNING_RANDOM)
4666           MovDir[x][y] = 1 << RND(4);
4667         else if (move_pattern == MV_HORIZONTAL)
4668           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4669         else if (move_pattern == MV_VERTICAL)
4670           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4671         else if (move_pattern & MV_ANY_DIRECTION)
4672           MovDir[x][y] = element_info[element].move_pattern;
4673         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4674                  move_pattern == MV_ALONG_RIGHT_SIDE)
4675         {
4676           // use random direction as default start direction
4677           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4678             MovDir[x][y] = 1 << RND(4);
4679
4680           for (i = 0; i < NUM_DIRECTIONS; i++)
4681           {
4682             int x1 = x + xy[i][0];
4683             int y1 = y + xy[i][1];
4684
4685             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4686             {
4687               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4688                 MovDir[x][y] = direction[0][i];
4689               else
4690                 MovDir[x][y] = direction[1][i];
4691
4692               break;
4693             }
4694           }
4695         }                
4696       }
4697       else
4698       {
4699         MovDir[x][y] = 1 << RND(4);
4700
4701         if (element != EL_BUG &&
4702             element != EL_SPACESHIP &&
4703             element != EL_BD_BUTTERFLY &&
4704             element != EL_BD_FIREFLY)
4705           break;
4706
4707         for (i = 0; i < NUM_DIRECTIONS; i++)
4708         {
4709           int x1 = x + xy[i][0];
4710           int y1 = y + xy[i][1];
4711
4712           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4713           {
4714             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4715             {
4716               MovDir[x][y] = direction[0][i];
4717               break;
4718             }
4719             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4720                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4721             {
4722               MovDir[x][y] = direction[1][i];
4723               break;
4724             }
4725           }
4726         }
4727       }
4728       break;
4729   }
4730
4731   GfxDir[x][y] = MovDir[x][y];
4732 }
4733
4734 void InitAmoebaNr(int x, int y)
4735 {
4736   int i;
4737   int group_nr = AmoebaNeighbourNr(x, y);
4738
4739   if (group_nr == 0)
4740   {
4741     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4742     {
4743       if (AmoebaCnt[i] == 0)
4744       {
4745         group_nr = i;
4746         break;
4747       }
4748     }
4749   }
4750
4751   AmoebaNr[x][y] = group_nr;
4752   AmoebaCnt[group_nr]++;
4753   AmoebaCnt2[group_nr]++;
4754 }
4755
4756 static void LevelSolved_SetFinalGameValues(void)
4757 {
4758   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4759   game.score_time_final = (level.use_step_counter ? TimePlayed :
4760                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4761
4762   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4763                       game_em.lev->score :
4764                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4765                       game_mm.score :
4766                       game.score);
4767
4768   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4769                        MM_HEALTH(game_mm.laser_overload_value) :
4770                        game.health);
4771
4772   game.LevelSolved_CountingTime = game.time_final;
4773   game.LevelSolved_CountingScore = game.score_final;
4774   game.LevelSolved_CountingHealth = game.health_final;
4775 }
4776
4777 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4778 {
4779   game.LevelSolved_CountingTime = time;
4780   game.LevelSolved_CountingScore = score;
4781   game.LevelSolved_CountingHealth = health;
4782
4783   game_panel_controls[GAME_PANEL_TIME].value = time;
4784   game_panel_controls[GAME_PANEL_SCORE].value = score;
4785   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4786
4787   DisplayGameControlValues();
4788 }
4789
4790 static void LevelSolved(void)
4791 {
4792   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4793       game.players_still_needed > 0)
4794     return;
4795
4796   game.LevelSolved = TRUE;
4797   game.GameOver = TRUE;
4798
4799   tape.solved = TRUE;
4800
4801   // needed here to display correct panel values while player walks into exit
4802   LevelSolved_SetFinalGameValues();
4803 }
4804
4805 void GameWon(void)
4806 {
4807   static int time_count_steps;
4808   static int time, time_final;
4809   static float score, score_final; // needed for time score < 10 for 10 seconds
4810   static int health, health_final;
4811   static int game_over_delay_1 = 0;
4812   static int game_over_delay_2 = 0;
4813   static int game_over_delay_3 = 0;
4814   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4815   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4816
4817   if (!game.LevelSolved_GameWon)
4818   {
4819     int i;
4820
4821     // do not start end game actions before the player stops moving (to exit)
4822     if (local_player->active && local_player->MovPos)
4823       return;
4824
4825     // calculate final game values after player finished walking into exit
4826     LevelSolved_SetFinalGameValues();
4827
4828     game.LevelSolved_GameWon = TRUE;
4829     game.LevelSolved_SaveTape = tape.recording;
4830     game.LevelSolved_SaveScore = !tape.playing;
4831
4832     if (!tape.playing)
4833     {
4834       LevelStats_incSolved(level_nr);
4835
4836       SaveLevelSetup_SeriesInfo();
4837     }
4838
4839     if (tape.auto_play)         // tape might already be stopped here
4840       tape.auto_play_level_solved = TRUE;
4841
4842     TapeStop();
4843
4844     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4845     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4846     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4847
4848     time = time_final = game.time_final;
4849     score = score_final = game.score_final;
4850     health = health_final = game.health_final;
4851
4852     // update game panel values before (delayed) counting of score (if any)
4853     LevelSolved_DisplayFinalGameValues(time, score, health);
4854
4855     // if level has time score defined, calculate new final game values
4856     if (time_score > 0)
4857     {
4858       int time_final_max = 999;
4859       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4860       int time_frames = 0;
4861       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4862       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4863
4864       if (TimeLeft > 0)
4865       {
4866         time_final = 0;
4867         time_frames = time_frames_left;
4868       }
4869       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4870       {
4871         time_final = time_final_max;
4872         time_frames = time_frames_final_max - time_frames_played;
4873       }
4874
4875       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4876
4877       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4878
4879       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4880       {
4881         health_final = 0;
4882         score_final += health * time_score;
4883       }
4884
4885       game.score_final = score_final;
4886       game.health_final = health_final;
4887     }
4888
4889     // if not counting score after game, immediately update game panel values
4890     if (level_editor_test_game || !setup.count_score_after_game)
4891     {
4892       time = time_final;
4893       score = score_final;
4894
4895       LevelSolved_DisplayFinalGameValues(time, score, health);
4896     }
4897
4898     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4899     {
4900       // check if last player has left the level
4901       if (game.exit_x >= 0 &&
4902           game.exit_y >= 0)
4903       {
4904         int x = game.exit_x;
4905         int y = game.exit_y;
4906         int element = Tile[x][y];
4907
4908         // close exit door after last player
4909         if ((game.all_players_gone &&
4910              (element == EL_EXIT_OPEN ||
4911               element == EL_SP_EXIT_OPEN ||
4912               element == EL_STEEL_EXIT_OPEN)) ||
4913             element == EL_EM_EXIT_OPEN ||
4914             element == EL_EM_STEEL_EXIT_OPEN)
4915         {
4916
4917           Tile[x][y] =
4918             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4919              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4920              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4921              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4922              EL_EM_STEEL_EXIT_CLOSING);
4923
4924           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4925         }
4926
4927         // player disappears
4928         DrawLevelField(x, y);
4929       }
4930
4931       for (i = 0; i < MAX_PLAYERS; i++)
4932       {
4933         struct PlayerInfo *player = &stored_player[i];
4934
4935         if (player->present)
4936         {
4937           RemovePlayer(player);
4938
4939           // player disappears
4940           DrawLevelField(player->jx, player->jy);
4941         }
4942       }
4943     }
4944
4945     PlaySound(SND_GAME_WINNING);
4946   }
4947
4948   if (setup.count_score_after_game)
4949   {
4950     if (time != time_final)
4951     {
4952       if (game_over_delay_1 > 0)
4953       {
4954         game_over_delay_1--;
4955
4956         return;
4957       }
4958
4959       int time_to_go = ABS(time_final - time);
4960       int time_count_dir = (time < time_final ? +1 : -1);
4961
4962       if (time_to_go < time_count_steps)
4963         time_count_steps = 1;
4964
4965       time  += time_count_steps * time_count_dir;
4966       score += time_count_steps * time_score;
4967
4968       // set final score to correct rounding differences after counting score
4969       if (time == time_final)
4970         score = score_final;
4971
4972       LevelSolved_DisplayFinalGameValues(time, score, health);
4973
4974       if (time == time_final)
4975         StopSound(SND_GAME_LEVELTIME_BONUS);
4976       else if (setup.sound_loops)
4977         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4978       else
4979         PlaySound(SND_GAME_LEVELTIME_BONUS);
4980
4981       return;
4982     }
4983
4984     if (health != health_final)
4985     {
4986       if (game_over_delay_2 > 0)
4987       {
4988         game_over_delay_2--;
4989
4990         return;
4991       }
4992
4993       int health_count_dir = (health < health_final ? +1 : -1);
4994
4995       health += health_count_dir;
4996       score  += time_score;
4997
4998       LevelSolved_DisplayFinalGameValues(time, score, health);
4999
5000       if (health == health_final)
5001         StopSound(SND_GAME_LEVELTIME_BONUS);
5002       else if (setup.sound_loops)
5003         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5004       else
5005         PlaySound(SND_GAME_LEVELTIME_BONUS);
5006
5007       return;
5008     }
5009   }
5010
5011   game.panel.active = FALSE;
5012
5013   if (game_over_delay_3 > 0)
5014   {
5015     game_over_delay_3--;
5016
5017     return;
5018   }
5019
5020   GameEnd();
5021 }
5022
5023 void GameEnd(void)
5024 {
5025   // used instead of "level_nr" (needed for network games)
5026   int last_level_nr = levelset.level_nr;
5027   boolean tape_saved = FALSE;
5028
5029   game.LevelSolved_GameEnd = TRUE;
5030
5031   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5032   {
5033     // make sure that request dialog to save tape does not open door again
5034     if (!global.use_envelope_request)
5035       CloseDoor(DOOR_CLOSE_1);
5036
5037     // ask to save tape
5038     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5039
5040     // set unique basename for score tape (also saved in high score table)
5041     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5042   }
5043
5044   // if no tape is to be saved, close both doors simultaneously
5045   CloseDoor(DOOR_CLOSE_ALL);
5046
5047   if (level_editor_test_game || score_info_tape_play)
5048   {
5049     SetGameStatus(GAME_MODE_MAIN);
5050
5051     DrawMainMenu();
5052
5053     return;
5054   }
5055
5056   if (!game.LevelSolved_SaveScore)
5057   {
5058     SetGameStatus(GAME_MODE_MAIN);
5059
5060     DrawMainMenu();
5061
5062     return;
5063   }
5064
5065   if (level_nr == leveldir_current->handicap_level)
5066   {
5067     leveldir_current->handicap_level++;
5068
5069     SaveLevelSetup_SeriesInfo();
5070   }
5071
5072   // save score and score tape before potentially erasing tape below
5073   NewHighScore(last_level_nr, tape_saved);
5074
5075   if (setup.increment_levels &&
5076       level_nr < leveldir_current->last_level &&
5077       !network_playing)
5078   {
5079     level_nr++;         // advance to next level
5080     TapeErase();        // start with empty tape
5081
5082     if (setup.auto_play_next_level)
5083     {
5084       scores.continue_playing = TRUE;
5085       scores.next_level_nr = level_nr;
5086
5087       LoadLevel(level_nr);
5088
5089       SaveLevelSetup_SeriesInfo();
5090     }
5091   }
5092
5093   if (scores.last_added >= 0 && setup.show_scores_after_game)
5094   {
5095     SetGameStatus(GAME_MODE_SCORES);
5096
5097     DrawHallOfFame(last_level_nr);
5098   }
5099   else if (scores.continue_playing)
5100   {
5101     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5102   }
5103   else
5104   {
5105     SetGameStatus(GAME_MODE_MAIN);
5106
5107     DrawMainMenu();
5108   }
5109 }
5110
5111 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5112                          boolean one_score_entry_per_name)
5113 {
5114   int i;
5115
5116   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5117     return -1;
5118
5119   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5120   {
5121     struct ScoreEntry *entry = &list->entry[i];
5122     boolean score_is_better = (new_entry->score >  entry->score);
5123     boolean score_is_equal  = (new_entry->score == entry->score);
5124     boolean time_is_better  = (new_entry->time  <  entry->time);
5125     boolean time_is_equal   = (new_entry->time  == entry->time);
5126     boolean better_by_score = (score_is_better ||
5127                                (score_is_equal && time_is_better));
5128     boolean better_by_time  = (time_is_better ||
5129                                (time_is_equal && score_is_better));
5130     boolean is_better = (level.rate_time_over_score ? better_by_time :
5131                          better_by_score);
5132     boolean entry_is_empty = (entry->score == 0 &&
5133                               entry->time == 0);
5134
5135     // prevent adding server score entries if also existing in local score file
5136     // (special case: historic score entries have an empty tape basename entry)
5137     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5138         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5139     {
5140       // add fields from server score entry not stored in local score entry
5141       // (currently, this means setting platform, version and country fields;
5142       // in rare cases, this may also correct an invalid score value, as
5143       // historic scores might have been truncated to 16-bit values locally)
5144       *entry = *new_entry;
5145
5146       return -1;
5147     }
5148
5149     if (is_better || entry_is_empty)
5150     {
5151       // player has made it to the hall of fame
5152
5153       if (i < MAX_SCORE_ENTRIES - 1)
5154       {
5155         int m = MAX_SCORE_ENTRIES - 1;
5156         int l;
5157
5158         if (one_score_entry_per_name)
5159         {
5160           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5161             if (strEqual(list->entry[l].name, new_entry->name))
5162               m = l;
5163
5164           if (m == i)   // player's new highscore overwrites his old one
5165             goto put_into_list;
5166         }
5167
5168         for (l = m; l > i; l--)
5169           list->entry[l] = list->entry[l - 1];
5170       }
5171
5172       put_into_list:
5173
5174       *entry = *new_entry;
5175
5176       return i;
5177     }
5178     else if (one_score_entry_per_name &&
5179              strEqual(entry->name, new_entry->name))
5180     {
5181       // player already in high score list with better score or time
5182
5183       return -1;
5184     }
5185   }
5186
5187   // special case: new score is beyond the last high score list position
5188   return MAX_SCORE_ENTRIES;
5189 }
5190
5191 void NewHighScore(int level_nr, boolean tape_saved)
5192 {
5193   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5194   boolean one_per_name = FALSE;
5195
5196   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5197   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5198
5199   new_entry.score = game.score_final;
5200   new_entry.time = game.score_time_final;
5201
5202   LoadScore(level_nr);
5203
5204   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5205
5206   if (scores.last_added >= MAX_SCORE_ENTRIES)
5207   {
5208     scores.last_added = MAX_SCORE_ENTRIES - 1;
5209     scores.force_last_added = TRUE;
5210
5211     scores.entry[scores.last_added] = new_entry;
5212
5213     // store last added local score entry (before merging server scores)
5214     scores.last_added_local = scores.last_added;
5215
5216     return;
5217   }
5218
5219   if (scores.last_added < 0)
5220     return;
5221
5222   SaveScore(level_nr);
5223
5224   // store last added local score entry (before merging server scores)
5225   scores.last_added_local = scores.last_added;
5226
5227   if (!game.LevelSolved_SaveTape)
5228     return;
5229
5230   SaveScoreTape(level_nr);
5231
5232   if (setup.ask_for_using_api_server)
5233   {
5234     setup.use_api_server =
5235       Request("Upload your score and tape to the high score server?", REQ_ASK);
5236
5237     if (!setup.use_api_server)
5238       Request("Not using high score server! Use setup menu to enable again!",
5239               REQ_CONFIRM);
5240
5241     runtime.use_api_server = setup.use_api_server;
5242
5243     // after asking for using API server once, do not ask again
5244     setup.ask_for_using_api_server = FALSE;
5245
5246     SaveSetup_ServerSetup();
5247   }
5248
5249   SaveServerScore(level_nr, tape_saved);
5250 }
5251
5252 void MergeServerScore(void)
5253 {
5254   struct ScoreEntry last_added_entry;
5255   boolean one_per_name = FALSE;
5256   int i;
5257
5258   if (scores.last_added >= 0)
5259     last_added_entry = scores.entry[scores.last_added];
5260
5261   for (i = 0; i < server_scores.num_entries; i++)
5262   {
5263     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5264
5265     if (pos >= 0 && pos <= scores.last_added)
5266       scores.last_added++;
5267   }
5268
5269   if (scores.last_added >= MAX_SCORE_ENTRIES)
5270   {
5271     scores.last_added = MAX_SCORE_ENTRIES - 1;
5272     scores.force_last_added = TRUE;
5273
5274     scores.entry[scores.last_added] = last_added_entry;
5275   }
5276 }
5277
5278 static int getElementMoveStepsizeExt(int x, int y, int direction)
5279 {
5280   int element = Tile[x][y];
5281   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5282   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5283   int horiz_move = (dx != 0);
5284   int sign = (horiz_move ? dx : dy);
5285   int step = sign * element_info[element].move_stepsize;
5286
5287   // special values for move stepsize for spring and things on conveyor belt
5288   if (horiz_move)
5289   {
5290     if (CAN_FALL(element) &&
5291         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5292       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5293     else if (element == EL_SPRING)
5294       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5295   }
5296
5297   return step;
5298 }
5299
5300 static int getElementMoveStepsize(int x, int y)
5301 {
5302   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5303 }
5304
5305 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5306 {
5307   if (player->GfxAction != action || player->GfxDir != dir)
5308   {
5309     player->GfxAction = action;
5310     player->GfxDir = dir;
5311     player->Frame = 0;
5312     player->StepFrame = 0;
5313   }
5314 }
5315
5316 static void ResetGfxFrame(int x, int y)
5317 {
5318   // profiling showed that "autotest" spends 10~20% of its time in this function
5319   if (DrawingDeactivatedField())
5320     return;
5321
5322   int element = Tile[x][y];
5323   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5324
5325   if (graphic_info[graphic].anim_global_sync)
5326     GfxFrame[x][y] = FrameCounter;
5327   else if (graphic_info[graphic].anim_global_anim_sync)
5328     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5329   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5330     GfxFrame[x][y] = CustomValue[x][y];
5331   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5332     GfxFrame[x][y] = element_info[element].collect_score;
5333   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5334     GfxFrame[x][y] = ChangeDelay[x][y];
5335 }
5336
5337 static void ResetGfxAnimation(int x, int y)
5338 {
5339   GfxAction[x][y] = ACTION_DEFAULT;
5340   GfxDir[x][y] = MovDir[x][y];
5341   GfxFrame[x][y] = 0;
5342
5343   ResetGfxFrame(x, y);
5344 }
5345
5346 static void ResetRandomAnimationValue(int x, int y)
5347 {
5348   GfxRandom[x][y] = INIT_GFX_RANDOM();
5349 }
5350
5351 static void InitMovingField(int x, int y, int direction)
5352 {
5353   int element = Tile[x][y];
5354   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5355   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5356   int newx = x + dx;
5357   int newy = y + dy;
5358   boolean is_moving_before, is_moving_after;
5359
5360   // check if element was/is moving or being moved before/after mode change
5361   is_moving_before = (WasJustMoving[x][y] != 0);
5362   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5363
5364   // reset animation only for moving elements which change direction of moving
5365   // or which just started or stopped moving
5366   // (else CEs with property "can move" / "not moving" are reset each frame)
5367   if (is_moving_before != is_moving_after ||
5368       direction != MovDir[x][y])
5369     ResetGfxAnimation(x, y);
5370
5371   MovDir[x][y] = direction;
5372   GfxDir[x][y] = direction;
5373
5374   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5375                      direction == MV_DOWN && CAN_FALL(element) ?
5376                      ACTION_FALLING : ACTION_MOVING);
5377
5378   // this is needed for CEs with property "can move" / "not moving"
5379
5380   if (is_moving_after)
5381   {
5382     if (Tile[newx][newy] == EL_EMPTY)
5383       Tile[newx][newy] = EL_BLOCKED;
5384
5385     MovDir[newx][newy] = MovDir[x][y];
5386
5387     CustomValue[newx][newy] = CustomValue[x][y];
5388
5389     GfxFrame[newx][newy] = GfxFrame[x][y];
5390     GfxRandom[newx][newy] = GfxRandom[x][y];
5391     GfxAction[newx][newy] = GfxAction[x][y];
5392     GfxDir[newx][newy] = GfxDir[x][y];
5393   }
5394 }
5395
5396 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5397 {
5398   int direction = MovDir[x][y];
5399   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5400   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5401
5402   *goes_to_x = newx;
5403   *goes_to_y = newy;
5404 }
5405
5406 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5407 {
5408   int oldx = x, oldy = y;
5409   int direction = MovDir[x][y];
5410
5411   if (direction == MV_LEFT)
5412     oldx++;
5413   else if (direction == MV_RIGHT)
5414     oldx--;
5415   else if (direction == MV_UP)
5416     oldy++;
5417   else if (direction == MV_DOWN)
5418     oldy--;
5419
5420   *comes_from_x = oldx;
5421   *comes_from_y = oldy;
5422 }
5423
5424 static int MovingOrBlocked2Element(int x, int y)
5425 {
5426   int element = Tile[x][y];
5427
5428   if (element == EL_BLOCKED)
5429   {
5430     int oldx, oldy;
5431
5432     Blocked2Moving(x, y, &oldx, &oldy);
5433     return Tile[oldx][oldy];
5434   }
5435   else
5436     return element;
5437 }
5438
5439 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5440 {
5441   // like MovingOrBlocked2Element(), but if element is moving
5442   // and (x,y) is the field the moving element is just leaving,
5443   // return EL_BLOCKED instead of the element value
5444   int element = Tile[x][y];
5445
5446   if (IS_MOVING(x, y))
5447   {
5448     if (element == EL_BLOCKED)
5449     {
5450       int oldx, oldy;
5451
5452       Blocked2Moving(x, y, &oldx, &oldy);
5453       return Tile[oldx][oldy];
5454     }
5455     else
5456       return EL_BLOCKED;
5457   }
5458   else
5459     return element;
5460 }
5461
5462 static void RemoveField(int x, int y)
5463 {
5464   Tile[x][y] = EL_EMPTY;
5465
5466   MovPos[x][y] = 0;
5467   MovDir[x][y] = 0;
5468   MovDelay[x][y] = 0;
5469
5470   CustomValue[x][y] = 0;
5471
5472   AmoebaNr[x][y] = 0;
5473   ChangeDelay[x][y] = 0;
5474   ChangePage[x][y] = -1;
5475   Pushed[x][y] = FALSE;
5476
5477   GfxElement[x][y] = EL_UNDEFINED;
5478   GfxAction[x][y] = ACTION_DEFAULT;
5479   GfxDir[x][y] = MV_NONE;
5480 }
5481
5482 static void RemoveMovingField(int x, int y)
5483 {
5484   int oldx = x, oldy = y, newx = x, newy = y;
5485   int element = Tile[x][y];
5486   int next_element = EL_UNDEFINED;
5487
5488   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5489     return;
5490
5491   if (IS_MOVING(x, y))
5492   {
5493     Moving2Blocked(x, y, &newx, &newy);
5494
5495     if (Tile[newx][newy] != EL_BLOCKED)
5496     {
5497       // element is moving, but target field is not free (blocked), but
5498       // already occupied by something different (example: acid pool);
5499       // in this case, only remove the moving field, but not the target
5500
5501       RemoveField(oldx, oldy);
5502
5503       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5504
5505       TEST_DrawLevelField(oldx, oldy);
5506
5507       return;
5508     }
5509   }
5510   else if (element == EL_BLOCKED)
5511   {
5512     Blocked2Moving(x, y, &oldx, &oldy);
5513     if (!IS_MOVING(oldx, oldy))
5514       return;
5515   }
5516
5517   if (element == EL_BLOCKED &&
5518       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5519        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5520        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5521        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5522        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5523        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5524     next_element = get_next_element(Tile[oldx][oldy]);
5525
5526   RemoveField(oldx, oldy);
5527   RemoveField(newx, newy);
5528
5529   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5530
5531   if (next_element != EL_UNDEFINED)
5532     Tile[oldx][oldy] = next_element;
5533
5534   TEST_DrawLevelField(oldx, oldy);
5535   TEST_DrawLevelField(newx, newy);
5536 }
5537
5538 void DrawDynamite(int x, int y)
5539 {
5540   int sx = SCREENX(x), sy = SCREENY(y);
5541   int graphic = el2img(Tile[x][y]);
5542   int frame;
5543
5544   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5545     return;
5546
5547   if (IS_WALKABLE_INSIDE(Back[x][y]))
5548     return;
5549
5550   if (Back[x][y])
5551     DrawLevelElement(x, y, Back[x][y]);
5552   else if (Store[x][y])
5553     DrawLevelElement(x, y, Store[x][y]);
5554   else if (game.use_masked_elements)
5555     DrawLevelElement(x, y, EL_EMPTY);
5556
5557   frame = getGraphicAnimationFrameXY(graphic, x, y);
5558
5559   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5560     DrawGraphicThruMask(sx, sy, graphic, frame);
5561   else
5562     DrawGraphic(sx, sy, graphic, frame);
5563 }
5564
5565 static void CheckDynamite(int x, int y)
5566 {
5567   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5568   {
5569     MovDelay[x][y]--;
5570
5571     if (MovDelay[x][y] != 0)
5572     {
5573       DrawDynamite(x, y);
5574       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5575
5576       return;
5577     }
5578   }
5579
5580   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5581
5582   Bang(x, y);
5583 }
5584
5585 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5586 {
5587   boolean num_checked_players = 0;
5588   int i;
5589
5590   for (i = 0; i < MAX_PLAYERS; i++)
5591   {
5592     if (stored_player[i].active)
5593     {
5594       int sx = stored_player[i].jx;
5595       int sy = stored_player[i].jy;
5596
5597       if (num_checked_players == 0)
5598       {
5599         *sx1 = *sx2 = sx;
5600         *sy1 = *sy2 = sy;
5601       }
5602       else
5603       {
5604         *sx1 = MIN(*sx1, sx);
5605         *sy1 = MIN(*sy1, sy);
5606         *sx2 = MAX(*sx2, sx);
5607         *sy2 = MAX(*sy2, sy);
5608       }
5609
5610       num_checked_players++;
5611     }
5612   }
5613 }
5614
5615 static boolean checkIfAllPlayersFitToScreen_RND(void)
5616 {
5617   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5618
5619   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5620
5621   return (sx2 - sx1 < SCR_FIELDX &&
5622           sy2 - sy1 < SCR_FIELDY);
5623 }
5624
5625 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5626 {
5627   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5628
5629   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5630
5631   *sx = (sx1 + sx2) / 2;
5632   *sy = (sy1 + sy2) / 2;
5633 }
5634
5635 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5636                                boolean center_screen, boolean quick_relocation)
5637 {
5638   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5639   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5640   boolean no_delay = (tape.warp_forward);
5641   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5642   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5643   int new_scroll_x, new_scroll_y;
5644
5645   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5646   {
5647     // case 1: quick relocation inside visible screen (without scrolling)
5648
5649     RedrawPlayfield();
5650
5651     return;
5652   }
5653
5654   if (!level.shifted_relocation || center_screen)
5655   {
5656     // relocation _with_ centering of screen
5657
5658     new_scroll_x = SCROLL_POSITION_X(x);
5659     new_scroll_y = SCROLL_POSITION_Y(y);
5660   }
5661   else
5662   {
5663     // relocation _without_ centering of screen
5664
5665     int center_scroll_x = SCROLL_POSITION_X(old_x);
5666     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5667     int offset_x = x + (scroll_x - center_scroll_x);
5668     int offset_y = y + (scroll_y - center_scroll_y);
5669
5670     // for new screen position, apply previous offset to center position
5671     new_scroll_x = SCROLL_POSITION_X(offset_x);
5672     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5673   }
5674
5675   if (quick_relocation)
5676   {
5677     // case 2: quick relocation (redraw without visible scrolling)
5678
5679     scroll_x = new_scroll_x;
5680     scroll_y = new_scroll_y;
5681
5682     RedrawPlayfield();
5683
5684     return;
5685   }
5686
5687   // case 3: visible relocation (with scrolling to new position)
5688
5689   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5690
5691   SetVideoFrameDelay(wait_delay_value);
5692
5693   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5694   {
5695     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5696     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5697
5698     if (dx == 0 && dy == 0)             // no scrolling needed at all
5699       break;
5700
5701     scroll_x -= dx;
5702     scroll_y -= dy;
5703
5704     // set values for horizontal/vertical screen scrolling (half tile size)
5705     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5706     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5707     int pos_x = dx * TILEX / 2;
5708     int pos_y = dy * TILEY / 2;
5709     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5710     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5711
5712     ScrollLevel(dx, dy);
5713     DrawAllPlayers();
5714
5715     // scroll in two steps of half tile size to make things smoother
5716     BlitScreenToBitmapExt_RND(window, fx, fy);
5717
5718     // scroll second step to align at full tile size
5719     BlitScreenToBitmap(window);
5720   }
5721
5722   DrawAllPlayers();
5723   BackToFront();
5724
5725   SetVideoFrameDelay(frame_delay_value_old);
5726 }
5727
5728 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5729 {
5730   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5731   int player_nr = GET_PLAYER_NR(el_player);
5732   struct PlayerInfo *player = &stored_player[player_nr];
5733   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5734   boolean no_delay = (tape.warp_forward);
5735   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5736   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5737   int old_jx = player->jx;
5738   int old_jy = player->jy;
5739   int old_element = Tile[old_jx][old_jy];
5740   int element = Tile[jx][jy];
5741   boolean player_relocated = (old_jx != jx || old_jy != jy);
5742
5743   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5744   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5745   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5746   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5747   int leave_side_horiz = move_dir_horiz;
5748   int leave_side_vert  = move_dir_vert;
5749   int enter_side = enter_side_horiz | enter_side_vert;
5750   int leave_side = leave_side_horiz | leave_side_vert;
5751
5752   if (player->buried)           // do not reanimate dead player
5753     return;
5754
5755   if (!player_relocated)        // no need to relocate the player
5756     return;
5757
5758   if (IS_PLAYER(jx, jy))        // player already placed at new position
5759   {
5760     RemoveField(jx, jy);        // temporarily remove newly placed player
5761     DrawLevelField(jx, jy);
5762   }
5763
5764   if (player->present)
5765   {
5766     while (player->MovPos)
5767     {
5768       ScrollPlayer(player, SCROLL_GO_ON);
5769       ScrollScreen(NULL, SCROLL_GO_ON);
5770
5771       AdvanceFrameAndPlayerCounters(player->index_nr);
5772
5773       DrawPlayer(player);
5774
5775       BackToFront_WithFrameDelay(wait_delay_value);
5776     }
5777
5778     DrawPlayer(player);         // needed here only to cleanup last field
5779     DrawLevelField(player->jx, player->jy);     // remove player graphic
5780
5781     player->is_moving = FALSE;
5782   }
5783
5784   if (IS_CUSTOM_ELEMENT(old_element))
5785     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5786                                CE_LEFT_BY_PLAYER,
5787                                player->index_bit, leave_side);
5788
5789   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5790                                       CE_PLAYER_LEAVES_X,
5791                                       player->index_bit, leave_side);
5792
5793   Tile[jx][jy] = el_player;
5794   InitPlayerField(jx, jy, el_player, TRUE);
5795
5796   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5797      possible that the relocation target field did not contain a player element,
5798      but a walkable element, to which the new player was relocated -- in this
5799      case, restore that (already initialized!) element on the player field */
5800   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5801   {
5802     Tile[jx][jy] = element;     // restore previously existing element
5803   }
5804
5805   // only visually relocate centered player
5806   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5807                      FALSE, level.instant_relocation);
5808
5809   TestIfPlayerTouchesBadThing(jx, jy);
5810   TestIfPlayerTouchesCustomElement(jx, jy);
5811
5812   if (IS_CUSTOM_ELEMENT(element))
5813     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5814                                player->index_bit, enter_side);
5815
5816   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5817                                       player->index_bit, enter_side);
5818
5819   if (player->is_switching)
5820   {
5821     /* ensure that relocation while still switching an element does not cause
5822        a new element to be treated as also switched directly after relocation
5823        (this is important for teleporter switches that teleport the player to
5824        a place where another teleporter switch is in the same direction, which
5825        would then incorrectly be treated as immediately switched before the
5826        direction key that caused the switch was released) */
5827
5828     player->switch_x += jx - old_jx;
5829     player->switch_y += jy - old_jy;
5830   }
5831 }
5832
5833 static void Explode(int ex, int ey, int phase, int mode)
5834 {
5835   int x, y;
5836   int last_phase;
5837   int border_element;
5838
5839   if (game.explosions_delayed)
5840   {
5841     ExplodeField[ex][ey] = mode;
5842     return;
5843   }
5844
5845   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5846   {
5847     int center_element = Tile[ex][ey];
5848     int artwork_element, explosion_element;     // set these values later
5849
5850     // remove things displayed in background while burning dynamite
5851     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5852       Back[ex][ey] = 0;
5853
5854     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5855     {
5856       // put moving element to center field (and let it explode there)
5857       center_element = MovingOrBlocked2Element(ex, ey);
5858       RemoveMovingField(ex, ey);
5859       Tile[ex][ey] = center_element;
5860     }
5861
5862     // now "center_element" is finally determined -- set related values now
5863     artwork_element = center_element;           // for custom player artwork
5864     explosion_element = center_element;         // for custom player artwork
5865
5866     if (IS_PLAYER(ex, ey))
5867     {
5868       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5869
5870       artwork_element = stored_player[player_nr].artwork_element;
5871
5872       if (level.use_explosion_element[player_nr])
5873       {
5874         explosion_element = level.explosion_element[player_nr];
5875         artwork_element = explosion_element;
5876       }
5877     }
5878
5879     if (mode == EX_TYPE_NORMAL ||
5880         mode == EX_TYPE_CENTER ||
5881         mode == EX_TYPE_CROSS)
5882       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5883
5884     last_phase = element_info[explosion_element].explosion_delay + 1;
5885
5886     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5887     {
5888       int xx = x - ex + 1;
5889       int yy = y - ey + 1;
5890       int element;
5891
5892       if (!IN_LEV_FIELD(x, y) ||
5893           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5895         continue;
5896
5897       element = Tile[x][y];
5898
5899       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5900       {
5901         element = MovingOrBlocked2Element(x, y);
5902
5903         if (!IS_EXPLOSION_PROOF(element))
5904           RemoveMovingField(x, y);
5905       }
5906
5907       // indestructible elements can only explode in center (but not flames)
5908       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909                                            mode == EX_TYPE_BORDER)) ||
5910           element == EL_FLAMES)
5911         continue;
5912
5913       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914          behaviour, for example when touching a yamyam that explodes to rocks
5915          with active deadly shield, a rock is created under the player !!! */
5916       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5917 #if 0
5918       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5921 #else
5922       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5923 #endif
5924       {
5925         if (IS_ACTIVE_BOMB(element))
5926         {
5927           // re-activate things under the bomb like gate or penguin
5928           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5929           Back[x][y] = 0;
5930         }
5931
5932         continue;
5933       }
5934
5935       // save walkable background elements while explosion on same tile
5936       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938         Back[x][y] = element;
5939
5940       // ignite explodable elements reached by other explosion
5941       if (element == EL_EXPLOSION)
5942         element = Store2[x][y];
5943
5944       if (AmoebaNr[x][y] &&
5945           (element == EL_AMOEBA_FULL ||
5946            element == EL_BD_AMOEBA ||
5947            element == EL_AMOEBA_GROWING))
5948       {
5949         AmoebaCnt[AmoebaNr[x][y]]--;
5950         AmoebaCnt2[AmoebaNr[x][y]]--;
5951       }
5952
5953       RemoveField(x, y);
5954
5955       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5956       {
5957         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5958
5959         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5960
5961         if (PLAYERINFO(ex, ey)->use_murphy)
5962           Store[x][y] = EL_EMPTY;
5963       }
5964
5965       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5966       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5967       else if (IS_PLAYER_ELEMENT(center_element))
5968         Store[x][y] = EL_EMPTY;
5969       else if (center_element == EL_YAMYAM)
5970         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972         Store[x][y] = element_info[center_element].content.e[xx][yy];
5973 #if 1
5974       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976       // otherwise) -- FIX THIS !!!
5977       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978         Store[x][y] = element_info[element].content.e[1][1];
5979 #else
5980       else if (!CAN_EXPLODE(element))
5981         Store[x][y] = element_info[element].content.e[1][1];
5982 #endif
5983       else
5984         Store[x][y] = EL_EMPTY;
5985
5986       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987           center_element == EL_AMOEBA_TO_DIAMOND)
5988         Store2[x][y] = element;
5989
5990       Tile[x][y] = EL_EXPLOSION;
5991       GfxElement[x][y] = artwork_element;
5992
5993       ExplodePhase[x][y] = 1;
5994       ExplodeDelay[x][y] = last_phase;
5995
5996       Stop[x][y] = TRUE;
5997     }
5998
5999     if (center_element == EL_YAMYAM)
6000       game.yamyam_content_nr =
6001         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6002
6003     return;
6004   }
6005
6006   if (Stop[ex][ey])
6007     return;
6008
6009   x = ex;
6010   y = ey;
6011
6012   if (phase == 1)
6013     GfxFrame[x][y] = 0;         // restart explosion animation
6014
6015   last_phase = ExplodeDelay[x][y];
6016
6017   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6018
6019   // this can happen if the player leaves an explosion just in time
6020   if (GfxElement[x][y] == EL_UNDEFINED)
6021     GfxElement[x][y] = EL_EMPTY;
6022
6023   border_element = Store2[x][y];
6024   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6025     border_element = StorePlayer[x][y];
6026
6027   if (phase == element_info[border_element].ignition_delay ||
6028       phase == last_phase)
6029   {
6030     boolean border_explosion = FALSE;
6031
6032     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6033         !PLAYER_EXPLOSION_PROTECTED(x, y))
6034     {
6035       KillPlayerUnlessExplosionProtected(x, y);
6036       border_explosion = TRUE;
6037     }
6038     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6039     {
6040       Tile[x][y] = Store2[x][y];
6041       Store2[x][y] = 0;
6042       Bang(x, y);
6043       border_explosion = TRUE;
6044     }
6045     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6046     {
6047       AmoebaToDiamond(x, y);
6048       Store2[x][y] = 0;
6049       border_explosion = TRUE;
6050     }
6051
6052     // if an element just explodes due to another explosion (chain-reaction),
6053     // do not immediately end the new explosion when it was the last frame of
6054     // the explosion (as it would be done in the following "if"-statement!)
6055     if (border_explosion && phase == last_phase)
6056       return;
6057   }
6058
6059   // this can happen if the player was just killed by an explosion
6060   if (GfxElement[x][y] == EL_UNDEFINED)
6061     GfxElement[x][y] = EL_EMPTY;
6062
6063   if (phase == last_phase)
6064   {
6065     int element;
6066
6067     element = Tile[x][y] = Store[x][y];
6068     Store[x][y] = Store2[x][y] = 0;
6069     GfxElement[x][y] = EL_UNDEFINED;
6070
6071     // player can escape from explosions and might therefore be still alive
6072     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6073         element <= EL_PLAYER_IS_EXPLODING_4)
6074     {
6075       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6076       int explosion_element = EL_PLAYER_1 + player_nr;
6077       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6078       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6079
6080       if (level.use_explosion_element[player_nr])
6081         explosion_element = level.explosion_element[player_nr];
6082
6083       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6084                     element_info[explosion_element].content.e[xx][yy]);
6085     }
6086
6087     // restore probably existing indestructible background element
6088     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6089       element = Tile[x][y] = Back[x][y];
6090     Back[x][y] = 0;
6091
6092     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6093     GfxDir[x][y] = MV_NONE;
6094     ChangeDelay[x][y] = 0;
6095     ChangePage[x][y] = -1;
6096
6097     CustomValue[x][y] = 0;
6098
6099     InitField_WithBug2(x, y, FALSE);
6100
6101     TEST_DrawLevelField(x, y);
6102
6103     TestIfElementTouchesCustomElement(x, y);
6104
6105     if (GFX_CRUMBLED(element))
6106       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6107
6108     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6109       StorePlayer[x][y] = 0;
6110
6111     if (IS_PLAYER_ELEMENT(element))
6112       RelocatePlayer(x, y, element);
6113   }
6114   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6115   {
6116     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6117     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6118
6119     if (phase == 1)
6120       TEST_DrawLevelFieldCrumbled(x, y);
6121
6122     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6123     {
6124       DrawLevelElement(x, y, Back[x][y]);
6125       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6126     }
6127     else if (IS_WALKABLE_UNDER(Back[x][y]))
6128     {
6129       DrawLevelGraphic(x, y, graphic, frame);
6130       DrawLevelElementThruMask(x, y, Back[x][y]);
6131     }
6132     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6133       DrawLevelGraphic(x, y, graphic, frame);
6134   }
6135 }
6136
6137 static void DynaExplode(int ex, int ey)
6138 {
6139   int i, j;
6140   int dynabomb_element = Tile[ex][ey];
6141   int dynabomb_size = 1;
6142   boolean dynabomb_xl = FALSE;
6143   struct PlayerInfo *player;
6144   struct XY *xy = xy_topdown;
6145
6146   if (IS_ACTIVE_BOMB(dynabomb_element))
6147   {
6148     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6149     dynabomb_size = player->dynabomb_size;
6150     dynabomb_xl = player->dynabomb_xl;
6151     player->dynabombs_left++;
6152   }
6153
6154   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6155
6156   for (i = 0; i < NUM_DIRECTIONS; i++)
6157   {
6158     for (j = 1; j <= dynabomb_size; j++)
6159     {
6160       int x = ex + j * xy[i].x;
6161       int y = ey + j * xy[i].y;
6162       int element;
6163
6164       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6165         break;
6166
6167       element = Tile[x][y];
6168
6169       // do not restart explosions of fields with active bombs
6170       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6171         continue;
6172
6173       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6174
6175       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6176           !IS_DIGGABLE(element) && !dynabomb_xl)
6177         break;
6178     }
6179   }
6180 }
6181
6182 void Bang(int x, int y)
6183 {
6184   int element = MovingOrBlocked2Element(x, y);
6185   int explosion_type = EX_TYPE_NORMAL;
6186
6187   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6188   {
6189     struct PlayerInfo *player = PLAYERINFO(x, y);
6190
6191     element = Tile[x][y] = player->initial_element;
6192
6193     if (level.use_explosion_element[player->index_nr])
6194     {
6195       int explosion_element = level.explosion_element[player->index_nr];
6196
6197       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6198         explosion_type = EX_TYPE_CROSS;
6199       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6200         explosion_type = EX_TYPE_CENTER;
6201     }
6202   }
6203
6204   switch (element)
6205   {
6206     case EL_BUG:
6207     case EL_SPACESHIP:
6208     case EL_BD_BUTTERFLY:
6209     case EL_BD_FIREFLY:
6210     case EL_YAMYAM:
6211     case EL_DARK_YAMYAM:
6212     case EL_ROBOT:
6213     case EL_PACMAN:
6214     case EL_MOLE:
6215       RaiseScoreElement(element);
6216       break;
6217
6218     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6219     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6220     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6221     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6222     case EL_DYNABOMB_INCREASE_NUMBER:
6223     case EL_DYNABOMB_INCREASE_SIZE:
6224     case EL_DYNABOMB_INCREASE_POWER:
6225       explosion_type = EX_TYPE_DYNA;
6226       break;
6227
6228     case EL_DC_LANDMINE:
6229       explosion_type = EX_TYPE_CENTER;
6230       break;
6231
6232     case EL_PENGUIN:
6233     case EL_LAMP:
6234     case EL_LAMP_ACTIVE:
6235     case EL_AMOEBA_TO_DIAMOND:
6236       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6237         explosion_type = EX_TYPE_CENTER;
6238       break;
6239
6240     default:
6241       if (element_info[element].explosion_type == EXPLODES_CROSS)
6242         explosion_type = EX_TYPE_CROSS;
6243       else if (element_info[element].explosion_type == EXPLODES_1X1)
6244         explosion_type = EX_TYPE_CENTER;
6245       break;
6246   }
6247
6248   if (explosion_type == EX_TYPE_DYNA)
6249     DynaExplode(x, y);
6250   else
6251     Explode(x, y, EX_PHASE_START, explosion_type);
6252
6253   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6254 }
6255
6256 static void SplashAcid(int x, int y)
6257 {
6258   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6259       (!IN_LEV_FIELD(x - 1, y - 2) ||
6260        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6261     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6262
6263   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6264       (!IN_LEV_FIELD(x + 1, y - 2) ||
6265        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6266     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6267
6268   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6269 }
6270
6271 static void InitBeltMovement(void)
6272 {
6273   static int belt_base_element[4] =
6274   {
6275     EL_CONVEYOR_BELT_1_LEFT,
6276     EL_CONVEYOR_BELT_2_LEFT,
6277     EL_CONVEYOR_BELT_3_LEFT,
6278     EL_CONVEYOR_BELT_4_LEFT
6279   };
6280   static int belt_base_active_element[4] =
6281   {
6282     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6283     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6284     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6285     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6286   };
6287
6288   int x, y, i, j;
6289
6290   // set frame order for belt animation graphic according to belt direction
6291   for (i = 0; i < NUM_BELTS; i++)
6292   {
6293     int belt_nr = i;
6294
6295     for (j = 0; j < NUM_BELT_PARTS; j++)
6296     {
6297       int element = belt_base_active_element[belt_nr] + j;
6298       int graphic_1 = el2img(element);
6299       int graphic_2 = el2panelimg(element);
6300
6301       if (game.belt_dir[i] == MV_LEFT)
6302       {
6303         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6304         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6305       }
6306       else
6307       {
6308         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6309         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6310       }
6311     }
6312   }
6313
6314   SCAN_PLAYFIELD(x, y)
6315   {
6316     int element = Tile[x][y];
6317
6318     for (i = 0; i < NUM_BELTS; i++)
6319     {
6320       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6321       {
6322         int e_belt_nr = getBeltNrFromBeltElement(element);
6323         int belt_nr = i;
6324
6325         if (e_belt_nr == belt_nr)
6326         {
6327           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6328
6329           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6330         }
6331       }
6332     }
6333   }
6334 }
6335
6336 static void ToggleBeltSwitch(int x, int y)
6337 {
6338   static int belt_base_element[4] =
6339   {
6340     EL_CONVEYOR_BELT_1_LEFT,
6341     EL_CONVEYOR_BELT_2_LEFT,
6342     EL_CONVEYOR_BELT_3_LEFT,
6343     EL_CONVEYOR_BELT_4_LEFT
6344   };
6345   static int belt_base_active_element[4] =
6346   {
6347     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6348     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6349     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6350     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6351   };
6352   static int belt_base_switch_element[4] =
6353   {
6354     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6355     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6356     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6357     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6358   };
6359   static int belt_move_dir[4] =
6360   {
6361     MV_LEFT,
6362     MV_NONE,
6363     MV_RIGHT,
6364     MV_NONE,
6365   };
6366
6367   int element = Tile[x][y];
6368   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6369   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6370   int belt_dir = belt_move_dir[belt_dir_nr];
6371   int xx, yy, i;
6372
6373   if (!IS_BELT_SWITCH(element))
6374     return;
6375
6376   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6377   game.belt_dir[belt_nr] = belt_dir;
6378
6379   if (belt_dir_nr == 3)
6380     belt_dir_nr = 1;
6381
6382   // set frame order for belt animation graphic according to belt direction
6383   for (i = 0; i < NUM_BELT_PARTS; i++)
6384   {
6385     int element = belt_base_active_element[belt_nr] + i;
6386     int graphic_1 = el2img(element);
6387     int graphic_2 = el2panelimg(element);
6388
6389     if (belt_dir == MV_LEFT)
6390     {
6391       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6392       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6393     }
6394     else
6395     {
6396       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6397       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6398     }
6399   }
6400
6401   SCAN_PLAYFIELD(xx, yy)
6402   {
6403     int element = Tile[xx][yy];
6404
6405     if (IS_BELT_SWITCH(element))
6406     {
6407       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6408
6409       if (e_belt_nr == belt_nr)
6410       {
6411         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6412         TEST_DrawLevelField(xx, yy);
6413       }
6414     }
6415     else if (IS_BELT(element) && belt_dir != MV_NONE)
6416     {
6417       int e_belt_nr = getBeltNrFromBeltElement(element);
6418
6419       if (e_belt_nr == belt_nr)
6420       {
6421         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6422
6423         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6424         TEST_DrawLevelField(xx, yy);
6425       }
6426     }
6427     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6428     {
6429       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6430
6431       if (e_belt_nr == belt_nr)
6432       {
6433         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6434
6435         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6436         TEST_DrawLevelField(xx, yy);
6437       }
6438     }
6439   }
6440 }
6441
6442 static void ToggleSwitchgateSwitch(void)
6443 {
6444   int xx, yy;
6445
6446   game.switchgate_pos = !game.switchgate_pos;
6447
6448   SCAN_PLAYFIELD(xx, yy)
6449   {
6450     int element = Tile[xx][yy];
6451
6452     if (element == EL_SWITCHGATE_SWITCH_UP)
6453     {
6454       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6455       TEST_DrawLevelField(xx, yy);
6456     }
6457     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6458     {
6459       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6460       TEST_DrawLevelField(xx, yy);
6461     }
6462     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6463     {
6464       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6465       TEST_DrawLevelField(xx, yy);
6466     }
6467     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6468     {
6469       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6470       TEST_DrawLevelField(xx, yy);
6471     }
6472     else if (element == EL_SWITCHGATE_OPEN ||
6473              element == EL_SWITCHGATE_OPENING)
6474     {
6475       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6476
6477       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6478     }
6479     else if (element == EL_SWITCHGATE_CLOSED ||
6480              element == EL_SWITCHGATE_CLOSING)
6481     {
6482       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6483
6484       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6485     }
6486   }
6487 }
6488
6489 static int getInvisibleActiveFromInvisibleElement(int element)
6490 {
6491   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6492           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6493           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6494           element);
6495 }
6496
6497 static int getInvisibleFromInvisibleActiveElement(int element)
6498 {
6499   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6500           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6501           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6502           element);
6503 }
6504
6505 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6506 {
6507   int x, y;
6508
6509   SCAN_PLAYFIELD(x, y)
6510   {
6511     int element = Tile[x][y];
6512
6513     if (element == EL_LIGHT_SWITCH &&
6514         game.light_time_left > 0)
6515     {
6516       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6520              game.light_time_left == 0)
6521     {
6522       Tile[x][y] = EL_LIGHT_SWITCH;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_EMC_DRIPPER &&
6526              game.light_time_left > 0)
6527     {
6528       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6529       TEST_DrawLevelField(x, y);
6530     }
6531     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6532              game.light_time_left == 0)
6533     {
6534       Tile[x][y] = EL_EMC_DRIPPER;
6535       TEST_DrawLevelField(x, y);
6536     }
6537     else if (element == EL_INVISIBLE_STEELWALL ||
6538              element == EL_INVISIBLE_WALL ||
6539              element == EL_INVISIBLE_SAND)
6540     {
6541       if (game.light_time_left > 0)
6542         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6543
6544       TEST_DrawLevelField(x, y);
6545
6546       // uncrumble neighbour fields, if needed
6547       if (element == EL_INVISIBLE_SAND)
6548         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6549     }
6550     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6551              element == EL_INVISIBLE_WALL_ACTIVE ||
6552              element == EL_INVISIBLE_SAND_ACTIVE)
6553     {
6554       if (game.light_time_left == 0)
6555         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6556
6557       TEST_DrawLevelField(x, y);
6558
6559       // re-crumble neighbour fields, if needed
6560       if (element == EL_INVISIBLE_SAND)
6561         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6562     }
6563   }
6564 }
6565
6566 static void RedrawAllInvisibleElementsForLenses(void)
6567 {
6568   int x, y;
6569
6570   SCAN_PLAYFIELD(x, y)
6571   {
6572     int element = Tile[x][y];
6573
6574     if (element == EL_EMC_DRIPPER &&
6575         game.lenses_time_left > 0)
6576     {
6577       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6578       TEST_DrawLevelField(x, y);
6579     }
6580     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6581              game.lenses_time_left == 0)
6582     {
6583       Tile[x][y] = EL_EMC_DRIPPER;
6584       TEST_DrawLevelField(x, y);
6585     }
6586     else if (element == EL_INVISIBLE_STEELWALL ||
6587              element == EL_INVISIBLE_WALL ||
6588              element == EL_INVISIBLE_SAND)
6589     {
6590       if (game.lenses_time_left > 0)
6591         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6592
6593       TEST_DrawLevelField(x, y);
6594
6595       // uncrumble neighbour fields, if needed
6596       if (element == EL_INVISIBLE_SAND)
6597         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6598     }
6599     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6600              element == EL_INVISIBLE_WALL_ACTIVE ||
6601              element == EL_INVISIBLE_SAND_ACTIVE)
6602     {
6603       if (game.lenses_time_left == 0)
6604         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6605
6606       TEST_DrawLevelField(x, y);
6607
6608       // re-crumble neighbour fields, if needed
6609       if (element == EL_INVISIBLE_SAND)
6610         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6611     }
6612   }
6613 }
6614
6615 static void RedrawAllInvisibleElementsForMagnifier(void)
6616 {
6617   int x, y;
6618
6619   SCAN_PLAYFIELD(x, y)
6620   {
6621     int element = Tile[x][y];
6622
6623     if (element == EL_EMC_FAKE_GRASS &&
6624         game.magnify_time_left > 0)
6625     {
6626       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6627       TEST_DrawLevelField(x, y);
6628     }
6629     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6630              game.magnify_time_left == 0)
6631     {
6632       Tile[x][y] = EL_EMC_FAKE_GRASS;
6633       TEST_DrawLevelField(x, y);
6634     }
6635     else if (IS_GATE_GRAY(element) &&
6636              game.magnify_time_left > 0)
6637     {
6638       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6639                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6640                     IS_EM_GATE_GRAY(element) ?
6641                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6642                     IS_EMC_GATE_GRAY(element) ?
6643                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6644                     IS_DC_GATE_GRAY(element) ?
6645                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6646                     element);
6647       TEST_DrawLevelField(x, y);
6648     }
6649     else if (IS_GATE_GRAY_ACTIVE(element) &&
6650              game.magnify_time_left == 0)
6651     {
6652       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6653                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6654                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6655                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6656                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6657                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6658                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6659                     EL_DC_GATE_WHITE_GRAY :
6660                     element);
6661       TEST_DrawLevelField(x, y);
6662     }
6663   }
6664 }
6665
6666 static void ToggleLightSwitch(int x, int y)
6667 {
6668   int element = Tile[x][y];
6669
6670   game.light_time_left =
6671     (element == EL_LIGHT_SWITCH ?
6672      level.time_light * FRAMES_PER_SECOND : 0);
6673
6674   RedrawAllLightSwitchesAndInvisibleElements();
6675 }
6676
6677 static void ActivateTimegateSwitch(int x, int y)
6678 {
6679   int xx, yy;
6680
6681   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6682
6683   SCAN_PLAYFIELD(xx, yy)
6684   {
6685     int element = Tile[xx][yy];
6686
6687     if (element == EL_TIMEGATE_CLOSED ||
6688         element == EL_TIMEGATE_CLOSING)
6689     {
6690       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6691       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6692     }
6693
6694     /*
6695     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6696     {
6697       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6698       TEST_DrawLevelField(xx, yy);
6699     }
6700     */
6701
6702   }
6703
6704   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6705                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6706 }
6707
6708 static void Impact(int x, int y)
6709 {
6710   boolean last_line = (y == lev_fieldy - 1);
6711   boolean object_hit = FALSE;
6712   boolean impact = (last_line || object_hit);
6713   int element = Tile[x][y];
6714   int smashed = EL_STEELWALL;
6715
6716   if (!last_line)       // check if element below was hit
6717   {
6718     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6719       return;
6720
6721     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6722                                          MovDir[x][y + 1] != MV_DOWN ||
6723                                          MovPos[x][y + 1] <= TILEY / 2));
6724
6725     // do not smash moving elements that left the smashed field in time
6726     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6727         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6728       object_hit = FALSE;
6729
6730 #if USE_QUICKSAND_IMPACT_BUGFIX
6731     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6732     {
6733       RemoveMovingField(x, y + 1);
6734       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6735       Tile[x][y + 2] = EL_ROCK;
6736       TEST_DrawLevelField(x, y + 2);
6737
6738       object_hit = TRUE;
6739     }
6740
6741     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6742     {
6743       RemoveMovingField(x, y + 1);
6744       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6745       Tile[x][y + 2] = EL_ROCK;
6746       TEST_DrawLevelField(x, y + 2);
6747
6748       object_hit = TRUE;
6749     }
6750 #endif
6751
6752     if (object_hit)
6753       smashed = MovingOrBlocked2Element(x, y + 1);
6754
6755     impact = (last_line || object_hit);
6756   }
6757
6758   if (!last_line && smashed == EL_ACID) // element falls into acid
6759   {
6760     SplashAcid(x, y + 1);
6761     return;
6762   }
6763
6764   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6765   // only reset graphic animation if graphic really changes after impact
6766   if (impact &&
6767       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6768   {
6769     ResetGfxAnimation(x, y);
6770     TEST_DrawLevelField(x, y);
6771   }
6772
6773   if (impact && CAN_EXPLODE_IMPACT(element))
6774   {
6775     Bang(x, y);
6776     return;
6777   }
6778   else if (impact && element == EL_PEARL &&
6779            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6780   {
6781     ResetGfxAnimation(x, y);
6782
6783     Tile[x][y] = EL_PEARL_BREAKING;
6784     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6785     return;
6786   }
6787   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6788   {
6789     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6790
6791     return;
6792   }
6793
6794   if (impact && element == EL_AMOEBA_DROP)
6795   {
6796     if (object_hit && IS_PLAYER(x, y + 1))
6797       KillPlayerUnlessEnemyProtected(x, y + 1);
6798     else if (object_hit && smashed == EL_PENGUIN)
6799       Bang(x, y + 1);
6800     else
6801     {
6802       Tile[x][y] = EL_AMOEBA_GROWING;
6803       Store[x][y] = EL_AMOEBA_WET;
6804
6805       ResetRandomAnimationValue(x, y);
6806     }
6807     return;
6808   }
6809
6810   if (object_hit)               // check which object was hit
6811   {
6812     if ((CAN_PASS_MAGIC_WALL(element) && 
6813          (smashed == EL_MAGIC_WALL ||
6814           smashed == EL_BD_MAGIC_WALL)) ||
6815         (CAN_PASS_DC_MAGIC_WALL(element) &&
6816          smashed == EL_DC_MAGIC_WALL))
6817     {
6818       int xx, yy;
6819       int activated_magic_wall =
6820         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6821          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6822          EL_DC_MAGIC_WALL_ACTIVE);
6823
6824       // activate magic wall / mill
6825       SCAN_PLAYFIELD(xx, yy)
6826       {
6827         if (Tile[xx][yy] == smashed)
6828           Tile[xx][yy] = activated_magic_wall;
6829       }
6830
6831       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6832       game.magic_wall_active = TRUE;
6833
6834       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6835                             SND_MAGIC_WALL_ACTIVATING :
6836                             smashed == EL_BD_MAGIC_WALL ?
6837                             SND_BD_MAGIC_WALL_ACTIVATING :
6838                             SND_DC_MAGIC_WALL_ACTIVATING));
6839     }
6840
6841     if (IS_PLAYER(x, y + 1))
6842     {
6843       if (CAN_SMASH_PLAYER(element))
6844       {
6845         KillPlayerUnlessEnemyProtected(x, y + 1);
6846         return;
6847       }
6848     }
6849     else if (smashed == EL_PENGUIN)
6850     {
6851       if (CAN_SMASH_PLAYER(element))
6852       {
6853         Bang(x, y + 1);
6854         return;
6855       }
6856     }
6857     else if (element == EL_BD_DIAMOND)
6858     {
6859       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6860       {
6861         Bang(x, y + 1);
6862         return;
6863       }
6864     }
6865     else if (((element == EL_SP_INFOTRON ||
6866                element == EL_SP_ZONK) &&
6867               (smashed == EL_SP_SNIKSNAK ||
6868                smashed == EL_SP_ELECTRON ||
6869                smashed == EL_SP_DISK_ORANGE)) ||
6870              (element == EL_SP_INFOTRON &&
6871               smashed == EL_SP_DISK_YELLOW))
6872     {
6873       Bang(x, y + 1);
6874       return;
6875     }
6876     else if (CAN_SMASH_EVERYTHING(element))
6877     {
6878       if (IS_CLASSIC_ENEMY(smashed) ||
6879           CAN_EXPLODE_SMASHED(smashed))
6880       {
6881         Bang(x, y + 1);
6882         return;
6883       }
6884       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6885       {
6886         if (smashed == EL_LAMP ||
6887             smashed == EL_LAMP_ACTIVE)
6888         {
6889           Bang(x, y + 1);
6890           return;
6891         }
6892         else if (smashed == EL_NUT)
6893         {
6894           Tile[x][y + 1] = EL_NUT_BREAKING;
6895           PlayLevelSound(x, y, SND_NUT_BREAKING);
6896           RaiseScoreElement(EL_NUT);
6897           return;
6898         }
6899         else if (smashed == EL_PEARL)
6900         {
6901           ResetGfxAnimation(x, y);
6902
6903           Tile[x][y + 1] = EL_PEARL_BREAKING;
6904           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6905           return;
6906         }
6907         else if (smashed == EL_DIAMOND)
6908         {
6909           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6910           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6911           return;
6912         }
6913         else if (IS_BELT_SWITCH(smashed))
6914         {
6915           ToggleBeltSwitch(x, y + 1);
6916         }
6917         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6918                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6919                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6920                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6921         {
6922           ToggleSwitchgateSwitch();
6923         }
6924         else if (smashed == EL_LIGHT_SWITCH ||
6925                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6926         {
6927           ToggleLightSwitch(x, y + 1);
6928         }
6929         else
6930         {
6931           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6932
6933           CheckElementChangeBySide(x, y + 1, smashed, element,
6934                                    CE_SWITCHED, CH_SIDE_TOP);
6935           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6936                                             CH_SIDE_TOP);
6937         }
6938       }
6939       else
6940       {
6941         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6942       }
6943     }
6944   }
6945
6946   // play sound of magic wall / mill
6947   if (!last_line &&
6948       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6949        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6950        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6951   {
6952     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6953       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6954     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6955       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6956     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6957       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6958
6959     return;
6960   }
6961
6962   // play sound of object that hits the ground
6963   if (last_line || object_hit)
6964     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6965 }
6966
6967 static void TurnRoundExt(int x, int y)
6968 {
6969   static struct
6970   {
6971     int dx, dy;
6972   } move_xy[] =
6973   {
6974     {  0,  0 },
6975     { -1,  0 },
6976     { +1,  0 },
6977     {  0,  0 },
6978     {  0, -1 },
6979     {  0,  0 }, { 0, 0 }, { 0, 0 },
6980     {  0, +1 }
6981   };
6982   static struct
6983   {
6984     int left, right, back;
6985   } turn[] =
6986   {
6987     { 0,        0,              0        },
6988     { MV_DOWN,  MV_UP,          MV_RIGHT },
6989     { MV_UP,    MV_DOWN,        MV_LEFT  },
6990     { 0,        0,              0        },
6991     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6992     { 0,        0,              0        },
6993     { 0,        0,              0        },
6994     { 0,        0,              0        },
6995     { MV_RIGHT, MV_LEFT,        MV_UP    }
6996   };
6997
6998   int element = Tile[x][y];
6999   int move_pattern = element_info[element].move_pattern;
7000
7001   int old_move_dir = MovDir[x][y];
7002   int left_dir  = turn[old_move_dir].left;
7003   int right_dir = turn[old_move_dir].right;
7004   int back_dir  = turn[old_move_dir].back;
7005
7006   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7007   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7008   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7009   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7010
7011   int left_x  = x + left_dx,  left_y  = y + left_dy;
7012   int right_x = x + right_dx, right_y = y + right_dy;
7013   int move_x  = x + move_dx,  move_y  = y + move_dy;
7014
7015   int xx, yy;
7016
7017   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7018   {
7019     TestIfBadThingTouchesOtherBadThing(x, y);
7020
7021     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7022       MovDir[x][y] = right_dir;
7023     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7024       MovDir[x][y] = left_dir;
7025
7026     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7027       MovDelay[x][y] = 9;
7028     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7029       MovDelay[x][y] = 1;
7030   }
7031   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7032   {
7033     TestIfBadThingTouchesOtherBadThing(x, y);
7034
7035     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7036       MovDir[x][y] = left_dir;
7037     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7038       MovDir[x][y] = right_dir;
7039
7040     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7041       MovDelay[x][y] = 9;
7042     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7043       MovDelay[x][y] = 1;
7044   }
7045   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7046   {
7047     TestIfBadThingTouchesOtherBadThing(x, y);
7048
7049     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7050       MovDir[x][y] = left_dir;
7051     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7052       MovDir[x][y] = right_dir;
7053
7054     if (MovDir[x][y] != old_move_dir)
7055       MovDelay[x][y] = 9;
7056   }
7057   else if (element == EL_YAMYAM)
7058   {
7059     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7060     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7061
7062     if (can_turn_left && can_turn_right)
7063       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7064     else if (can_turn_left)
7065       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7066     else if (can_turn_right)
7067       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7068     else
7069       MovDir[x][y] = back_dir;
7070
7071     MovDelay[x][y] = 16 + 16 * RND(3);
7072   }
7073   else if (element == EL_DARK_YAMYAM)
7074   {
7075     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7076                                                          left_x, left_y);
7077     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7078                                                          right_x, right_y);
7079
7080     if (can_turn_left && can_turn_right)
7081       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7082     else if (can_turn_left)
7083       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7084     else if (can_turn_right)
7085       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7086     else
7087       MovDir[x][y] = back_dir;
7088
7089     MovDelay[x][y] = 16 + 16 * RND(3);
7090   }
7091   else if (element == EL_PACMAN)
7092   {
7093     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7094     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7095
7096     if (can_turn_left && can_turn_right)
7097       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7098     else if (can_turn_left)
7099       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7100     else if (can_turn_right)
7101       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7102     else
7103       MovDir[x][y] = back_dir;
7104
7105     MovDelay[x][y] = 6 + RND(40);
7106   }
7107   else if (element == EL_PIG)
7108   {
7109     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7110     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7111     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7112     boolean should_turn_left, should_turn_right, should_move_on;
7113     int rnd_value = 24;
7114     int rnd = RND(rnd_value);
7115
7116     should_turn_left = (can_turn_left &&
7117                         (!can_move_on ||
7118                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7119                                                    y + back_dy + left_dy)));
7120     should_turn_right = (can_turn_right &&
7121                          (!can_move_on ||
7122                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7123                                                     y + back_dy + right_dy)));
7124     should_move_on = (can_move_on &&
7125                       (!can_turn_left ||
7126                        !can_turn_right ||
7127                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7128                                                  y + move_dy + left_dy) ||
7129                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7130                                                  y + move_dy + right_dy)));
7131
7132     if (should_turn_left || should_turn_right || should_move_on)
7133     {
7134       if (should_turn_left && should_turn_right && should_move_on)
7135         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7136                         rnd < 2 * rnd_value / 3 ? right_dir :
7137                         old_move_dir);
7138       else if (should_turn_left && should_turn_right)
7139         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7140       else if (should_turn_left && should_move_on)
7141         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7142       else if (should_turn_right && should_move_on)
7143         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7144       else if (should_turn_left)
7145         MovDir[x][y] = left_dir;
7146       else if (should_turn_right)
7147         MovDir[x][y] = right_dir;
7148       else if (should_move_on)
7149         MovDir[x][y] = old_move_dir;
7150     }
7151     else if (can_move_on && rnd > rnd_value / 8)
7152       MovDir[x][y] = old_move_dir;
7153     else if (can_turn_left && can_turn_right)
7154       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7155     else if (can_turn_left && rnd > rnd_value / 8)
7156       MovDir[x][y] = left_dir;
7157     else if (can_turn_right && rnd > rnd_value/8)
7158       MovDir[x][y] = right_dir;
7159     else
7160       MovDir[x][y] = back_dir;
7161
7162     xx = x + move_xy[MovDir[x][y]].dx;
7163     yy = y + move_xy[MovDir[x][y]].dy;
7164
7165     if (!IN_LEV_FIELD(xx, yy) ||
7166         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7167       MovDir[x][y] = old_move_dir;
7168
7169     MovDelay[x][y] = 0;
7170   }
7171   else if (element == EL_DRAGON)
7172   {
7173     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7174     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7175     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7176     int rnd_value = 24;
7177     int rnd = RND(rnd_value);
7178
7179     if (can_move_on && rnd > rnd_value / 8)
7180       MovDir[x][y] = old_move_dir;
7181     else if (can_turn_left && can_turn_right)
7182       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7183     else if (can_turn_left && rnd > rnd_value / 8)
7184       MovDir[x][y] = left_dir;
7185     else if (can_turn_right && rnd > rnd_value / 8)
7186       MovDir[x][y] = right_dir;
7187     else
7188       MovDir[x][y] = back_dir;
7189
7190     xx = x + move_xy[MovDir[x][y]].dx;
7191     yy = y + move_xy[MovDir[x][y]].dy;
7192
7193     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7194       MovDir[x][y] = old_move_dir;
7195
7196     MovDelay[x][y] = 0;
7197   }
7198   else if (element == EL_MOLE)
7199   {
7200     boolean can_move_on =
7201       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7202                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7203                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7204     if (!can_move_on)
7205     {
7206       boolean can_turn_left =
7207         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7208                               IS_AMOEBOID(Tile[left_x][left_y])));
7209
7210       boolean can_turn_right =
7211         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7212                               IS_AMOEBOID(Tile[right_x][right_y])));
7213
7214       if (can_turn_left && can_turn_right)
7215         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7216       else if (can_turn_left)
7217         MovDir[x][y] = left_dir;
7218       else
7219         MovDir[x][y] = right_dir;
7220     }
7221
7222     if (MovDir[x][y] != old_move_dir)
7223       MovDelay[x][y] = 9;
7224   }
7225   else if (element == EL_BALLOON)
7226   {
7227     MovDir[x][y] = game.wind_direction;
7228     MovDelay[x][y] = 0;
7229   }
7230   else if (element == EL_SPRING)
7231   {
7232     if (MovDir[x][y] & MV_HORIZONTAL)
7233     {
7234       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7235           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7236       {
7237         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7238         ResetGfxAnimation(move_x, move_y);
7239         TEST_DrawLevelField(move_x, move_y);
7240
7241         MovDir[x][y] = back_dir;
7242       }
7243       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7244                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7245         MovDir[x][y] = MV_NONE;
7246     }
7247
7248     MovDelay[x][y] = 0;
7249   }
7250   else if (element == EL_ROBOT ||
7251            element == EL_SATELLITE ||
7252            element == EL_PENGUIN ||
7253            element == EL_EMC_ANDROID)
7254   {
7255     int attr_x = -1, attr_y = -1;
7256
7257     if (game.all_players_gone)
7258     {
7259       attr_x = game.exit_x;
7260       attr_y = game.exit_y;
7261     }
7262     else
7263     {
7264       int i;
7265
7266       for (i = 0; i < MAX_PLAYERS; i++)
7267       {
7268         struct PlayerInfo *player = &stored_player[i];
7269         int jx = player->jx, jy = player->jy;
7270
7271         if (!player->active)
7272           continue;
7273
7274         if (attr_x == -1 ||
7275             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7276         {
7277           attr_x = jx;
7278           attr_y = jy;
7279         }
7280       }
7281     }
7282
7283     if (element == EL_ROBOT &&
7284         game.robot_wheel_x >= 0 &&
7285         game.robot_wheel_y >= 0 &&
7286         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7287          game.engine_version < VERSION_IDENT(3,1,0,0)))
7288     {
7289       attr_x = game.robot_wheel_x;
7290       attr_y = game.robot_wheel_y;
7291     }
7292
7293     if (element == EL_PENGUIN)
7294     {
7295       int i;
7296       struct XY *xy = xy_topdown;
7297
7298       for (i = 0; i < NUM_DIRECTIONS; i++)
7299       {
7300         int ex = x + xy[i].x;
7301         int ey = y + xy[i].y;
7302
7303         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7304                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7305                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7306                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7307         {
7308           attr_x = ex;
7309           attr_y = ey;
7310           break;
7311         }
7312       }
7313     }
7314
7315     MovDir[x][y] = MV_NONE;
7316     if (attr_x < x)
7317       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7318     else if (attr_x > x)
7319       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7320     if (attr_y < y)
7321       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7322     else if (attr_y > y)
7323       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7324
7325     if (element == EL_ROBOT)
7326     {
7327       int newx, newy;
7328
7329       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7330         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7331       Moving2Blocked(x, y, &newx, &newy);
7332
7333       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7334         MovDelay[x][y] = 8 + 8 * !RND(3);
7335       else
7336         MovDelay[x][y] = 16;
7337     }
7338     else if (element == EL_PENGUIN)
7339     {
7340       int newx, newy;
7341
7342       MovDelay[x][y] = 1;
7343
7344       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7345       {
7346         boolean first_horiz = RND(2);
7347         int new_move_dir = MovDir[x][y];
7348
7349         MovDir[x][y] =
7350           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351         Moving2Blocked(x, y, &newx, &newy);
7352
7353         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7354           return;
7355
7356         MovDir[x][y] =
7357           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358         Moving2Blocked(x, y, &newx, &newy);
7359
7360         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7361           return;
7362
7363         MovDir[x][y] = old_move_dir;
7364         return;
7365       }
7366     }
7367     else if (element == EL_SATELLITE)
7368     {
7369       int newx, newy;
7370
7371       MovDelay[x][y] = 1;
7372
7373       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7374       {
7375         boolean first_horiz = RND(2);
7376         int new_move_dir = MovDir[x][y];
7377
7378         MovDir[x][y] =
7379           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7380         Moving2Blocked(x, y, &newx, &newy);
7381
7382         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7383           return;
7384
7385         MovDir[x][y] =
7386           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7387         Moving2Blocked(x, y, &newx, &newy);
7388
7389         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7390           return;
7391
7392         MovDir[x][y] = old_move_dir;
7393         return;
7394       }
7395     }
7396     else if (element == EL_EMC_ANDROID)
7397     {
7398       static int check_pos[16] =
7399       {
7400         -1,             //  0 => (invalid)
7401         7,              //  1 => MV_LEFT
7402         3,              //  2 => MV_RIGHT
7403         -1,             //  3 => (invalid)
7404         1,              //  4 =>            MV_UP
7405         0,              //  5 => MV_LEFT  | MV_UP
7406         2,              //  6 => MV_RIGHT | MV_UP
7407         -1,             //  7 => (invalid)
7408         5,              //  8 =>            MV_DOWN
7409         6,              //  9 => MV_LEFT  | MV_DOWN
7410         4,              // 10 => MV_RIGHT | MV_DOWN
7411         -1,             // 11 => (invalid)
7412         -1,             // 12 => (invalid)
7413         -1,             // 13 => (invalid)
7414         -1,             // 14 => (invalid)
7415         -1,             // 15 => (invalid)
7416       };
7417       static struct
7418       {
7419         int dx, dy;
7420         int dir;
7421       } check_xy[8] =
7422       {
7423         { -1, -1,       MV_LEFT  | MV_UP   },
7424         {  0, -1,                  MV_UP   },
7425         { +1, -1,       MV_RIGHT | MV_UP   },
7426         { +1,  0,       MV_RIGHT           },
7427         { +1, +1,       MV_RIGHT | MV_DOWN },
7428         {  0, +1,                  MV_DOWN },
7429         { -1, +1,       MV_LEFT  | MV_DOWN },
7430         { -1,  0,       MV_LEFT            },
7431       };
7432       int start_pos, check_order;
7433       boolean can_clone = FALSE;
7434       int i;
7435
7436       // check if there is any free field around current position
7437       for (i = 0; i < 8; i++)
7438       {
7439         int newx = x + check_xy[i].dx;
7440         int newy = y + check_xy[i].dy;
7441
7442         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7443         {
7444           can_clone = TRUE;
7445
7446           break;
7447         }
7448       }
7449
7450       if (can_clone)            // randomly find an element to clone
7451       {
7452         can_clone = FALSE;
7453
7454         start_pos = check_pos[RND(8)];
7455         check_order = (RND(2) ? -1 : +1);
7456
7457         for (i = 0; i < 8; i++)
7458         {
7459           int pos_raw = start_pos + i * check_order;
7460           int pos = (pos_raw + 8) % 8;
7461           int newx = x + check_xy[pos].dx;
7462           int newy = y + check_xy[pos].dy;
7463
7464           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7465           {
7466             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7467             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7468
7469             Store[x][y] = Tile[newx][newy];
7470
7471             can_clone = TRUE;
7472
7473             break;
7474           }
7475         }
7476       }
7477
7478       if (can_clone)            // randomly find a direction to move
7479       {
7480         can_clone = FALSE;
7481
7482         start_pos = check_pos[RND(8)];
7483         check_order = (RND(2) ? -1 : +1);
7484
7485         for (i = 0; i < 8; i++)
7486         {
7487           int pos_raw = start_pos + i * check_order;
7488           int pos = (pos_raw + 8) % 8;
7489           int newx = x + check_xy[pos].dx;
7490           int newy = y + check_xy[pos].dy;
7491           int new_move_dir = check_xy[pos].dir;
7492
7493           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7494           {
7495             MovDir[x][y] = new_move_dir;
7496             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7497
7498             can_clone = TRUE;
7499
7500             break;
7501           }
7502         }
7503       }
7504
7505       if (can_clone)            // cloning and moving successful
7506         return;
7507
7508       // cannot clone -- try to move towards player
7509
7510       start_pos = check_pos[MovDir[x][y] & 0x0f];
7511       check_order = (RND(2) ? -1 : +1);
7512
7513       for (i = 0; i < 3; i++)
7514       {
7515         // first check start_pos, then previous/next or (next/previous) pos
7516         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7517         int pos = (pos_raw + 8) % 8;
7518         int newx = x + check_xy[pos].dx;
7519         int newy = y + check_xy[pos].dy;
7520         int new_move_dir = check_xy[pos].dir;
7521
7522         if (IS_PLAYER(newx, newy))
7523           break;
7524
7525         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7526         {
7527           MovDir[x][y] = new_move_dir;
7528           MovDelay[x][y] = level.android_move_time * 8 + 1;
7529
7530           break;
7531         }
7532       }
7533     }
7534   }
7535   else if (move_pattern == MV_TURNING_LEFT ||
7536            move_pattern == MV_TURNING_RIGHT ||
7537            move_pattern == MV_TURNING_LEFT_RIGHT ||
7538            move_pattern == MV_TURNING_RIGHT_LEFT ||
7539            move_pattern == MV_TURNING_RANDOM ||
7540            move_pattern == MV_ALL_DIRECTIONS)
7541   {
7542     boolean can_turn_left =
7543       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7544     boolean can_turn_right =
7545       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7546
7547     if (element_info[element].move_stepsize == 0)       // "not moving"
7548       return;
7549
7550     if (move_pattern == MV_TURNING_LEFT)
7551       MovDir[x][y] = left_dir;
7552     else if (move_pattern == MV_TURNING_RIGHT)
7553       MovDir[x][y] = right_dir;
7554     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7555       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7556     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7557       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7558     else if (move_pattern == MV_TURNING_RANDOM)
7559       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7560                       can_turn_right && !can_turn_left ? right_dir :
7561                       RND(2) ? left_dir : right_dir);
7562     else if (can_turn_left && can_turn_right)
7563       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7564     else if (can_turn_left)
7565       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7566     else if (can_turn_right)
7567       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7568     else
7569       MovDir[x][y] = back_dir;
7570
7571     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7572   }
7573   else if (move_pattern == MV_HORIZONTAL ||
7574            move_pattern == MV_VERTICAL)
7575   {
7576     if (move_pattern & old_move_dir)
7577       MovDir[x][y] = back_dir;
7578     else if (move_pattern == MV_HORIZONTAL)
7579       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7580     else if (move_pattern == MV_VERTICAL)
7581       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7582
7583     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7584   }
7585   else if (move_pattern & MV_ANY_DIRECTION)
7586   {
7587     MovDir[x][y] = move_pattern;
7588     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7589   }
7590   else if (move_pattern & MV_WIND_DIRECTION)
7591   {
7592     MovDir[x][y] = game.wind_direction;
7593     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7596   {
7597     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7598       MovDir[x][y] = left_dir;
7599     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7600       MovDir[x][y] = right_dir;
7601
7602     if (MovDir[x][y] != old_move_dir)
7603       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7604   }
7605   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7606   {
7607     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7608       MovDir[x][y] = right_dir;
7609     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7610       MovDir[x][y] = left_dir;
7611
7612     if (MovDir[x][y] != old_move_dir)
7613       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7614   }
7615   else if (move_pattern == MV_TOWARDS_PLAYER ||
7616            move_pattern == MV_AWAY_FROM_PLAYER)
7617   {
7618     int attr_x = -1, attr_y = -1;
7619     int newx, newy;
7620     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7621
7622     if (game.all_players_gone)
7623     {
7624       attr_x = game.exit_x;
7625       attr_y = game.exit_y;
7626     }
7627     else
7628     {
7629       int i;
7630
7631       for (i = 0; i < MAX_PLAYERS; i++)
7632       {
7633         struct PlayerInfo *player = &stored_player[i];
7634         int jx = player->jx, jy = player->jy;
7635
7636         if (!player->active)
7637           continue;
7638
7639         if (attr_x == -1 ||
7640             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7641         {
7642           attr_x = jx;
7643           attr_y = jy;
7644         }
7645       }
7646     }
7647
7648     MovDir[x][y] = MV_NONE;
7649     if (attr_x < x)
7650       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7651     else if (attr_x > x)
7652       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7653     if (attr_y < y)
7654       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7655     else if (attr_y > y)
7656       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7657
7658     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7659
7660     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7661     {
7662       boolean first_horiz = RND(2);
7663       int new_move_dir = MovDir[x][y];
7664
7665       if (element_info[element].move_stepsize == 0)     // "not moving"
7666       {
7667         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7668         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7669
7670         return;
7671       }
7672
7673       MovDir[x][y] =
7674         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7675       Moving2Blocked(x, y, &newx, &newy);
7676
7677       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7678         return;
7679
7680       MovDir[x][y] =
7681         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7682       Moving2Blocked(x, y, &newx, &newy);
7683
7684       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7685         return;
7686
7687       MovDir[x][y] = old_move_dir;
7688     }
7689   }
7690   else if (move_pattern == MV_WHEN_PUSHED ||
7691            move_pattern == MV_WHEN_DROPPED)
7692   {
7693     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7694       MovDir[x][y] = MV_NONE;
7695
7696     MovDelay[x][y] = 0;
7697   }
7698   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7699   {
7700     struct XY *test_xy = xy_topdown;
7701     static int test_dir[4] =
7702     {
7703       MV_UP,
7704       MV_LEFT,
7705       MV_RIGHT,
7706       MV_DOWN
7707     };
7708     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7709     int move_preference = -1000000;     // start with very low preference
7710     int new_move_dir = MV_NONE;
7711     int start_test = RND(4);
7712     int i;
7713
7714     for (i = 0; i < NUM_DIRECTIONS; i++)
7715     {
7716       int j = (start_test + i) % 4;
7717       int move_dir = test_dir[j];
7718       int move_dir_preference;
7719
7720       xx = x + test_xy[j].x;
7721       yy = y + test_xy[j].y;
7722
7723       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7724           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7725       {
7726         new_move_dir = move_dir;
7727
7728         break;
7729       }
7730
7731       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7732         continue;
7733
7734       move_dir_preference = -1 * RunnerVisit[xx][yy];
7735       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7736         move_dir_preference = PlayerVisit[xx][yy];
7737
7738       if (move_dir_preference > move_preference)
7739       {
7740         // prefer field that has not been visited for the longest time
7741         move_preference = move_dir_preference;
7742         new_move_dir = move_dir;
7743       }
7744       else if (move_dir_preference == move_preference &&
7745                move_dir == old_move_dir)
7746       {
7747         // prefer last direction when all directions are preferred equally
7748         move_preference = move_dir_preference;
7749         new_move_dir = move_dir;
7750       }
7751     }
7752
7753     MovDir[x][y] = new_move_dir;
7754     if (old_move_dir != new_move_dir)
7755       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756   }
7757 }
7758
7759 static void TurnRound(int x, int y)
7760 {
7761   int direction = MovDir[x][y];
7762
7763   TurnRoundExt(x, y);
7764
7765   GfxDir[x][y] = MovDir[x][y];
7766
7767   if (direction != MovDir[x][y])
7768     GfxFrame[x][y] = 0;
7769
7770   if (MovDelay[x][y])
7771     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7772
7773   ResetGfxFrame(x, y);
7774 }
7775
7776 static boolean JustBeingPushed(int x, int y)
7777 {
7778   int i;
7779
7780   for (i = 0; i < MAX_PLAYERS; i++)
7781   {
7782     struct PlayerInfo *player = &stored_player[i];
7783
7784     if (player->active && player->is_pushing && player->MovPos)
7785     {
7786       int next_jx = player->jx + (player->jx - player->last_jx);
7787       int next_jy = player->jy + (player->jy - player->last_jy);
7788
7789       if (x == next_jx && y == next_jy)
7790         return TRUE;
7791     }
7792   }
7793
7794   return FALSE;
7795 }
7796
7797 static void StartMoving(int x, int y)
7798 {
7799   boolean started_moving = FALSE;       // some elements can fall _and_ move
7800   int element = Tile[x][y];
7801
7802   if (Stop[x][y])
7803     return;
7804
7805   if (MovDelay[x][y] == 0)
7806     GfxAction[x][y] = ACTION_DEFAULT;
7807
7808   if (CAN_FALL(element) && y < lev_fieldy - 1)
7809   {
7810     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7811         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7812       if (JustBeingPushed(x, y))
7813         return;
7814
7815     if (element == EL_QUICKSAND_FULL)
7816     {
7817       if (IS_FREE(x, y + 1))
7818       {
7819         InitMovingField(x, y, MV_DOWN);
7820         started_moving = TRUE;
7821
7822         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7823 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7824         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7825           Store[x][y] = EL_ROCK;
7826 #else
7827         Store[x][y] = EL_ROCK;
7828 #endif
7829
7830         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7831       }
7832       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7833       {
7834         if (!MovDelay[x][y])
7835         {
7836           MovDelay[x][y] = TILEY + 1;
7837
7838           ResetGfxAnimation(x, y);
7839           ResetGfxAnimation(x, y + 1);
7840         }
7841
7842         if (MovDelay[x][y])
7843         {
7844           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7845           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7846
7847           MovDelay[x][y]--;
7848           if (MovDelay[x][y])
7849             return;
7850         }
7851
7852         Tile[x][y] = EL_QUICKSAND_EMPTY;
7853         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7854         Store[x][y + 1] = Store[x][y];
7855         Store[x][y] = 0;
7856
7857         PlayLevelSoundAction(x, y, ACTION_FILLING);
7858       }
7859       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7860       {
7861         if (!MovDelay[x][y])
7862         {
7863           MovDelay[x][y] = TILEY + 1;
7864
7865           ResetGfxAnimation(x, y);
7866           ResetGfxAnimation(x, y + 1);
7867         }
7868
7869         if (MovDelay[x][y])
7870         {
7871           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7872           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7873
7874           MovDelay[x][y]--;
7875           if (MovDelay[x][y])
7876             return;
7877         }
7878
7879         Tile[x][y] = EL_QUICKSAND_EMPTY;
7880         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7881         Store[x][y + 1] = Store[x][y];
7882         Store[x][y] = 0;
7883
7884         PlayLevelSoundAction(x, y, ACTION_FILLING);
7885       }
7886     }
7887     else if (element == EL_QUICKSAND_FAST_FULL)
7888     {
7889       if (IS_FREE(x, y + 1))
7890       {
7891         InitMovingField(x, y, MV_DOWN);
7892         started_moving = TRUE;
7893
7894         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7895 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7896         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7897           Store[x][y] = EL_ROCK;
7898 #else
7899         Store[x][y] = EL_ROCK;
7900 #endif
7901
7902         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7903       }
7904       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7905       {
7906         if (!MovDelay[x][y])
7907         {
7908           MovDelay[x][y] = TILEY + 1;
7909
7910           ResetGfxAnimation(x, y);
7911           ResetGfxAnimation(x, y + 1);
7912         }
7913
7914         if (MovDelay[x][y])
7915         {
7916           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7917           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7918
7919           MovDelay[x][y]--;
7920           if (MovDelay[x][y])
7921             return;
7922         }
7923
7924         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7925         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7926         Store[x][y + 1] = Store[x][y];
7927         Store[x][y] = 0;
7928
7929         PlayLevelSoundAction(x, y, ACTION_FILLING);
7930       }
7931       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7932       {
7933         if (!MovDelay[x][y])
7934         {
7935           MovDelay[x][y] = TILEY + 1;
7936
7937           ResetGfxAnimation(x, y);
7938           ResetGfxAnimation(x, y + 1);
7939         }
7940
7941         if (MovDelay[x][y])
7942         {
7943           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7944           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7945
7946           MovDelay[x][y]--;
7947           if (MovDelay[x][y])
7948             return;
7949         }
7950
7951         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7952         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7953         Store[x][y + 1] = Store[x][y];
7954         Store[x][y] = 0;
7955
7956         PlayLevelSoundAction(x, y, ACTION_FILLING);
7957       }
7958     }
7959     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7960              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7961     {
7962       InitMovingField(x, y, MV_DOWN);
7963       started_moving = TRUE;
7964
7965       Tile[x][y] = EL_QUICKSAND_FILLING;
7966       Store[x][y] = element;
7967
7968       PlayLevelSoundAction(x, y, ACTION_FILLING);
7969     }
7970     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7971              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7972     {
7973       InitMovingField(x, y, MV_DOWN);
7974       started_moving = TRUE;
7975
7976       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7977       Store[x][y] = element;
7978
7979       PlayLevelSoundAction(x, y, ACTION_FILLING);
7980     }
7981     else if (element == EL_MAGIC_WALL_FULL)
7982     {
7983       if (IS_FREE(x, y + 1))
7984       {
7985         InitMovingField(x, y, MV_DOWN);
7986         started_moving = TRUE;
7987
7988         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7989         Store[x][y] = EL_CHANGED(Store[x][y]);
7990       }
7991       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7992       {
7993         if (!MovDelay[x][y])
7994           MovDelay[x][y] = TILEY / 4 + 1;
7995
7996         if (MovDelay[x][y])
7997         {
7998           MovDelay[x][y]--;
7999           if (MovDelay[x][y])
8000             return;
8001         }
8002
8003         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8004         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8005         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8006         Store[x][y] = 0;
8007       }
8008     }
8009     else if (element == EL_BD_MAGIC_WALL_FULL)
8010     {
8011       if (IS_FREE(x, y + 1))
8012       {
8013         InitMovingField(x, y, MV_DOWN);
8014         started_moving = TRUE;
8015
8016         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8017         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8018       }
8019       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8020       {
8021         if (!MovDelay[x][y])
8022           MovDelay[x][y] = TILEY / 4 + 1;
8023
8024         if (MovDelay[x][y])
8025         {
8026           MovDelay[x][y]--;
8027           if (MovDelay[x][y])
8028             return;
8029         }
8030
8031         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8032         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8033         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8034         Store[x][y] = 0;
8035       }
8036     }
8037     else if (element == EL_DC_MAGIC_WALL_FULL)
8038     {
8039       if (IS_FREE(x, y + 1))
8040       {
8041         InitMovingField(x, y, MV_DOWN);
8042         started_moving = TRUE;
8043
8044         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8045         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8046       }
8047       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8048       {
8049         if (!MovDelay[x][y])
8050           MovDelay[x][y] = TILEY / 4 + 1;
8051
8052         if (MovDelay[x][y])
8053         {
8054           MovDelay[x][y]--;
8055           if (MovDelay[x][y])
8056             return;
8057         }
8058
8059         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8060         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8061         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8062         Store[x][y] = 0;
8063       }
8064     }
8065     else if ((CAN_PASS_MAGIC_WALL(element) &&
8066               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8067                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8068              (CAN_PASS_DC_MAGIC_WALL(element) &&
8069               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8070
8071     {
8072       InitMovingField(x, y, MV_DOWN);
8073       started_moving = TRUE;
8074
8075       Tile[x][y] =
8076         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8077          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8078          EL_DC_MAGIC_WALL_FILLING);
8079       Store[x][y] = element;
8080     }
8081     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8082     {
8083       SplashAcid(x, y + 1);
8084
8085       InitMovingField(x, y, MV_DOWN);
8086       started_moving = TRUE;
8087
8088       Store[x][y] = EL_ACID;
8089     }
8090     else if (
8091              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8092               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8093              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8094               CAN_FALL(element) && WasJustFalling[x][y] &&
8095               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8096
8097              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8098               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8099               (Tile[x][y + 1] == EL_BLOCKED)))
8100     {
8101       /* this is needed for a special case not covered by calling "Impact()"
8102          from "ContinueMoving()": if an element moves to a tile directly below
8103          another element which was just falling on that tile (which was empty
8104          in the previous frame), the falling element above would just stop
8105          instead of smashing the element below (in previous version, the above
8106          element was just checked for "moving" instead of "falling", resulting
8107          in incorrect smashes caused by horizontal movement of the above
8108          element; also, the case of the player being the element to smash was
8109          simply not covered here... :-/ ) */
8110
8111       CheckCollision[x][y] = 0;
8112       CheckImpact[x][y] = 0;
8113
8114       Impact(x, y);
8115     }
8116     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8117     {
8118       if (MovDir[x][y] == MV_NONE)
8119       {
8120         InitMovingField(x, y, MV_DOWN);
8121         started_moving = TRUE;
8122       }
8123     }
8124     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8125     {
8126       if (WasJustFalling[x][y]) // prevent animation from being restarted
8127         MovDir[x][y] = MV_DOWN;
8128
8129       InitMovingField(x, y, MV_DOWN);
8130       started_moving = TRUE;
8131     }
8132     else if (element == EL_AMOEBA_DROP)
8133     {
8134       Tile[x][y] = EL_AMOEBA_GROWING;
8135       Store[x][y] = EL_AMOEBA_WET;
8136     }
8137     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8138               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8139              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8140              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8141     {
8142       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8143                                 (IS_FREE(x - 1, y + 1) ||
8144                                  Tile[x - 1][y + 1] == EL_ACID));
8145       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8146                                 (IS_FREE(x + 1, y + 1) ||
8147                                  Tile[x + 1][y + 1] == EL_ACID));
8148       boolean can_fall_any  = (can_fall_left || can_fall_right);
8149       boolean can_fall_both = (can_fall_left && can_fall_right);
8150       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8151
8152       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8153       {
8154         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8155           can_fall_right = FALSE;
8156         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8157           can_fall_left = FALSE;
8158         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8159           can_fall_right = FALSE;
8160         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8161           can_fall_left = FALSE;
8162
8163         can_fall_any  = (can_fall_left || can_fall_right);
8164         can_fall_both = FALSE;
8165       }
8166
8167       if (can_fall_both)
8168       {
8169         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8170           can_fall_right = FALSE;       // slip down on left side
8171         else
8172           can_fall_left = !(can_fall_right = RND(2));
8173
8174         can_fall_both = FALSE;
8175       }
8176
8177       if (can_fall_any)
8178       {
8179         // if not determined otherwise, prefer left side for slipping down
8180         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8181         started_moving = TRUE;
8182       }
8183     }
8184     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8185     {
8186       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8187       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8188       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8189       int belt_dir = game.belt_dir[belt_nr];
8190
8191       if ((belt_dir == MV_LEFT  && left_is_free) ||
8192           (belt_dir == MV_RIGHT && right_is_free))
8193       {
8194         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8195
8196         InitMovingField(x, y, belt_dir);
8197         started_moving = TRUE;
8198
8199         Pushed[x][y] = TRUE;
8200         Pushed[nextx][y] = TRUE;
8201
8202         GfxAction[x][y] = ACTION_DEFAULT;
8203       }
8204       else
8205       {
8206         MovDir[x][y] = 0;       // if element was moving, stop it
8207       }
8208     }
8209   }
8210
8211   // not "else if" because of elements that can fall and move (EL_SPRING)
8212   if (CAN_MOVE(element) && !started_moving)
8213   {
8214     int move_pattern = element_info[element].move_pattern;
8215     int newx, newy;
8216
8217     Moving2Blocked(x, y, &newx, &newy);
8218
8219     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8220       return;
8221
8222     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8223         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8224     {
8225       WasJustMoving[x][y] = 0;
8226       CheckCollision[x][y] = 0;
8227
8228       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8229
8230       if (Tile[x][y] != element)        // element has changed
8231         return;
8232     }
8233
8234     if (!MovDelay[x][y])        // start new movement phase
8235     {
8236       // all objects that can change their move direction after each step
8237       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8238
8239       if (element != EL_YAMYAM &&
8240           element != EL_DARK_YAMYAM &&
8241           element != EL_PACMAN &&
8242           !(move_pattern & MV_ANY_DIRECTION) &&
8243           move_pattern != MV_TURNING_LEFT &&
8244           move_pattern != MV_TURNING_RIGHT &&
8245           move_pattern != MV_TURNING_LEFT_RIGHT &&
8246           move_pattern != MV_TURNING_RIGHT_LEFT &&
8247           move_pattern != MV_TURNING_RANDOM)
8248       {
8249         TurnRound(x, y);
8250
8251         if (MovDelay[x][y] && (element == EL_BUG ||
8252                                element == EL_SPACESHIP ||
8253                                element == EL_SP_SNIKSNAK ||
8254                                element == EL_SP_ELECTRON ||
8255                                element == EL_MOLE))
8256           TEST_DrawLevelField(x, y);
8257       }
8258     }
8259
8260     if (MovDelay[x][y])         // wait some time before next movement
8261     {
8262       MovDelay[x][y]--;
8263
8264       if (element == EL_ROBOT ||
8265           element == EL_YAMYAM ||
8266           element == EL_DARK_YAMYAM)
8267       {
8268         DrawLevelElementAnimationIfNeeded(x, y, element);
8269         PlayLevelSoundAction(x, y, ACTION_WAITING);
8270       }
8271       else if (element == EL_SP_ELECTRON)
8272         DrawLevelElementAnimationIfNeeded(x, y, element);
8273       else if (element == EL_DRAGON)
8274       {
8275         int i;
8276         int dir = MovDir[x][y];
8277         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8278         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8279         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8280                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8281                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8282                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8283         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8284
8285         GfxAction[x][y] = ACTION_ATTACKING;
8286
8287         if (IS_PLAYER(x, y))
8288           DrawPlayerField(x, y);
8289         else
8290           TEST_DrawLevelField(x, y);
8291
8292         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8293
8294         for (i = 1; i <= 3; i++)
8295         {
8296           int xx = x + i * dx;
8297           int yy = y + i * dy;
8298           int sx = SCREENX(xx);
8299           int sy = SCREENY(yy);
8300           int flame_graphic = graphic + (i - 1);
8301
8302           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8303             break;
8304
8305           if (MovDelay[x][y])
8306           {
8307             int flamed = MovingOrBlocked2Element(xx, yy);
8308
8309             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8310               Bang(xx, yy);
8311             else
8312               RemoveMovingField(xx, yy);
8313
8314             ChangeDelay[xx][yy] = 0;
8315
8316             Tile[xx][yy] = EL_FLAMES;
8317
8318             if (IN_SCR_FIELD(sx, sy))
8319             {
8320               TEST_DrawLevelFieldCrumbled(xx, yy);
8321               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8322             }
8323           }
8324           else
8325           {
8326             if (Tile[xx][yy] == EL_FLAMES)
8327               Tile[xx][yy] = EL_EMPTY;
8328             TEST_DrawLevelField(xx, yy);
8329           }
8330         }
8331       }
8332
8333       if (MovDelay[x][y])       // element still has to wait some time
8334       {
8335         PlayLevelSoundAction(x, y, ACTION_WAITING);
8336
8337         return;
8338       }
8339     }
8340
8341     // now make next step
8342
8343     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8344
8345     if (DONT_COLLIDE_WITH(element) &&
8346         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8347         !PLAYER_ENEMY_PROTECTED(newx, newy))
8348     {
8349       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8350
8351       return;
8352     }
8353
8354     else if (CAN_MOVE_INTO_ACID(element) &&
8355              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8356              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8357              (MovDir[x][y] == MV_DOWN ||
8358               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8359     {
8360       SplashAcid(newx, newy);
8361       Store[x][y] = EL_ACID;
8362     }
8363     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8364     {
8365       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8366           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8367           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8368           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8369       {
8370         RemoveField(x, y);
8371         TEST_DrawLevelField(x, y);
8372
8373         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8374         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8375           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8376
8377         game.friends_still_needed--;
8378         if (!game.friends_still_needed &&
8379             !game.GameOver &&
8380             game.all_players_gone)
8381           LevelSolved();
8382
8383         return;
8384       }
8385       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8386       {
8387         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8388           TEST_DrawLevelField(newx, newy);
8389         else
8390           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8391       }
8392       else if (!IS_FREE(newx, newy))
8393       {
8394         GfxAction[x][y] = ACTION_WAITING;
8395
8396         if (IS_PLAYER(x, y))
8397           DrawPlayerField(x, y);
8398         else
8399           TEST_DrawLevelField(x, y);
8400
8401         return;
8402       }
8403     }
8404     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8405     {
8406       if (IS_FOOD_PIG(Tile[newx][newy]))
8407       {
8408         if (IS_MOVING(newx, newy))
8409           RemoveMovingField(newx, newy);
8410         else
8411         {
8412           Tile[newx][newy] = EL_EMPTY;
8413           TEST_DrawLevelField(newx, newy);
8414         }
8415
8416         PlayLevelSound(x, y, SND_PIG_DIGGING);
8417       }
8418       else if (!IS_FREE(newx, newy))
8419       {
8420         if (IS_PLAYER(x, y))
8421           DrawPlayerField(x, y);
8422         else
8423           TEST_DrawLevelField(x, y);
8424
8425         return;
8426       }
8427     }
8428     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8429     {
8430       if (Store[x][y] != EL_EMPTY)
8431       {
8432         boolean can_clone = FALSE;
8433         int xx, yy;
8434
8435         // check if element to clone is still there
8436         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8437         {
8438           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8439           {
8440             can_clone = TRUE;
8441
8442             break;
8443           }
8444         }
8445
8446         // cannot clone or target field not free anymore -- do not clone
8447         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8448           Store[x][y] = EL_EMPTY;
8449       }
8450
8451       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8452       {
8453         if (IS_MV_DIAGONAL(MovDir[x][y]))
8454         {
8455           int diagonal_move_dir = MovDir[x][y];
8456           int stored = Store[x][y];
8457           int change_delay = 8;
8458           int graphic;
8459
8460           // android is moving diagonally
8461
8462           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8463
8464           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8465           GfxElement[x][y] = EL_EMC_ANDROID;
8466           GfxAction[x][y] = ACTION_SHRINKING;
8467           GfxDir[x][y] = diagonal_move_dir;
8468           ChangeDelay[x][y] = change_delay;
8469
8470           if (Store[x][y] == EL_EMPTY)
8471             Store[x][y] = GfxElementEmpty[x][y];
8472
8473           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8474                                    GfxDir[x][y]);
8475
8476           DrawLevelGraphicAnimation(x, y, graphic);
8477           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8478
8479           if (Tile[newx][newy] == EL_ACID)
8480           {
8481             SplashAcid(newx, newy);
8482
8483             return;
8484           }
8485
8486           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8487
8488           Store[newx][newy] = EL_EMC_ANDROID;
8489           GfxElement[newx][newy] = EL_EMC_ANDROID;
8490           GfxAction[newx][newy] = ACTION_GROWING;
8491           GfxDir[newx][newy] = diagonal_move_dir;
8492           ChangeDelay[newx][newy] = change_delay;
8493
8494           graphic = el_act_dir2img(GfxElement[newx][newy],
8495                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8496
8497           DrawLevelGraphicAnimation(newx, newy, graphic);
8498           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8499
8500           return;
8501         }
8502         else
8503         {
8504           Tile[newx][newy] = EL_EMPTY;
8505           TEST_DrawLevelField(newx, newy);
8506
8507           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8508         }
8509       }
8510       else if (!IS_FREE(newx, newy))
8511       {
8512         return;
8513       }
8514     }
8515     else if (IS_CUSTOM_ELEMENT(element) &&
8516              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8517     {
8518       if (!DigFieldByCE(newx, newy, element))
8519         return;
8520
8521       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8522       {
8523         RunnerVisit[x][y] = FrameCounter;
8524         PlayerVisit[x][y] /= 8;         // expire player visit path
8525       }
8526     }
8527     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8528     {
8529       if (!IS_FREE(newx, newy))
8530       {
8531         if (IS_PLAYER(x, y))
8532           DrawPlayerField(x, y);
8533         else
8534           TEST_DrawLevelField(x, y);
8535
8536         return;
8537       }
8538       else
8539       {
8540         boolean wanna_flame = !RND(10);
8541         int dx = newx - x, dy = newy - y;
8542         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8543         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8544         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8545                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8546         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8547                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8548
8549         if ((wanna_flame ||
8550              IS_CLASSIC_ENEMY(element1) ||
8551              IS_CLASSIC_ENEMY(element2)) &&
8552             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8553             element1 != EL_FLAMES && element2 != EL_FLAMES)
8554         {
8555           ResetGfxAnimation(x, y);
8556           GfxAction[x][y] = ACTION_ATTACKING;
8557
8558           if (IS_PLAYER(x, y))
8559             DrawPlayerField(x, y);
8560           else
8561             TEST_DrawLevelField(x, y);
8562
8563           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8564
8565           MovDelay[x][y] = 50;
8566
8567           Tile[newx][newy] = EL_FLAMES;
8568           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8569             Tile[newx1][newy1] = EL_FLAMES;
8570           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8571             Tile[newx2][newy2] = EL_FLAMES;
8572
8573           return;
8574         }
8575       }
8576     }
8577     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8578              Tile[newx][newy] == EL_DIAMOND)
8579     {
8580       if (IS_MOVING(newx, newy))
8581         RemoveMovingField(newx, newy);
8582       else
8583       {
8584         Tile[newx][newy] = EL_EMPTY;
8585         TEST_DrawLevelField(newx, newy);
8586       }
8587
8588       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8589     }
8590     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8591              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8592     {
8593       if (AmoebaNr[newx][newy])
8594       {
8595         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8596         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8597             Tile[newx][newy] == EL_BD_AMOEBA)
8598           AmoebaCnt[AmoebaNr[newx][newy]]--;
8599       }
8600
8601       if (IS_MOVING(newx, newy))
8602       {
8603         RemoveMovingField(newx, newy);
8604       }
8605       else
8606       {
8607         Tile[newx][newy] = EL_EMPTY;
8608         TEST_DrawLevelField(newx, newy);
8609       }
8610
8611       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8612     }
8613     else if ((element == EL_PACMAN || element == EL_MOLE)
8614              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8615     {
8616       if (AmoebaNr[newx][newy])
8617       {
8618         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8619         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8620             Tile[newx][newy] == EL_BD_AMOEBA)
8621           AmoebaCnt[AmoebaNr[newx][newy]]--;
8622       }
8623
8624       if (element == EL_MOLE)
8625       {
8626         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8627         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8628
8629         ResetGfxAnimation(x, y);
8630         GfxAction[x][y] = ACTION_DIGGING;
8631         TEST_DrawLevelField(x, y);
8632
8633         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8634
8635         return;                         // wait for shrinking amoeba
8636       }
8637       else      // element == EL_PACMAN
8638       {
8639         Tile[newx][newy] = EL_EMPTY;
8640         TEST_DrawLevelField(newx, newy);
8641         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8642       }
8643     }
8644     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8645              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8646               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8647     {
8648       // wait for shrinking amoeba to completely disappear
8649       return;
8650     }
8651     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8652     {
8653       // object was running against a wall
8654
8655       TurnRound(x, y);
8656
8657       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8658         DrawLevelElementAnimation(x, y, element);
8659
8660       if (DONT_TOUCH(element))
8661         TestIfBadThingTouchesPlayer(x, y);
8662
8663       return;
8664     }
8665
8666     InitMovingField(x, y, MovDir[x][y]);
8667
8668     PlayLevelSoundAction(x, y, ACTION_MOVING);
8669   }
8670
8671   if (MovDir[x][y])
8672     ContinueMoving(x, y);
8673 }
8674
8675 void ContinueMoving(int x, int y)
8676 {
8677   int element = Tile[x][y];
8678   struct ElementInfo *ei = &element_info[element];
8679   int direction = MovDir[x][y];
8680   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8681   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8682   int newx = x + dx, newy = y + dy;
8683   int stored = Store[x][y];
8684   int stored_new = Store[newx][newy];
8685   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8686   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8687   boolean last_line = (newy == lev_fieldy - 1);
8688   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8689
8690   if (pushed_by_player)         // special case: moving object pushed by player
8691   {
8692     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8693   }
8694   else if (use_step_delay)      // special case: moving object has step delay
8695   {
8696     if (!MovDelay[x][y])
8697       MovPos[x][y] += getElementMoveStepsize(x, y);
8698
8699     if (MovDelay[x][y])
8700       MovDelay[x][y]--;
8701     else
8702       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8703
8704     if (MovDelay[x][y])
8705     {
8706       TEST_DrawLevelField(x, y);
8707
8708       return;   // element is still waiting
8709     }
8710   }
8711   else                          // normal case: generically moving object
8712   {
8713     MovPos[x][y] += getElementMoveStepsize(x, y);
8714   }
8715
8716   if (ABS(MovPos[x][y]) < TILEX)
8717   {
8718     TEST_DrawLevelField(x, y);
8719
8720     return;     // element is still moving
8721   }
8722
8723   // element reached destination field
8724
8725   Tile[x][y] = EL_EMPTY;
8726   Tile[newx][newy] = element;
8727   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8728
8729   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8730   {
8731     element = Tile[newx][newy] = EL_ACID;
8732   }
8733   else if (element == EL_MOLE)
8734   {
8735     Tile[x][y] = EL_SAND;
8736
8737     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8738   }
8739   else if (element == EL_QUICKSAND_FILLING)
8740   {
8741     element = Tile[newx][newy] = get_next_element(element);
8742     Store[newx][newy] = Store[x][y];
8743   }
8744   else if (element == EL_QUICKSAND_EMPTYING)
8745   {
8746     Tile[x][y] = get_next_element(element);
8747     element = Tile[newx][newy] = Store[x][y];
8748   }
8749   else if (element == EL_QUICKSAND_FAST_FILLING)
8750   {
8751     element = Tile[newx][newy] = get_next_element(element);
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     element = Tile[newx][newy] = Store[x][y];
8758   }
8759   else if (element == EL_MAGIC_WALL_FILLING)
8760   {
8761     element = Tile[newx][newy] = get_next_element(element);
8762     if (!game.magic_wall_active)
8763       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8764     Store[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_MAGIC_WALL_EMPTYING)
8767   {
8768     Tile[x][y] = get_next_element(element);
8769     if (!game.magic_wall_active)
8770       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8771     element = Tile[newx][newy] = Store[x][y];
8772
8773     InitField(newx, newy, FALSE);
8774   }
8775   else if (element == EL_BD_MAGIC_WALL_FILLING)
8776   {
8777     element = Tile[newx][newy] = get_next_element(element);
8778     if (!game.magic_wall_active)
8779       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8780     Store[newx][newy] = Store[x][y];
8781   }
8782   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8783   {
8784     Tile[x][y] = get_next_element(element);
8785     if (!game.magic_wall_active)
8786       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8787     element = Tile[newx][newy] = Store[x][y];
8788
8789     InitField(newx, newy, FALSE);
8790   }
8791   else if (element == EL_DC_MAGIC_WALL_FILLING)
8792   {
8793     element = Tile[newx][newy] = get_next_element(element);
8794     if (!game.magic_wall_active)
8795       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8796     Store[newx][newy] = Store[x][y];
8797   }
8798   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8799   {
8800     Tile[x][y] = get_next_element(element);
8801     if (!game.magic_wall_active)
8802       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8803     element = Tile[newx][newy] = Store[x][y];
8804
8805     InitField(newx, newy, FALSE);
8806   }
8807   else if (element == EL_AMOEBA_DROPPING)
8808   {
8809     Tile[x][y] = get_next_element(element);
8810     element = Tile[newx][newy] = Store[x][y];
8811   }
8812   else if (element == EL_SOKOBAN_OBJECT)
8813   {
8814     if (Back[x][y])
8815       Tile[x][y] = Back[x][y];
8816
8817     if (Back[newx][newy])
8818       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8819
8820     Back[x][y] = Back[newx][newy] = 0;
8821   }
8822
8823   Store[x][y] = EL_EMPTY;
8824   MovPos[x][y] = 0;
8825   MovDir[x][y] = 0;
8826   MovDelay[x][y] = 0;
8827
8828   MovDelay[newx][newy] = 0;
8829
8830   if (CAN_CHANGE_OR_HAS_ACTION(element))
8831   {
8832     // copy element change control values to new field
8833     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8834     ChangePage[newx][newy]  = ChangePage[x][y];
8835     ChangeCount[newx][newy] = ChangeCount[x][y];
8836     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8837   }
8838
8839   CustomValue[newx][newy] = CustomValue[x][y];
8840
8841   ChangeDelay[x][y] = 0;
8842   ChangePage[x][y] = -1;
8843   ChangeCount[x][y] = 0;
8844   ChangeEvent[x][y] = -1;
8845
8846   CustomValue[x][y] = 0;
8847
8848   // copy animation control values to new field
8849   GfxFrame[newx][newy]  = GfxFrame[x][y];
8850   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8851   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8852   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8853
8854   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8855
8856   // some elements can leave other elements behind after moving
8857   if (ei->move_leave_element != EL_EMPTY &&
8858       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8859       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8860   {
8861     int move_leave_element = ei->move_leave_element;
8862
8863     // this makes it possible to leave the removed element again
8864     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8865       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8866
8867     Tile[x][y] = move_leave_element;
8868
8869     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8870       MovDir[x][y] = direction;
8871
8872     InitField(x, y, FALSE);
8873
8874     if (GFX_CRUMBLED(Tile[x][y]))
8875       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8876
8877     if (IS_PLAYER_ELEMENT(move_leave_element))
8878       RelocatePlayer(x, y, move_leave_element);
8879   }
8880
8881   // do this after checking for left-behind element
8882   ResetGfxAnimation(x, y);      // reset animation values for old field
8883
8884   if (!CAN_MOVE(element) ||
8885       (CAN_FALL(element) && direction == MV_DOWN &&
8886        (element == EL_SPRING ||
8887         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8888         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8889     GfxDir[x][y] = MovDir[newx][newy] = 0;
8890
8891   TEST_DrawLevelField(x, y);
8892   TEST_DrawLevelField(newx, newy);
8893
8894   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8895
8896   // prevent pushed element from moving on in pushed direction
8897   if (pushed_by_player && CAN_MOVE(element) &&
8898       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8899       !(element_info[element].move_pattern & direction))
8900     TurnRound(newx, newy);
8901
8902   // prevent elements on conveyor belt from moving on in last direction
8903   if (pushed_by_conveyor && CAN_FALL(element) &&
8904       direction & MV_HORIZONTAL)
8905     MovDir[newx][newy] = 0;
8906
8907   if (!pushed_by_player)
8908   {
8909     int nextx = newx + dx, nexty = newy + dy;
8910     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8911
8912     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8913
8914     if (CAN_FALL(element) && direction == MV_DOWN)
8915       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8916
8917     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8918       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8919
8920     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8921       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8922   }
8923
8924   if (DONT_TOUCH(element))      // object may be nasty to player or others
8925   {
8926     TestIfBadThingTouchesPlayer(newx, newy);
8927     TestIfBadThingTouchesFriend(newx, newy);
8928
8929     if (!IS_CUSTOM_ELEMENT(element))
8930       TestIfBadThingTouchesOtherBadThing(newx, newy);
8931   }
8932   else if (element == EL_PENGUIN)
8933     TestIfFriendTouchesBadThing(newx, newy);
8934
8935   if (DONT_GET_HIT_BY(element))
8936   {
8937     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8938   }
8939
8940   // give the player one last chance (one more frame) to move away
8941   if (CAN_FALL(element) && direction == MV_DOWN &&
8942       (last_line || (!IS_FREE(x, newy + 1) &&
8943                      (!IS_PLAYER(x, newy + 1) ||
8944                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8945     Impact(x, newy);
8946
8947   if (pushed_by_player && !game.use_change_when_pushing_bug)
8948   {
8949     int push_side = MV_DIR_OPPOSITE(direction);
8950     struct PlayerInfo *player = PLAYERINFO(x, y);
8951
8952     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8953                                player->index_bit, push_side);
8954     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8955                                         player->index_bit, push_side);
8956   }
8957
8958   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8959     MovDelay[newx][newy] = 1;
8960
8961   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8962
8963   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8964   TestIfElementHitsCustomElement(newx, newy, direction);
8965   TestIfPlayerTouchesCustomElement(newx, newy);
8966   TestIfElementTouchesCustomElement(newx, newy);
8967
8968   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8969       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8970     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8971                              MV_DIR_OPPOSITE(direction));
8972 }
8973
8974 int AmoebaNeighbourNr(int ax, int ay)
8975 {
8976   int i;
8977   int element = Tile[ax][ay];
8978   int group_nr = 0;
8979   struct XY *xy = xy_topdown;
8980
8981   for (i = 0; i < NUM_DIRECTIONS; i++)
8982   {
8983     int x = ax + xy[i].x;
8984     int y = ay + xy[i].y;
8985
8986     if (!IN_LEV_FIELD(x, y))
8987       continue;
8988
8989     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8990       group_nr = AmoebaNr[x][y];
8991   }
8992
8993   return group_nr;
8994 }
8995
8996 static void AmoebaMerge(int ax, int ay)
8997 {
8998   int i, x, y, xx, yy;
8999   int new_group_nr = AmoebaNr[ax][ay];
9000   struct XY *xy = xy_topdown;
9001
9002   if (new_group_nr == 0)
9003     return;
9004
9005   for (i = 0; i < NUM_DIRECTIONS; i++)
9006   {
9007     x = ax + xy[i].x;
9008     y = ay + xy[i].y;
9009
9010     if (!IN_LEV_FIELD(x, y))
9011       continue;
9012
9013     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9014          Tile[x][y] == EL_BD_AMOEBA ||
9015          Tile[x][y] == EL_AMOEBA_DEAD) &&
9016         AmoebaNr[x][y] != new_group_nr)
9017     {
9018       int old_group_nr = AmoebaNr[x][y];
9019
9020       if (old_group_nr == 0)
9021         return;
9022
9023       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9024       AmoebaCnt[old_group_nr] = 0;
9025       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9026       AmoebaCnt2[old_group_nr] = 0;
9027
9028       SCAN_PLAYFIELD(xx, yy)
9029       {
9030         if (AmoebaNr[xx][yy] == old_group_nr)
9031           AmoebaNr[xx][yy] = new_group_nr;
9032       }
9033     }
9034   }
9035 }
9036
9037 void AmoebaToDiamond(int ax, int ay)
9038 {
9039   int i, x, y;
9040
9041   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9042   {
9043     int group_nr = AmoebaNr[ax][ay];
9044
9045 #ifdef DEBUG
9046     if (group_nr == 0)
9047     {
9048       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9049       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9050
9051       return;
9052     }
9053 #endif
9054
9055     SCAN_PLAYFIELD(x, y)
9056     {
9057       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9058       {
9059         AmoebaNr[x][y] = 0;
9060         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9061       }
9062     }
9063
9064     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9065                             SND_AMOEBA_TURNING_TO_GEM :
9066                             SND_AMOEBA_TURNING_TO_ROCK));
9067     Bang(ax, ay);
9068   }
9069   else
9070   {
9071     struct XY *xy = xy_topdown;
9072
9073     for (i = 0; i < NUM_DIRECTIONS; i++)
9074     {
9075       x = ax + xy[i].x;
9076       y = ay + xy[i].y;
9077
9078       if (!IN_LEV_FIELD(x, y))
9079         continue;
9080
9081       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9082       {
9083         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9084                               SND_AMOEBA_TURNING_TO_GEM :
9085                               SND_AMOEBA_TURNING_TO_ROCK));
9086         Bang(x, y);
9087       }
9088     }
9089   }
9090 }
9091
9092 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9093 {
9094   int x, y;
9095   int group_nr = AmoebaNr[ax][ay];
9096   boolean done = FALSE;
9097
9098 #ifdef DEBUG
9099   if (group_nr == 0)
9100   {
9101     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9102     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9103
9104     return;
9105   }
9106 #endif
9107
9108   SCAN_PLAYFIELD(x, y)
9109   {
9110     if (AmoebaNr[x][y] == group_nr &&
9111         (Tile[x][y] == EL_AMOEBA_DEAD ||
9112          Tile[x][y] == EL_BD_AMOEBA ||
9113          Tile[x][y] == EL_AMOEBA_GROWING))
9114     {
9115       AmoebaNr[x][y] = 0;
9116       Tile[x][y] = new_element;
9117       InitField(x, y, FALSE);
9118       TEST_DrawLevelField(x, y);
9119       done = TRUE;
9120     }
9121   }
9122
9123   if (done)
9124     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9125                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9126                             SND_BD_AMOEBA_TURNING_TO_GEM));
9127 }
9128
9129 static void AmoebaGrowing(int x, int y)
9130 {
9131   static DelayCounter sound_delay = { 0 };
9132
9133   if (!MovDelay[x][y])          // start new growing cycle
9134   {
9135     MovDelay[x][y] = 7;
9136
9137     if (DelayReached(&sound_delay))
9138     {
9139       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9140       sound_delay.value = 30;
9141     }
9142   }
9143
9144   if (MovDelay[x][y])           // wait some time before growing bigger
9145   {
9146     MovDelay[x][y]--;
9147     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9148     {
9149       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9150                                            6 - MovDelay[x][y]);
9151
9152       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9153     }
9154
9155     if (!MovDelay[x][y])
9156     {
9157       Tile[x][y] = Store[x][y];
9158       Store[x][y] = 0;
9159       TEST_DrawLevelField(x, y);
9160     }
9161   }
9162 }
9163
9164 static void AmoebaShrinking(int x, int y)
9165 {
9166   static DelayCounter sound_delay = { 0 };
9167
9168   if (!MovDelay[x][y])          // start new shrinking cycle
9169   {
9170     MovDelay[x][y] = 7;
9171
9172     if (DelayReached(&sound_delay))
9173       sound_delay.value = 30;
9174   }
9175
9176   if (MovDelay[x][y])           // wait some time before shrinking
9177   {
9178     MovDelay[x][y]--;
9179     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9180     {
9181       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9182                                            6 - MovDelay[x][y]);
9183
9184       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9185     }
9186
9187     if (!MovDelay[x][y])
9188     {
9189       Tile[x][y] = EL_EMPTY;
9190       TEST_DrawLevelField(x, y);
9191
9192       // don't let mole enter this field in this cycle;
9193       // (give priority to objects falling to this field from above)
9194       Stop[x][y] = TRUE;
9195     }
9196   }
9197 }
9198
9199 static void AmoebaReproduce(int ax, int ay)
9200 {
9201   int i;
9202   int element = Tile[ax][ay];
9203   int graphic = el2img(element);
9204   int newax = ax, neway = ay;
9205   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9206   struct XY *xy = xy_topdown;
9207
9208   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9209   {
9210     Tile[ax][ay] = EL_AMOEBA_DEAD;
9211     TEST_DrawLevelField(ax, ay);
9212     return;
9213   }
9214
9215   if (IS_ANIMATED(graphic))
9216     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9217
9218   if (!MovDelay[ax][ay])        // start making new amoeba field
9219     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9220
9221   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9222   {
9223     MovDelay[ax][ay]--;
9224     if (MovDelay[ax][ay])
9225       return;
9226   }
9227
9228   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9229   {
9230     int start = RND(4);
9231     int x = ax + xy[start].x;
9232     int y = ay + xy[start].y;
9233
9234     if (!IN_LEV_FIELD(x, y))
9235       return;
9236
9237     if (IS_FREE(x, y) ||
9238         CAN_GROW_INTO(Tile[x][y]) ||
9239         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9240         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9241     {
9242       newax = x;
9243       neway = y;
9244     }
9245
9246     if (newax == ax && neway == ay)
9247       return;
9248   }
9249   else                          // normal or "filled" (BD style) amoeba
9250   {
9251     int start = RND(4);
9252     boolean waiting_for_player = FALSE;
9253
9254     for (i = 0; i < NUM_DIRECTIONS; i++)
9255     {
9256       int j = (start + i) % 4;
9257       int x = ax + xy[j].x;
9258       int y = ay + xy[j].y;
9259
9260       if (!IN_LEV_FIELD(x, y))
9261         continue;
9262
9263       if (IS_FREE(x, y) ||
9264           CAN_GROW_INTO(Tile[x][y]) ||
9265           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9266           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9267       {
9268         newax = x;
9269         neway = y;
9270         break;
9271       }
9272       else if (IS_PLAYER(x, y))
9273         waiting_for_player = TRUE;
9274     }
9275
9276     if (newax == ax && neway == ay)             // amoeba cannot grow
9277     {
9278       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9279       {
9280         Tile[ax][ay] = EL_AMOEBA_DEAD;
9281         TEST_DrawLevelField(ax, ay);
9282         AmoebaCnt[AmoebaNr[ax][ay]]--;
9283
9284         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9285         {
9286           if (element == EL_AMOEBA_FULL)
9287             AmoebaToDiamond(ax, ay);
9288           else if (element == EL_BD_AMOEBA)
9289             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9290         }
9291       }
9292       return;
9293     }
9294     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9295     {
9296       // amoeba gets larger by growing in some direction
9297
9298       int new_group_nr = AmoebaNr[ax][ay];
9299
9300 #ifdef DEBUG
9301   if (new_group_nr == 0)
9302   {
9303     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9304           newax, neway);
9305     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9306
9307     return;
9308   }
9309 #endif
9310
9311       AmoebaNr[newax][neway] = new_group_nr;
9312       AmoebaCnt[new_group_nr]++;
9313       AmoebaCnt2[new_group_nr]++;
9314
9315       // if amoeba touches other amoeba(s) after growing, unify them
9316       AmoebaMerge(newax, neway);
9317
9318       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9319       {
9320         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9321         return;
9322       }
9323     }
9324   }
9325
9326   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9327       (neway == lev_fieldy - 1 && newax != ax))
9328   {
9329     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9330     Store[newax][neway] = element;
9331   }
9332   else if (neway == ay || element == EL_EMC_DRIPPER)
9333   {
9334     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9335
9336     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9337   }
9338   else
9339   {
9340     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9341     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9342     Store[ax][ay] = EL_AMOEBA_DROP;
9343     ContinueMoving(ax, ay);
9344     return;
9345   }
9346
9347   TEST_DrawLevelField(newax, neway);
9348 }
9349
9350 static void Life(int ax, int ay)
9351 {
9352   int x1, y1, x2, y2;
9353   int life_time = 40;
9354   int element = Tile[ax][ay];
9355   int graphic = el2img(element);
9356   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9357                          level.biomaze);
9358   boolean changed = FALSE;
9359
9360   if (IS_ANIMATED(graphic))
9361     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9362
9363   if (Stop[ax][ay])
9364     return;
9365
9366   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9367     MovDelay[ax][ay] = life_time;
9368
9369   if (MovDelay[ax][ay])         // wait some time before next cycle
9370   {
9371     MovDelay[ax][ay]--;
9372     if (MovDelay[ax][ay])
9373       return;
9374   }
9375
9376   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9377   {
9378     int xx = ax+x1, yy = ay+y1;
9379     int old_element = Tile[xx][yy];
9380     int num_neighbours = 0;
9381
9382     if (!IN_LEV_FIELD(xx, yy))
9383       continue;
9384
9385     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9386     {
9387       int x = xx+x2, y = yy+y2;
9388
9389       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9390         continue;
9391
9392       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9393       boolean is_neighbour = FALSE;
9394
9395       if (level.use_life_bugs)
9396         is_neighbour =
9397           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9398            (IS_FREE(x, y)                             &&  Stop[x][y]));
9399       else
9400         is_neighbour =
9401           (Last[x][y] == element || is_player_cell);
9402
9403       if (is_neighbour)
9404         num_neighbours++;
9405     }
9406
9407     boolean is_free = FALSE;
9408
9409     if (level.use_life_bugs)
9410       is_free = (IS_FREE(xx, yy));
9411     else
9412       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9413
9414     if (xx == ax && yy == ay)           // field in the middle
9415     {
9416       if (num_neighbours < life_parameter[0] ||
9417           num_neighbours > life_parameter[1])
9418       {
9419         Tile[xx][yy] = EL_EMPTY;
9420         if (Tile[xx][yy] != old_element)
9421           TEST_DrawLevelField(xx, yy);
9422         Stop[xx][yy] = TRUE;
9423         changed = TRUE;
9424       }
9425     }
9426     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9427     {                                   // free border field
9428       if (num_neighbours >= life_parameter[2] &&
9429           num_neighbours <= life_parameter[3])
9430       {
9431         Tile[xx][yy] = element;
9432         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9433         if (Tile[xx][yy] != old_element)
9434           TEST_DrawLevelField(xx, yy);
9435         Stop[xx][yy] = TRUE;
9436         changed = TRUE;
9437       }
9438     }
9439   }
9440
9441   if (changed)
9442     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9443                    SND_GAME_OF_LIFE_GROWING);
9444 }
9445
9446 static void InitRobotWheel(int x, int y)
9447 {
9448   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9449 }
9450
9451 static void RunRobotWheel(int x, int y)
9452 {
9453   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9454 }
9455
9456 static void StopRobotWheel(int x, int y)
9457 {
9458   if (game.robot_wheel_x == x &&
9459       game.robot_wheel_y == y)
9460   {
9461     game.robot_wheel_x = -1;
9462     game.robot_wheel_y = -1;
9463     game.robot_wheel_active = FALSE;
9464   }
9465 }
9466
9467 static void InitTimegateWheel(int x, int y)
9468 {
9469   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9470 }
9471
9472 static void RunTimegateWheel(int x, int y)
9473 {
9474   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9475 }
9476
9477 static void InitMagicBallDelay(int x, int y)
9478 {
9479   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9480 }
9481
9482 static void ActivateMagicBall(int bx, int by)
9483 {
9484   int x, y;
9485
9486   if (level.ball_random)
9487   {
9488     int pos_border = RND(8);    // select one of the eight border elements
9489     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9490     int xx = pos_content % 3;
9491     int yy = pos_content / 3;
9492
9493     x = bx - 1 + xx;
9494     y = by - 1 + yy;
9495
9496     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9497       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9498   }
9499   else
9500   {
9501     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9502     {
9503       int xx = x - bx + 1;
9504       int yy = y - by + 1;
9505
9506       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9507         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9508     }
9509   }
9510
9511   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9512 }
9513
9514 static void CheckExit(int x, int y)
9515 {
9516   if (game.gems_still_needed > 0 ||
9517       game.sokoban_fields_still_needed > 0 ||
9518       game.sokoban_objects_still_needed > 0 ||
9519       game.lights_still_needed > 0)
9520   {
9521     int element = Tile[x][y];
9522     int graphic = el2img(element);
9523
9524     if (IS_ANIMATED(graphic))
9525       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9526
9527     return;
9528   }
9529
9530   // do not re-open exit door closed after last player
9531   if (game.all_players_gone)
9532     return;
9533
9534   Tile[x][y] = EL_EXIT_OPENING;
9535
9536   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9537 }
9538
9539 static void CheckExitEM(int x, int y)
9540 {
9541   if (game.gems_still_needed > 0 ||
9542       game.sokoban_fields_still_needed > 0 ||
9543       game.sokoban_objects_still_needed > 0 ||
9544       game.lights_still_needed > 0)
9545   {
9546     int element = Tile[x][y];
9547     int graphic = el2img(element);
9548
9549     if (IS_ANIMATED(graphic))
9550       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9551
9552     return;
9553   }
9554
9555   // do not re-open exit door closed after last player
9556   if (game.all_players_gone)
9557     return;
9558
9559   Tile[x][y] = EL_EM_EXIT_OPENING;
9560
9561   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9562 }
9563
9564 static void CheckExitSteel(int x, int y)
9565 {
9566   if (game.gems_still_needed > 0 ||
9567       game.sokoban_fields_still_needed > 0 ||
9568       game.sokoban_objects_still_needed > 0 ||
9569       game.lights_still_needed > 0)
9570   {
9571     int element = Tile[x][y];
9572     int graphic = el2img(element);
9573
9574     if (IS_ANIMATED(graphic))
9575       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9576
9577     return;
9578   }
9579
9580   // do not re-open exit door closed after last player
9581   if (game.all_players_gone)
9582     return;
9583
9584   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9585
9586   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9587 }
9588
9589 static void CheckExitSteelEM(int x, int y)
9590 {
9591   if (game.gems_still_needed > 0 ||
9592       game.sokoban_fields_still_needed > 0 ||
9593       game.sokoban_objects_still_needed > 0 ||
9594       game.lights_still_needed > 0)
9595   {
9596     int element = Tile[x][y];
9597     int graphic = el2img(element);
9598
9599     if (IS_ANIMATED(graphic))
9600       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9601
9602     return;
9603   }
9604
9605   // do not re-open exit door closed after last player
9606   if (game.all_players_gone)
9607     return;
9608
9609   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9610
9611   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9612 }
9613
9614 static void CheckExitSP(int x, int y)
9615 {
9616   if (game.gems_still_needed > 0)
9617   {
9618     int element = Tile[x][y];
9619     int graphic = el2img(element);
9620
9621     if (IS_ANIMATED(graphic))
9622       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9623
9624     return;
9625   }
9626
9627   // do not re-open exit door closed after last player
9628   if (game.all_players_gone)
9629     return;
9630
9631   Tile[x][y] = EL_SP_EXIT_OPENING;
9632
9633   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9634 }
9635
9636 static void CloseAllOpenTimegates(void)
9637 {
9638   int x, y;
9639
9640   SCAN_PLAYFIELD(x, y)
9641   {
9642     int element = Tile[x][y];
9643
9644     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9645     {
9646       Tile[x][y] = EL_TIMEGATE_CLOSING;
9647
9648       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9649     }
9650   }
9651 }
9652
9653 static void DrawTwinkleOnField(int x, int y)
9654 {
9655   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9656     return;
9657
9658   if (Tile[x][y] == EL_BD_DIAMOND)
9659     return;
9660
9661   if (MovDelay[x][y] == 0)      // next animation frame
9662     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9663
9664   if (MovDelay[x][y] != 0)      // wait some time before next frame
9665   {
9666     MovDelay[x][y]--;
9667
9668     DrawLevelElementAnimation(x, y, Tile[x][y]);
9669
9670     if (MovDelay[x][y] != 0)
9671     {
9672       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9673                                            10 - MovDelay[x][y]);
9674
9675       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9676     }
9677   }
9678 }
9679
9680 static void WallGrowing(int x, int y)
9681 {
9682   int delay = 6;
9683
9684   if (!MovDelay[x][y])          // next animation frame
9685     MovDelay[x][y] = 3 * delay;
9686
9687   if (MovDelay[x][y])           // wait some time before next frame
9688   {
9689     MovDelay[x][y]--;
9690
9691     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9692     {
9693       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9694       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9695
9696       DrawLevelGraphic(x, y, graphic, frame);
9697     }
9698
9699     if (!MovDelay[x][y])
9700     {
9701       if (MovDir[x][y] == MV_LEFT)
9702       {
9703         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9704           TEST_DrawLevelField(x - 1, y);
9705       }
9706       else if (MovDir[x][y] == MV_RIGHT)
9707       {
9708         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9709           TEST_DrawLevelField(x + 1, y);
9710       }
9711       else if (MovDir[x][y] == MV_UP)
9712       {
9713         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9714           TEST_DrawLevelField(x, y - 1);
9715       }
9716       else
9717       {
9718         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9719           TEST_DrawLevelField(x, y + 1);
9720       }
9721
9722       Tile[x][y] = Store[x][y];
9723       Store[x][y] = 0;
9724       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9725       TEST_DrawLevelField(x, y);
9726     }
9727   }
9728 }
9729
9730 static void CheckWallGrowing(int ax, int ay)
9731 {
9732   int element = Tile[ax][ay];
9733   int graphic = el2img(element);
9734   boolean free_top    = FALSE;
9735   boolean free_bottom = FALSE;
9736   boolean free_left   = FALSE;
9737   boolean free_right  = FALSE;
9738   boolean stop_top    = FALSE;
9739   boolean stop_bottom = FALSE;
9740   boolean stop_left   = FALSE;
9741   boolean stop_right  = FALSE;
9742   boolean new_wall    = FALSE;
9743
9744   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9745                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9746                            element == EL_EXPANDABLE_STEELWALL_ANY);
9747
9748   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9749                              element == EL_EXPANDABLE_WALL_ANY ||
9750                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9751                              element == EL_EXPANDABLE_STEELWALL_ANY);
9752
9753   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9754                              element == EL_EXPANDABLE_WALL_ANY ||
9755                              element == EL_EXPANDABLE_WALL ||
9756                              element == EL_BD_EXPANDABLE_WALL ||
9757                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9758                              element == EL_EXPANDABLE_STEELWALL_ANY);
9759
9760   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9761                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9762
9763   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9764                              element == EL_EXPANDABLE_WALL ||
9765                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9766
9767   int wall_growing = (is_steelwall ?
9768                       EL_EXPANDABLE_STEELWALL_GROWING :
9769                       EL_EXPANDABLE_WALL_GROWING);
9770
9771   int gfx_wall_growing_up    = (is_steelwall ?
9772                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9773                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9774   int gfx_wall_growing_down  = (is_steelwall ?
9775                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9776                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9777   int gfx_wall_growing_left  = (is_steelwall ?
9778                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9779                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9780   int gfx_wall_growing_right = (is_steelwall ?
9781                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9782                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9783
9784   if (IS_ANIMATED(graphic))
9785     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9786
9787   if (!MovDelay[ax][ay])        // start building new wall
9788     MovDelay[ax][ay] = 6;
9789
9790   if (MovDelay[ax][ay])         // wait some time before building new wall
9791   {
9792     MovDelay[ax][ay]--;
9793     if (MovDelay[ax][ay])
9794       return;
9795   }
9796
9797   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9798     free_top = TRUE;
9799   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9800     free_bottom = TRUE;
9801   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9802     free_left = TRUE;
9803   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9804     free_right = TRUE;
9805
9806   if (grow_vertical)
9807   {
9808     if (free_top)
9809     {
9810       Tile[ax][ay - 1] = wall_growing;
9811       Store[ax][ay - 1] = element;
9812       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9813
9814       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9815         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9816
9817       new_wall = TRUE;
9818     }
9819
9820     if (free_bottom)
9821     {
9822       Tile[ax][ay + 1] = wall_growing;
9823       Store[ax][ay + 1] = element;
9824       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9825
9826       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9827         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9828
9829       new_wall = TRUE;
9830     }
9831   }
9832
9833   if (grow_horizontal)
9834   {
9835     if (free_left)
9836     {
9837       Tile[ax - 1][ay] = wall_growing;
9838       Store[ax - 1][ay] = element;
9839       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9840
9841       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9842         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9843
9844       new_wall = TRUE;
9845     }
9846
9847     if (free_right)
9848     {
9849       Tile[ax + 1][ay] = wall_growing;
9850       Store[ax + 1][ay] = element;
9851       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9852
9853       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9854         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9855
9856       new_wall = TRUE;
9857     }
9858   }
9859
9860   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9861     TEST_DrawLevelField(ax, ay);
9862
9863   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9864     stop_top = TRUE;
9865   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9866     stop_bottom = TRUE;
9867   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9868     stop_left = TRUE;
9869   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9870     stop_right = TRUE;
9871
9872   if (((stop_top && stop_bottom) || stop_horizontal) &&
9873       ((stop_left && stop_right) || stop_vertical))
9874     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9875
9876   if (new_wall)
9877     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9878 }
9879
9880 static void CheckForDragon(int x, int y)
9881 {
9882   int i, j;
9883   boolean dragon_found = FALSE;
9884   struct XY *xy = xy_topdown;
9885
9886   for (i = 0; i < NUM_DIRECTIONS; i++)
9887   {
9888     for (j = 0; j < 4; j++)
9889     {
9890       int xx = x + j * xy[i].x;
9891       int yy = y + j * xy[i].y;
9892
9893       if (IN_LEV_FIELD(xx, yy) &&
9894           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9895       {
9896         if (Tile[xx][yy] == EL_DRAGON)
9897           dragon_found = TRUE;
9898       }
9899       else
9900         break;
9901     }
9902   }
9903
9904   if (!dragon_found)
9905   {
9906     for (i = 0; i < NUM_DIRECTIONS; i++)
9907     {
9908       for (j = 0; j < 3; j++)
9909       {
9910         int xx = x + j * xy[i].x;
9911         int yy = y + j * xy[i].y;
9912
9913         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9914         {
9915           Tile[xx][yy] = EL_EMPTY;
9916           TEST_DrawLevelField(xx, yy);
9917         }
9918         else
9919           break;
9920       }
9921     }
9922   }
9923 }
9924
9925 static void InitBuggyBase(int x, int y)
9926 {
9927   int element = Tile[x][y];
9928   int activating_delay = FRAMES_PER_SECOND / 4;
9929
9930   ChangeDelay[x][y] =
9931     (element == EL_SP_BUGGY_BASE ?
9932      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9933      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9934      activating_delay :
9935      element == EL_SP_BUGGY_BASE_ACTIVE ?
9936      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9937 }
9938
9939 static void WarnBuggyBase(int x, int y)
9940 {
9941   int i;
9942   struct XY *xy = xy_topdown;
9943
9944   for (i = 0; i < NUM_DIRECTIONS; i++)
9945   {
9946     int xx = x + xy[i].x;
9947     int yy = y + xy[i].y;
9948
9949     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9950     {
9951       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9952
9953       break;
9954     }
9955   }
9956 }
9957
9958 static void InitTrap(int x, int y)
9959 {
9960   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9961 }
9962
9963 static void ActivateTrap(int x, int y)
9964 {
9965   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9966 }
9967
9968 static void ChangeActiveTrap(int x, int y)
9969 {
9970   int graphic = IMG_TRAP_ACTIVE;
9971
9972   // if new animation frame was drawn, correct crumbled sand border
9973   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9974     TEST_DrawLevelFieldCrumbled(x, y);
9975 }
9976
9977 static int getSpecialActionElement(int element, int number, int base_element)
9978 {
9979   return (element != EL_EMPTY ? element :
9980           number != -1 ? base_element + number - 1 :
9981           EL_EMPTY);
9982 }
9983
9984 static int getModifiedActionNumber(int value_old, int operator, int operand,
9985                                    int value_min, int value_max)
9986 {
9987   int value_new = (operator == CA_MODE_SET      ? operand :
9988                    operator == CA_MODE_ADD      ? value_old + operand :
9989                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9990                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9991                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9992                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9993                    value_old);
9994
9995   return (value_new < value_min ? value_min :
9996           value_new > value_max ? value_max :
9997           value_new);
9998 }
9999
10000 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10001 {
10002   struct ElementInfo *ei = &element_info[element];
10003   struct ElementChangeInfo *change = &ei->change_page[page];
10004   int target_element = change->target_element;
10005   int action_type = change->action_type;
10006   int action_mode = change->action_mode;
10007   int action_arg = change->action_arg;
10008   int action_element = change->action_element;
10009   int i;
10010
10011   if (!change->has_action)
10012     return;
10013
10014   // ---------- determine action paramater values -----------------------------
10015
10016   int level_time_value =
10017     (level.time > 0 ? TimeLeft :
10018      TimePlayed);
10019
10020   int action_arg_element_raw =
10021     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10022      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10023      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10024      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10025      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10026      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10027      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10028      EL_EMPTY);
10029   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10030
10031   int action_arg_direction =
10032     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10033      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10034      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10035      change->actual_trigger_side :
10036      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10037      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10038      MV_NONE);
10039
10040   int action_arg_number_min =
10041     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10042      CA_ARG_MIN);
10043
10044   int action_arg_number_max =
10045     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10046      action_type == CA_SET_LEVEL_GEMS ? 999 :
10047      action_type == CA_SET_LEVEL_TIME ? 9999 :
10048      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10049      action_type == CA_SET_CE_VALUE ? 9999 :
10050      action_type == CA_SET_CE_SCORE ? 9999 :
10051      CA_ARG_MAX);
10052
10053   int action_arg_number_reset =
10054     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10055      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10056      action_type == CA_SET_LEVEL_TIME ? level.time :
10057      action_type == CA_SET_LEVEL_SCORE ? 0 :
10058      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10059      action_type == CA_SET_CE_SCORE ? 0 :
10060      0);
10061
10062   int action_arg_number =
10063     (action_arg <= CA_ARG_MAX ? action_arg :
10064      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10065      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10066      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10067      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10068      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10069      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10070      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10071      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10072      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10073      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10074      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10075      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10076      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10077      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10078      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10079      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10080      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10081      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10082      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10083      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10084      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10085      -1);
10086
10087   int action_arg_number_old =
10088     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10089      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10090      action_type == CA_SET_LEVEL_SCORE ? game.score :
10091      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10092      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10093      0);
10094
10095   int action_arg_number_new =
10096     getModifiedActionNumber(action_arg_number_old,
10097                             action_mode, action_arg_number,
10098                             action_arg_number_min, action_arg_number_max);
10099
10100   int trigger_player_bits =
10101     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10102      change->actual_trigger_player_bits : change->trigger_player);
10103
10104   int action_arg_player_bits =
10105     (action_arg >= CA_ARG_PLAYER_1 &&
10106      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10107      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10108      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10109      PLAYER_BITS_ANY);
10110
10111   // ---------- execute action  -----------------------------------------------
10112
10113   switch (action_type)
10114   {
10115     case CA_NO_ACTION:
10116     {
10117       return;
10118     }
10119
10120     // ---------- level actions  ----------------------------------------------
10121
10122     case CA_RESTART_LEVEL:
10123     {
10124       game.restart_level = TRUE;
10125
10126       break;
10127     }
10128
10129     case CA_SHOW_ENVELOPE:
10130     {
10131       int element = getSpecialActionElement(action_arg_element,
10132                                             action_arg_number, EL_ENVELOPE_1);
10133
10134       if (IS_ENVELOPE(element))
10135         local_player->show_envelope = element;
10136
10137       break;
10138     }
10139
10140     case CA_SET_LEVEL_TIME:
10141     {
10142       if (level.time > 0)       // only modify limited time value
10143       {
10144         TimeLeft = action_arg_number_new;
10145
10146         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10147
10148         DisplayGameControlValues();
10149
10150         if (!TimeLeft && game.time_limit)
10151           for (i = 0; i < MAX_PLAYERS; i++)
10152             KillPlayer(&stored_player[i]);
10153       }
10154
10155       break;
10156     }
10157
10158     case CA_SET_LEVEL_SCORE:
10159     {
10160       game.score = action_arg_number_new;
10161
10162       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10163
10164       DisplayGameControlValues();
10165
10166       break;
10167     }
10168
10169     case CA_SET_LEVEL_GEMS:
10170     {
10171       game.gems_still_needed = action_arg_number_new;
10172
10173       game.snapshot.collected_item = TRUE;
10174
10175       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10176
10177       DisplayGameControlValues();
10178
10179       break;
10180     }
10181
10182     case CA_SET_LEVEL_WIND:
10183     {
10184       game.wind_direction = action_arg_direction;
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_RANDOM_SEED:
10190     {
10191       // ensure that setting a new random seed while playing is predictable
10192       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10193
10194       break;
10195     }
10196
10197     // ---------- player actions  ---------------------------------------------
10198
10199     case CA_MOVE_PLAYER:
10200     case CA_MOVE_PLAYER_NEW:
10201     {
10202       // automatically move to the next field in specified direction
10203       for (i = 0; i < MAX_PLAYERS; i++)
10204         if (trigger_player_bits & (1 << i))
10205           if (action_type == CA_MOVE_PLAYER ||
10206               stored_player[i].MovPos == 0)
10207             stored_player[i].programmed_action = action_arg_direction;
10208
10209       break;
10210     }
10211
10212     case CA_EXIT_PLAYER:
10213     {
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215         if (action_arg_player_bits & (1 << i))
10216           ExitPlayer(&stored_player[i]);
10217
10218       if (game.players_still_needed == 0)
10219         LevelSolved();
10220
10221       break;
10222     }
10223
10224     case CA_KILL_PLAYER:
10225     {
10226       for (i = 0; i < MAX_PLAYERS; i++)
10227         if (action_arg_player_bits & (1 << i))
10228           KillPlayer(&stored_player[i]);
10229
10230       break;
10231     }
10232
10233     case CA_SET_PLAYER_KEYS:
10234     {
10235       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10236       int element = getSpecialActionElement(action_arg_element,
10237                                             action_arg_number, EL_KEY_1);
10238
10239       if (IS_KEY(element))
10240       {
10241         for (i = 0; i < MAX_PLAYERS; i++)
10242         {
10243           if (trigger_player_bits & (1 << i))
10244           {
10245             stored_player[i].key[KEY_NR(element)] = key_state;
10246
10247             DrawGameDoorValues();
10248           }
10249         }
10250       }
10251
10252       break;
10253     }
10254
10255     case CA_SET_PLAYER_SPEED:
10256     {
10257       for (i = 0; i < MAX_PLAYERS; i++)
10258       {
10259         if (trigger_player_bits & (1 << i))
10260         {
10261           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10262
10263           if (action_arg == CA_ARG_SPEED_FASTER &&
10264               stored_player[i].cannot_move)
10265           {
10266             action_arg_number = STEPSIZE_VERY_SLOW;
10267           }
10268           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10269                    action_arg == CA_ARG_SPEED_FASTER)
10270           {
10271             action_arg_number = 2;
10272             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10273                            CA_MODE_MULTIPLY);
10274           }
10275           else if (action_arg == CA_ARG_NUMBER_RESET)
10276           {
10277             action_arg_number = level.initial_player_stepsize[i];
10278           }
10279
10280           move_stepsize =
10281             getModifiedActionNumber(move_stepsize,
10282                                     action_mode,
10283                                     action_arg_number,
10284                                     action_arg_number_min,
10285                                     action_arg_number_max);
10286
10287           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10288         }
10289       }
10290
10291       break;
10292     }
10293
10294     case CA_SET_PLAYER_SHIELD:
10295     {
10296       for (i = 0; i < MAX_PLAYERS; i++)
10297       {
10298         if (trigger_player_bits & (1 << i))
10299         {
10300           if (action_arg == CA_ARG_SHIELD_OFF)
10301           {
10302             stored_player[i].shield_normal_time_left = 0;
10303             stored_player[i].shield_deadly_time_left = 0;
10304           }
10305           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10306           {
10307             stored_player[i].shield_normal_time_left = 999999;
10308           }
10309           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10310           {
10311             stored_player[i].shield_normal_time_left = 999999;
10312             stored_player[i].shield_deadly_time_left = 999999;
10313           }
10314         }
10315       }
10316
10317       break;
10318     }
10319
10320     case CA_SET_PLAYER_GRAVITY:
10321     {
10322       for (i = 0; i < MAX_PLAYERS; i++)
10323       {
10324         if (trigger_player_bits & (1 << i))
10325         {
10326           stored_player[i].gravity =
10327             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10328              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10329              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10330              stored_player[i].gravity);
10331         }
10332       }
10333
10334       break;
10335     }
10336
10337     case CA_SET_PLAYER_ARTWORK:
10338     {
10339       for (i = 0; i < MAX_PLAYERS; i++)
10340       {
10341         if (trigger_player_bits & (1 << i))
10342         {
10343           int artwork_element = action_arg_element;
10344
10345           if (action_arg == CA_ARG_ELEMENT_RESET)
10346             artwork_element =
10347               (level.use_artwork_element[i] ? level.artwork_element[i] :
10348                stored_player[i].element_nr);
10349
10350           if (stored_player[i].artwork_element != artwork_element)
10351             stored_player[i].Frame = 0;
10352
10353           stored_player[i].artwork_element = artwork_element;
10354
10355           SetPlayerWaiting(&stored_player[i], FALSE);
10356
10357           // set number of special actions for bored and sleeping animation
10358           stored_player[i].num_special_action_bored =
10359             get_num_special_action(artwork_element,
10360                                    ACTION_BORING_1, ACTION_BORING_LAST);
10361           stored_player[i].num_special_action_sleeping =
10362             get_num_special_action(artwork_element,
10363                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10364         }
10365       }
10366
10367       break;
10368     }
10369
10370     case CA_SET_PLAYER_INVENTORY:
10371     {
10372       for (i = 0; i < MAX_PLAYERS; i++)
10373       {
10374         struct PlayerInfo *player = &stored_player[i];
10375         int j, k;
10376
10377         if (trigger_player_bits & (1 << i))
10378         {
10379           int inventory_element = action_arg_element;
10380
10381           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10382               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10383               action_arg == CA_ARG_ELEMENT_ACTION)
10384           {
10385             int element = inventory_element;
10386             int collect_count = element_info[element].collect_count_initial;
10387
10388             if (!IS_CUSTOM_ELEMENT(element))
10389               collect_count = 1;
10390
10391             if (collect_count == 0)
10392               player->inventory_infinite_element = element;
10393             else
10394               for (k = 0; k < collect_count; k++)
10395                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10396                   player->inventory_element[player->inventory_size++] =
10397                     element;
10398           }
10399           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10400                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10401                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10402           {
10403             if (player->inventory_infinite_element != EL_UNDEFINED &&
10404                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10405                                      action_arg_element_raw))
10406               player->inventory_infinite_element = EL_UNDEFINED;
10407
10408             for (k = 0, j = 0; j < player->inventory_size; j++)
10409             {
10410               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10411                                         action_arg_element_raw))
10412                 player->inventory_element[k++] = player->inventory_element[j];
10413             }
10414
10415             player->inventory_size = k;
10416           }
10417           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10418           {
10419             if (player->inventory_size > 0)
10420             {
10421               for (j = 0; j < player->inventory_size - 1; j++)
10422                 player->inventory_element[j] = player->inventory_element[j + 1];
10423
10424               player->inventory_size--;
10425             }
10426           }
10427           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10428           {
10429             if (player->inventory_size > 0)
10430               player->inventory_size--;
10431           }
10432           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10433           {
10434             player->inventory_infinite_element = EL_UNDEFINED;
10435             player->inventory_size = 0;
10436           }
10437           else if (action_arg == CA_ARG_INVENTORY_RESET)
10438           {
10439             player->inventory_infinite_element = EL_UNDEFINED;
10440             player->inventory_size = 0;
10441
10442             if (level.use_initial_inventory[i])
10443             {
10444               for (j = 0; j < level.initial_inventory_size[i]; j++)
10445               {
10446                 int element = level.initial_inventory_content[i][j];
10447                 int collect_count = element_info[element].collect_count_initial;
10448
10449                 if (!IS_CUSTOM_ELEMENT(element))
10450                   collect_count = 1;
10451
10452                 if (collect_count == 0)
10453                   player->inventory_infinite_element = element;
10454                 else
10455                   for (k = 0; k < collect_count; k++)
10456                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10457                       player->inventory_element[player->inventory_size++] =
10458                         element;
10459               }
10460             }
10461           }
10462         }
10463       }
10464
10465       break;
10466     }
10467
10468     // ---------- CE actions  -------------------------------------------------
10469
10470     case CA_SET_CE_VALUE:
10471     {
10472       int last_ce_value = CustomValue[x][y];
10473
10474       CustomValue[x][y] = action_arg_number_new;
10475
10476       if (CustomValue[x][y] != last_ce_value)
10477       {
10478         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10479         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10480
10481         if (CustomValue[x][y] == 0)
10482         {
10483           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10484           ChangeCount[x][y] = 0;        // allow at least one more change
10485
10486           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10487           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10488         }
10489       }
10490
10491       break;
10492     }
10493
10494     case CA_SET_CE_SCORE:
10495     {
10496       int last_ce_score = ei->collect_score;
10497
10498       ei->collect_score = action_arg_number_new;
10499
10500       if (ei->collect_score != last_ce_score)
10501       {
10502         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10503         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10504
10505         if (ei->collect_score == 0)
10506         {
10507           int xx, yy;
10508
10509           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10510           ChangeCount[x][y] = 0;        // allow at least one more change
10511
10512           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10513           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10514
10515           /*
10516             This is a very special case that seems to be a mixture between
10517             CheckElementChange() and CheckTriggeredElementChange(): while
10518             the first one only affects single elements that are triggered
10519             directly, the second one affects multiple elements in the playfield
10520             that are triggered indirectly by another element. This is a third
10521             case: Changing the CE score always affects multiple identical CEs,
10522             so every affected CE must be checked, not only the single CE for
10523             which the CE score was changed in the first place (as every instance
10524             of that CE shares the same CE score, and therefore also can change)!
10525           */
10526           SCAN_PLAYFIELD(xx, yy)
10527           {
10528             if (Tile[xx][yy] == element)
10529               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10530                                  CE_SCORE_GETS_ZERO);
10531           }
10532         }
10533       }
10534
10535       break;
10536     }
10537
10538     case CA_SET_CE_ARTWORK:
10539     {
10540       int artwork_element = action_arg_element;
10541       boolean reset_frame = FALSE;
10542       int xx, yy;
10543
10544       if (action_arg == CA_ARG_ELEMENT_RESET)
10545         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10546                            element);
10547
10548       if (ei->gfx_element != artwork_element)
10549         reset_frame = TRUE;
10550
10551       ei->gfx_element = artwork_element;
10552
10553       SCAN_PLAYFIELD(xx, yy)
10554       {
10555         if (Tile[xx][yy] == element)
10556         {
10557           if (reset_frame)
10558           {
10559             ResetGfxAnimation(xx, yy);
10560             ResetRandomAnimationValue(xx, yy);
10561           }
10562
10563           TEST_DrawLevelField(xx, yy);
10564         }
10565       }
10566
10567       break;
10568     }
10569
10570     // ---------- engine actions  ---------------------------------------------
10571
10572     case CA_SET_ENGINE_SCAN_MODE:
10573     {
10574       InitPlayfieldScanMode(action_arg);
10575
10576       break;
10577     }
10578
10579     default:
10580       break;
10581   }
10582 }
10583
10584 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10585 {
10586   int old_element = Tile[x][y];
10587   int new_element = GetElementFromGroupElement(element);
10588   int previous_move_direction = MovDir[x][y];
10589   int last_ce_value = CustomValue[x][y];
10590   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10591   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10592   boolean add_player_onto_element = (new_element_is_player &&
10593                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10594                                      IS_WALKABLE(old_element));
10595
10596   if (!add_player_onto_element)
10597   {
10598     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10599       RemoveMovingField(x, y);
10600     else
10601       RemoveField(x, y);
10602
10603     Tile[x][y] = new_element;
10604
10605     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10606       MovDir[x][y] = previous_move_direction;
10607
10608     if (element_info[new_element].use_last_ce_value)
10609       CustomValue[x][y] = last_ce_value;
10610
10611     InitField_WithBug1(x, y, FALSE);
10612
10613     new_element = Tile[x][y];   // element may have changed
10614
10615     ResetGfxAnimation(x, y);
10616     ResetRandomAnimationValue(x, y);
10617
10618     TEST_DrawLevelField(x, y);
10619
10620     if (GFX_CRUMBLED(new_element))
10621       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10622   }
10623
10624   // check if element under the player changes from accessible to unaccessible
10625   // (needed for special case of dropping element which then changes)
10626   // (must be checked after creating new element for walkable group elements)
10627   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10628       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10629   {
10630     Bang(x, y);
10631
10632     return;
10633   }
10634
10635   // "ChangeCount" not set yet to allow "entered by player" change one time
10636   if (new_element_is_player)
10637     RelocatePlayer(x, y, new_element);
10638
10639   if (is_change)
10640     ChangeCount[x][y]++;        // count number of changes in the same frame
10641
10642   TestIfBadThingTouchesPlayer(x, y);
10643   TestIfPlayerTouchesCustomElement(x, y);
10644   TestIfElementTouchesCustomElement(x, y);
10645 }
10646
10647 static void CreateField(int x, int y, int element)
10648 {
10649   CreateFieldExt(x, y, element, FALSE);
10650 }
10651
10652 static void CreateElementFromChange(int x, int y, int element)
10653 {
10654   element = GET_VALID_RUNTIME_ELEMENT(element);
10655
10656   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10657   {
10658     int old_element = Tile[x][y];
10659
10660     // prevent changed element from moving in same engine frame
10661     // unless both old and new element can either fall or move
10662     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10663         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10664       Stop[x][y] = TRUE;
10665   }
10666
10667   CreateFieldExt(x, y, element, TRUE);
10668 }
10669
10670 static boolean ChangeElement(int x, int y, int element, int page)
10671 {
10672   struct ElementInfo *ei = &element_info[element];
10673   struct ElementChangeInfo *change = &ei->change_page[page];
10674   int ce_value = CustomValue[x][y];
10675   int ce_score = ei->collect_score;
10676   int target_element;
10677   int old_element = Tile[x][y];
10678
10679   // always use default change event to prevent running into a loop
10680   if (ChangeEvent[x][y] == -1)
10681     ChangeEvent[x][y] = CE_DELAY;
10682
10683   if (ChangeEvent[x][y] == CE_DELAY)
10684   {
10685     // reset actual trigger element, trigger player and action element
10686     change->actual_trigger_element = EL_EMPTY;
10687     change->actual_trigger_player = EL_EMPTY;
10688     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10689     change->actual_trigger_side = CH_SIDE_NONE;
10690     change->actual_trigger_ce_value = 0;
10691     change->actual_trigger_ce_score = 0;
10692   }
10693
10694   // do not change elements more than a specified maximum number of changes
10695   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10696     return FALSE;
10697
10698   ChangeCount[x][y]++;          // count number of changes in the same frame
10699
10700   if (change->explode)
10701   {
10702     Bang(x, y);
10703
10704     return TRUE;
10705   }
10706
10707   if (change->use_target_content)
10708   {
10709     boolean complete_replace = TRUE;
10710     boolean can_replace[3][3];
10711     int xx, yy;
10712
10713     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10714     {
10715       boolean is_empty;
10716       boolean is_walkable;
10717       boolean is_diggable;
10718       boolean is_collectible;
10719       boolean is_removable;
10720       boolean is_destructible;
10721       int ex = x + xx - 1;
10722       int ey = y + yy - 1;
10723       int content_element = change->target_content.e[xx][yy];
10724       int e;
10725
10726       can_replace[xx][yy] = TRUE;
10727
10728       if (ex == x && ey == y)   // do not check changing element itself
10729         continue;
10730
10731       if (content_element == EL_EMPTY_SPACE)
10732       {
10733         can_replace[xx][yy] = FALSE;    // do not replace border with space
10734
10735         continue;
10736       }
10737
10738       if (!IN_LEV_FIELD(ex, ey))
10739       {
10740         can_replace[xx][yy] = FALSE;
10741         complete_replace = FALSE;
10742
10743         continue;
10744       }
10745
10746       e = Tile[ex][ey];
10747
10748       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10749         e = MovingOrBlocked2Element(ex, ey);
10750
10751       is_empty = (IS_FREE(ex, ey) ||
10752                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10753
10754       is_walkable     = (is_empty || IS_WALKABLE(e));
10755       is_diggable     = (is_empty || IS_DIGGABLE(e));
10756       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10757       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10758       is_removable    = (is_diggable || is_collectible);
10759
10760       can_replace[xx][yy] =
10761         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10762           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10763           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10764           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10765           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10766           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10767          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10768
10769       if (!can_replace[xx][yy])
10770         complete_replace = FALSE;
10771     }
10772
10773     if (!change->only_if_complete || complete_replace)
10774     {
10775       boolean something_has_changed = FALSE;
10776
10777       if (change->only_if_complete && change->use_random_replace &&
10778           RND(100) < change->random_percentage)
10779         return FALSE;
10780
10781       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10782       {
10783         int ex = x + xx - 1;
10784         int ey = y + yy - 1;
10785         int content_element;
10786
10787         if (can_replace[xx][yy] && (!change->use_random_replace ||
10788                                     RND(100) < change->random_percentage))
10789         {
10790           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10791             RemoveMovingField(ex, ey);
10792
10793           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10794
10795           content_element = change->target_content.e[xx][yy];
10796           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10797                                               ce_value, ce_score);
10798
10799           CreateElementFromChange(ex, ey, target_element);
10800
10801           something_has_changed = TRUE;
10802
10803           // for symmetry reasons, freeze newly created border elements
10804           if (ex != x || ey != y)
10805             Stop[ex][ey] = TRUE;        // no more moving in this frame
10806         }
10807       }
10808
10809       if (something_has_changed)
10810       {
10811         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10812         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10813       }
10814     }
10815   }
10816   else
10817   {
10818     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10819                                         ce_value, ce_score);
10820
10821     if (element == EL_DIAGONAL_GROWING ||
10822         element == EL_DIAGONAL_SHRINKING)
10823     {
10824       target_element = Store[x][y];
10825
10826       Store[x][y] = EL_EMPTY;
10827     }
10828
10829     // special case: element changes to player (and may be kept if walkable)
10830     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10831       CreateElementFromChange(x, y, EL_EMPTY);
10832
10833     CreateElementFromChange(x, y, target_element);
10834
10835     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10836     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10837   }
10838
10839   // this uses direct change before indirect change
10840   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10841
10842   return TRUE;
10843 }
10844
10845 static void HandleElementChange(int x, int y, int page)
10846 {
10847   int element = MovingOrBlocked2Element(x, y);
10848   struct ElementInfo *ei = &element_info[element];
10849   struct ElementChangeInfo *change = &ei->change_page[page];
10850   boolean handle_action_before_change = FALSE;
10851
10852 #ifdef DEBUG
10853   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10854       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10855   {
10856     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10857           x, y, element, element_info[element].token_name);
10858     Debug("game:playing:HandleElementChange", "This should never happen!");
10859   }
10860 #endif
10861
10862   // this can happen with classic bombs on walkable, changing elements
10863   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10864   {
10865     return;
10866   }
10867
10868   if (ChangeDelay[x][y] == 0)           // initialize element change
10869   {
10870     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10871
10872     if (change->can_change)
10873     {
10874       // !!! not clear why graphic animation should be reset at all here !!!
10875       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10876       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10877
10878       /*
10879         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10880
10881         When using an animation frame delay of 1 (this only happens with
10882         "sp_zonk.moving.left/right" in the classic graphics), the default
10883         (non-moving) animation shows wrong animation frames (while the
10884         moving animation, like "sp_zonk.moving.left/right", is correct,
10885         so this graphical bug never shows up with the classic graphics).
10886         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10887         be drawn instead of the correct frames 0,1,2,3. This is caused by
10888         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10889         an element change: First when the change delay ("ChangeDelay[][]")
10890         counter has reached zero after decrementing, then a second time in
10891         the next frame (after "GfxFrame[][]" was already incremented) when
10892         "ChangeDelay[][]" is reset to the initial delay value again.
10893
10894         This causes frame 0 to be drawn twice, while the last frame won't
10895         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10896
10897         As some animations may already be cleverly designed around this bug
10898         (at least the "Snake Bite" snake tail animation does this), it cannot
10899         simply be fixed here without breaking such existing animations.
10900         Unfortunately, it cannot easily be detected if a graphics set was
10901         designed "before" or "after" the bug was fixed. As a workaround,
10902         a new graphics set option "game.graphics_engine_version" was added
10903         to be able to specify the game's major release version for which the
10904         graphics set was designed, which can then be used to decide if the
10905         bugfix should be used (version 4 and above) or not (version 3 or
10906         below, or if no version was specified at all, as with old sets).
10907
10908         (The wrong/fixed animation frames can be tested with the test level set
10909         "test_gfxframe" and level "000", which contains a specially prepared
10910         custom element at level position (x/y) == (11/9) which uses the zonk
10911         animation mentioned above. Using "game.graphics_engine_version: 4"
10912         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10913         This can also be seen from the debug output for this test element.)
10914       */
10915
10916       // when a custom element is about to change (for example by change delay),
10917       // do not reset graphic animation when the custom element is moving
10918       if (game.graphics_engine_version < 4 &&
10919           !IS_MOVING(x, y))
10920       {
10921         ResetGfxAnimation(x, y);
10922         ResetRandomAnimationValue(x, y);
10923       }
10924
10925       if (change->pre_change_function)
10926         change->pre_change_function(x, y);
10927     }
10928   }
10929
10930   ChangeDelay[x][y]--;
10931
10932   if (ChangeDelay[x][y] != 0)           // continue element change
10933   {
10934     if (change->can_change)
10935     {
10936       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10937
10938       if (IS_ANIMATED(graphic))
10939         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10940
10941       if (change->change_function)
10942         change->change_function(x, y);
10943     }
10944   }
10945   else                                  // finish element change
10946   {
10947     if (ChangePage[x][y] != -1)         // remember page from delayed change
10948     {
10949       page = ChangePage[x][y];
10950       ChangePage[x][y] = -1;
10951
10952       change = &ei->change_page[page];
10953     }
10954
10955     if (IS_MOVING(x, y))                // never change a running system ;-)
10956     {
10957       ChangeDelay[x][y] = 1;            // try change after next move step
10958       ChangePage[x][y] = page;          // remember page to use for change
10959
10960       return;
10961     }
10962
10963     // special case: set new level random seed before changing element
10964     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10965       handle_action_before_change = TRUE;
10966
10967     if (change->has_action && handle_action_before_change)
10968       ExecuteCustomElementAction(x, y, element, page);
10969
10970     if (change->can_change)
10971     {
10972       if (ChangeElement(x, y, element, page))
10973       {
10974         if (change->post_change_function)
10975           change->post_change_function(x, y);
10976       }
10977     }
10978
10979     if (change->has_action && !handle_action_before_change)
10980       ExecuteCustomElementAction(x, y, element, page);
10981   }
10982 }
10983
10984 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10985                                               int trigger_element,
10986                                               int trigger_event,
10987                                               int trigger_player,
10988                                               int trigger_side,
10989                                               int trigger_page)
10990 {
10991   boolean change_done_any = FALSE;
10992   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10993   int i;
10994
10995   if (!(trigger_events[trigger_element][trigger_event]))
10996     return FALSE;
10997
10998   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10999
11000   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11001   {
11002     int element = EL_CUSTOM_START + i;
11003     boolean change_done = FALSE;
11004     int p;
11005
11006     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11007         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11008       continue;
11009
11010     for (p = 0; p < element_info[element].num_change_pages; p++)
11011     {
11012       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11013
11014       if (change->can_change_or_has_action &&
11015           change->has_event[trigger_event] &&
11016           change->trigger_side & trigger_side &&
11017           change->trigger_player & trigger_player &&
11018           change->trigger_page & trigger_page_bits &&
11019           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11020       {
11021         change->actual_trigger_element = trigger_element;
11022         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11023         change->actual_trigger_player_bits = trigger_player;
11024         change->actual_trigger_side = trigger_side;
11025         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11026         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11027
11028         if ((change->can_change && !change_done) || change->has_action)
11029         {
11030           int x, y;
11031
11032           SCAN_PLAYFIELD(x, y)
11033           {
11034             if (Tile[x][y] == element)
11035             {
11036               if (change->can_change && !change_done)
11037               {
11038                 // if element already changed in this frame, not only prevent
11039                 // another element change (checked in ChangeElement()), but
11040                 // also prevent additional element actions for this element
11041
11042                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11043                     !level.use_action_after_change_bug)
11044                   continue;
11045
11046                 ChangeDelay[x][y] = 1;
11047                 ChangeEvent[x][y] = trigger_event;
11048
11049                 HandleElementChange(x, y, p);
11050               }
11051               else if (change->has_action)
11052               {
11053                 // if element already changed in this frame, not only prevent
11054                 // another element change (checked in ChangeElement()), but
11055                 // also prevent additional element actions for this element
11056
11057                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11058                     !level.use_action_after_change_bug)
11059                   continue;
11060
11061                 ExecuteCustomElementAction(x, y, element, p);
11062                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11063               }
11064             }
11065           }
11066
11067           if (change->can_change)
11068           {
11069             change_done = TRUE;
11070             change_done_any = TRUE;
11071           }
11072         }
11073       }
11074     }
11075   }
11076
11077   RECURSION_LOOP_DETECTION_END();
11078
11079   return change_done_any;
11080 }
11081
11082 static boolean CheckElementChangeExt(int x, int y,
11083                                      int element,
11084                                      int trigger_element,
11085                                      int trigger_event,
11086                                      int trigger_player,
11087                                      int trigger_side)
11088 {
11089   boolean change_done = FALSE;
11090   int p;
11091
11092   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11093       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11094     return FALSE;
11095
11096   if (Tile[x][y] == EL_BLOCKED)
11097   {
11098     Blocked2Moving(x, y, &x, &y);
11099     element = Tile[x][y];
11100   }
11101
11102   // check if element has already changed or is about to change after moving
11103   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11104        Tile[x][y] != element) ||
11105
11106       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11107        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11108         ChangePage[x][y] != -1)))
11109     return FALSE;
11110
11111   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11112
11113   for (p = 0; p < element_info[element].num_change_pages; p++)
11114   {
11115     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11116
11117     /* check trigger element for all events where the element that is checked
11118        for changing interacts with a directly adjacent element -- this is
11119        different to element changes that affect other elements to change on the
11120        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11121     boolean check_trigger_element =
11122       (trigger_event == CE_NEXT_TO_X ||
11123        trigger_event == CE_TOUCHING_X ||
11124        trigger_event == CE_HITTING_X ||
11125        trigger_event == CE_HIT_BY_X ||
11126        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11127
11128     if (change->can_change_or_has_action &&
11129         change->has_event[trigger_event] &&
11130         change->trigger_side & trigger_side &&
11131         change->trigger_player & trigger_player &&
11132         (!check_trigger_element ||
11133          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11134     {
11135       change->actual_trigger_element = trigger_element;
11136       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11137       change->actual_trigger_player_bits = trigger_player;
11138       change->actual_trigger_side = trigger_side;
11139       change->actual_trigger_ce_value = CustomValue[x][y];
11140       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11141
11142       // special case: trigger element not at (x,y) position for some events
11143       if (check_trigger_element)
11144       {
11145         static struct
11146         {
11147           int dx, dy;
11148         } move_xy[] =
11149           {
11150             {  0,  0 },
11151             { -1,  0 },
11152             { +1,  0 },
11153             {  0,  0 },
11154             {  0, -1 },
11155             {  0,  0 }, { 0, 0 }, { 0, 0 },
11156             {  0, +1 }
11157           };
11158
11159         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11160         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11161
11162         change->actual_trigger_ce_value = CustomValue[xx][yy];
11163         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11164       }
11165
11166       if (change->can_change && !change_done)
11167       {
11168         ChangeDelay[x][y] = 1;
11169         ChangeEvent[x][y] = trigger_event;
11170
11171         HandleElementChange(x, y, p);
11172
11173         change_done = TRUE;
11174       }
11175       else if (change->has_action)
11176       {
11177         ExecuteCustomElementAction(x, y, element, p);
11178         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11179       }
11180     }
11181   }
11182
11183   RECURSION_LOOP_DETECTION_END();
11184
11185   return change_done;
11186 }
11187
11188 static void PlayPlayerSound(struct PlayerInfo *player)
11189 {
11190   int jx = player->jx, jy = player->jy;
11191   int sound_element = player->artwork_element;
11192   int last_action = player->last_action_waiting;
11193   int action = player->action_waiting;
11194
11195   if (player->is_waiting)
11196   {
11197     if (action != last_action)
11198       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11199     else
11200       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11201   }
11202   else
11203   {
11204     if (action != last_action)
11205       StopSound(element_info[sound_element].sound[last_action]);
11206
11207     if (last_action == ACTION_SLEEPING)
11208       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11209   }
11210 }
11211
11212 static void PlayAllPlayersSound(void)
11213 {
11214   int i;
11215
11216   for (i = 0; i < MAX_PLAYERS; i++)
11217     if (stored_player[i].active)
11218       PlayPlayerSound(&stored_player[i]);
11219 }
11220
11221 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11222 {
11223   boolean last_waiting = player->is_waiting;
11224   int move_dir = player->MovDir;
11225
11226   player->dir_waiting = move_dir;
11227   player->last_action_waiting = player->action_waiting;
11228
11229   if (is_waiting)
11230   {
11231     if (!last_waiting)          // not waiting -> waiting
11232     {
11233       player->is_waiting = TRUE;
11234
11235       player->frame_counter_bored =
11236         FrameCounter +
11237         game.player_boring_delay_fixed +
11238         GetSimpleRandom(game.player_boring_delay_random);
11239       player->frame_counter_sleeping =
11240         FrameCounter +
11241         game.player_sleeping_delay_fixed +
11242         GetSimpleRandom(game.player_sleeping_delay_random);
11243
11244       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11245     }
11246
11247     if (game.player_sleeping_delay_fixed +
11248         game.player_sleeping_delay_random > 0 &&
11249         player->anim_delay_counter == 0 &&
11250         player->post_delay_counter == 0 &&
11251         FrameCounter >= player->frame_counter_sleeping)
11252       player->is_sleeping = TRUE;
11253     else if (game.player_boring_delay_fixed +
11254              game.player_boring_delay_random > 0 &&
11255              FrameCounter >= player->frame_counter_bored)
11256       player->is_bored = TRUE;
11257
11258     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11259                               player->is_bored ? ACTION_BORING :
11260                               ACTION_WAITING);
11261
11262     if (player->is_sleeping && player->use_murphy)
11263     {
11264       // special case for sleeping Murphy when leaning against non-free tile
11265
11266       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11267           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11268            !IS_MOVING(player->jx - 1, player->jy)))
11269         move_dir = MV_LEFT;
11270       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11271                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11272                 !IS_MOVING(player->jx + 1, player->jy)))
11273         move_dir = MV_RIGHT;
11274       else
11275         player->is_sleeping = FALSE;
11276
11277       player->dir_waiting = move_dir;
11278     }
11279
11280     if (player->is_sleeping)
11281     {
11282       if (player->num_special_action_sleeping > 0)
11283       {
11284         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11285         {
11286           int last_special_action = player->special_action_sleeping;
11287           int num_special_action = player->num_special_action_sleeping;
11288           int special_action =
11289             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11290              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11291              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11292              last_special_action + 1 : ACTION_SLEEPING);
11293           int special_graphic =
11294             el_act_dir2img(player->artwork_element, special_action, move_dir);
11295
11296           player->anim_delay_counter =
11297             graphic_info[special_graphic].anim_delay_fixed +
11298             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11299           player->post_delay_counter =
11300             graphic_info[special_graphic].post_delay_fixed +
11301             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11302
11303           player->special_action_sleeping = special_action;
11304         }
11305
11306         if (player->anim_delay_counter > 0)
11307         {
11308           player->action_waiting = player->special_action_sleeping;
11309           player->anim_delay_counter--;
11310         }
11311         else if (player->post_delay_counter > 0)
11312         {
11313           player->post_delay_counter--;
11314         }
11315       }
11316     }
11317     else if (player->is_bored)
11318     {
11319       if (player->num_special_action_bored > 0)
11320       {
11321         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11322         {
11323           int special_action =
11324             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11325           int special_graphic =
11326             el_act_dir2img(player->artwork_element, special_action, move_dir);
11327
11328           player->anim_delay_counter =
11329             graphic_info[special_graphic].anim_delay_fixed +
11330             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11331           player->post_delay_counter =
11332             graphic_info[special_graphic].post_delay_fixed +
11333             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11334
11335           player->special_action_bored = special_action;
11336         }
11337
11338         if (player->anim_delay_counter > 0)
11339         {
11340           player->action_waiting = player->special_action_bored;
11341           player->anim_delay_counter--;
11342         }
11343         else if (player->post_delay_counter > 0)
11344         {
11345           player->post_delay_counter--;
11346         }
11347       }
11348     }
11349   }
11350   else if (last_waiting)        // waiting -> not waiting
11351   {
11352     player->is_waiting = FALSE;
11353     player->is_bored = FALSE;
11354     player->is_sleeping = FALSE;
11355
11356     player->frame_counter_bored = -1;
11357     player->frame_counter_sleeping = -1;
11358
11359     player->anim_delay_counter = 0;
11360     player->post_delay_counter = 0;
11361
11362     player->dir_waiting = player->MovDir;
11363     player->action_waiting = ACTION_DEFAULT;
11364
11365     player->special_action_bored = ACTION_DEFAULT;
11366     player->special_action_sleeping = ACTION_DEFAULT;
11367   }
11368 }
11369
11370 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11371 {
11372   if ((!player->is_moving  && player->was_moving) ||
11373       (player->MovPos == 0 && player->was_moving) ||
11374       (player->is_snapping && !player->was_snapping) ||
11375       (player->is_dropping && !player->was_dropping))
11376   {
11377     if (!CheckSaveEngineSnapshotToList())
11378       return;
11379
11380     player->was_moving = FALSE;
11381     player->was_snapping = TRUE;
11382     player->was_dropping = TRUE;
11383   }
11384   else
11385   {
11386     if (player->is_moving)
11387       player->was_moving = TRUE;
11388
11389     if (!player->is_snapping)
11390       player->was_snapping = FALSE;
11391
11392     if (!player->is_dropping)
11393       player->was_dropping = FALSE;
11394   }
11395
11396   static struct MouseActionInfo mouse_action_last = { 0 };
11397   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11398   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11399
11400   if (new_released)
11401     CheckSaveEngineSnapshotToList();
11402
11403   mouse_action_last = mouse_action;
11404 }
11405
11406 static void CheckSingleStepMode(struct PlayerInfo *player)
11407 {
11408   if (tape.single_step && tape.recording && !tape.pausing)
11409   {
11410     // as it is called "single step mode", just return to pause mode when the
11411     // player stopped moving after one tile (or never starts moving at all)
11412     // (reverse logic needed here in case single step mode used in team mode)
11413     if (player->is_moving ||
11414         player->is_pushing ||
11415         player->is_dropping_pressed ||
11416         player->effective_mouse_action.button)
11417       game.enter_single_step_mode = FALSE;
11418   }
11419
11420   CheckSaveEngineSnapshot(player);
11421 }
11422
11423 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11424 {
11425   int left      = player_action & JOY_LEFT;
11426   int right     = player_action & JOY_RIGHT;
11427   int up        = player_action & JOY_UP;
11428   int down      = player_action & JOY_DOWN;
11429   int button1   = player_action & JOY_BUTTON_1;
11430   int button2   = player_action & JOY_BUTTON_2;
11431   int dx        = (left ? -1 : right ? 1 : 0);
11432   int dy        = (up   ? -1 : down  ? 1 : 0);
11433
11434   if (!player->active || tape.pausing)
11435     return 0;
11436
11437   if (player_action)
11438   {
11439     if (button1)
11440       SnapField(player, dx, dy);
11441     else
11442     {
11443       if (button2)
11444         DropElement(player);
11445
11446       MovePlayer(player, dx, dy);
11447     }
11448
11449     CheckSingleStepMode(player);
11450
11451     SetPlayerWaiting(player, FALSE);
11452
11453     return player_action;
11454   }
11455   else
11456   {
11457     // no actions for this player (no input at player's configured device)
11458
11459     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11460     SnapField(player, 0, 0);
11461     CheckGravityMovementWhenNotMoving(player);
11462
11463     if (player->MovPos == 0)
11464       SetPlayerWaiting(player, TRUE);
11465
11466     if (player->MovPos == 0)    // needed for tape.playing
11467       player->is_moving = FALSE;
11468
11469     player->is_dropping = FALSE;
11470     player->is_dropping_pressed = FALSE;
11471     player->drop_pressed_delay = 0;
11472
11473     CheckSingleStepMode(player);
11474
11475     return 0;
11476   }
11477 }
11478
11479 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11480                                          byte *tape_action)
11481 {
11482   if (!tape.use_mouse_actions)
11483     return;
11484
11485   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11486   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11487   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11488 }
11489
11490 static void SetTapeActionFromMouseAction(byte *tape_action,
11491                                          struct MouseActionInfo *mouse_action)
11492 {
11493   if (!tape.use_mouse_actions)
11494     return;
11495
11496   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11497   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11498   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11499 }
11500
11501 static void CheckLevelSolved(void)
11502 {
11503   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11504   {
11505     if (game_em.level_solved &&
11506         !game_em.game_over)                             // game won
11507     {
11508       LevelSolved();
11509
11510       game_em.game_over = TRUE;
11511
11512       game.all_players_gone = TRUE;
11513     }
11514
11515     if (game_em.game_over)                              // game lost
11516       game.all_players_gone = TRUE;
11517   }
11518   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11519   {
11520     if (game_sp.level_solved &&
11521         !game_sp.game_over)                             // game won
11522     {
11523       LevelSolved();
11524
11525       game_sp.game_over = TRUE;
11526
11527       game.all_players_gone = TRUE;
11528     }
11529
11530     if (game_sp.game_over)                              // game lost
11531       game.all_players_gone = TRUE;
11532   }
11533   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11534   {
11535     if (game_mm.level_solved &&
11536         !game_mm.game_over)                             // game won
11537     {
11538       LevelSolved();
11539
11540       game_mm.game_over = TRUE;
11541
11542       game.all_players_gone = TRUE;
11543     }
11544
11545     if (game_mm.game_over)                              // game lost
11546       game.all_players_gone = TRUE;
11547   }
11548 }
11549
11550 static void CheckLevelTime_StepCounter(void)
11551 {
11552   int i;
11553
11554   TimePlayed++;
11555
11556   if (TimeLeft > 0)
11557   {
11558     TimeLeft--;
11559
11560     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11561       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11562
11563     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11564
11565     DisplayGameControlValues();
11566
11567     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11568       for (i = 0; i < MAX_PLAYERS; i++)
11569         KillPlayer(&stored_player[i]);
11570   }
11571   else if (game.no_level_time_limit && !game.all_players_gone)
11572   {
11573     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11574
11575     DisplayGameControlValues();
11576   }
11577 }
11578
11579 static void CheckLevelTime(void)
11580 {
11581   int i;
11582
11583   if (TimeFrames >= FRAMES_PER_SECOND)
11584   {
11585     TimeFrames = 0;
11586     TapeTime++;
11587
11588     for (i = 0; i < MAX_PLAYERS; i++)
11589     {
11590       struct PlayerInfo *player = &stored_player[i];
11591
11592       if (SHIELD_ON(player))
11593       {
11594         player->shield_normal_time_left--;
11595
11596         if (player->shield_deadly_time_left > 0)
11597           player->shield_deadly_time_left--;
11598       }
11599     }
11600
11601     if (!game.LevelSolved && !level.use_step_counter)
11602     {
11603       TimePlayed++;
11604
11605       if (TimeLeft > 0)
11606       {
11607         TimeLeft--;
11608
11609         if (TimeLeft <= 10 && game.time_limit)
11610           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11611
11612         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11613            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11614
11615         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11616
11617         if (!TimeLeft && game.time_limit)
11618         {
11619           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11620             game_em.lev->killed_out_of_time = TRUE;
11621           else
11622             for (i = 0; i < MAX_PLAYERS; i++)
11623               KillPlayer(&stored_player[i]);
11624         }
11625       }
11626       else if (game.no_level_time_limit && !game.all_players_gone)
11627       {
11628         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11629       }
11630
11631       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11632     }
11633
11634     if (tape.recording || tape.playing)
11635       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11636   }
11637
11638   if (tape.recording || tape.playing)
11639     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11640
11641   UpdateAndDisplayGameControlValues();
11642 }
11643
11644 void AdvanceFrameAndPlayerCounters(int player_nr)
11645 {
11646   int i;
11647
11648   // advance frame counters (global frame counter and time frame counter)
11649   FrameCounter++;
11650   TimeFrames++;
11651
11652   // advance player counters (counters for move delay, move animation etc.)
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654   {
11655     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11656     int move_delay_value = stored_player[i].move_delay_value;
11657     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11658
11659     if (!advance_player_counters)       // not all players may be affected
11660       continue;
11661
11662     if (move_frames == 0)       // less than one move per game frame
11663     {
11664       int stepsize = TILEX / move_delay_value;
11665       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11666       int count = (stored_player[i].is_moving ?
11667                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11668
11669       if (count % delay == 0)
11670         move_frames = 1;
11671     }
11672
11673     stored_player[i].Frame += move_frames;
11674
11675     if (stored_player[i].MovPos != 0)
11676       stored_player[i].StepFrame += move_frames;
11677
11678     if (stored_player[i].move_delay > 0)
11679       stored_player[i].move_delay--;
11680
11681     // due to bugs in previous versions, counter must count up, not down
11682     if (stored_player[i].push_delay != -1)
11683       stored_player[i].push_delay++;
11684
11685     if (stored_player[i].drop_delay > 0)
11686       stored_player[i].drop_delay--;
11687
11688     if (stored_player[i].is_dropping_pressed)
11689       stored_player[i].drop_pressed_delay++;
11690   }
11691 }
11692
11693 void AdvanceFrameCounter(void)
11694 {
11695   FrameCounter++;
11696 }
11697
11698 void AdvanceGfxFrame(void)
11699 {
11700   int x, y;
11701
11702   SCAN_PLAYFIELD(x, y)
11703   {
11704     GfxFrame[x][y]++;
11705   }
11706 }
11707
11708 void StartGameActions(boolean init_network_game, boolean record_tape,
11709                       int random_seed)
11710 {
11711   unsigned int new_random_seed = InitRND(random_seed);
11712
11713   if (record_tape)
11714     TapeStartRecording(new_random_seed);
11715
11716   if (setup.auto_pause_on_start && !tape.pausing)
11717     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11718
11719   if (init_network_game)
11720   {
11721     SendToServer_LevelFile();
11722     SendToServer_StartPlaying();
11723
11724     return;
11725   }
11726
11727   InitGame();
11728 }
11729
11730 static void GameActionsExt(void)
11731 {
11732 #if 0
11733   static unsigned int game_frame_delay = 0;
11734 #endif
11735   unsigned int game_frame_delay_value;
11736   byte *recorded_player_action;
11737   byte summarized_player_action = 0;
11738   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11739   int i;
11740
11741   // detect endless loops, caused by custom element programming
11742   if (recursion_loop_detected && recursion_loop_depth == 0)
11743   {
11744     char *message = getStringCat3("Internal Error! Element ",
11745                                   EL_NAME(recursion_loop_element),
11746                                   " caused endless loop! Quit the game?");
11747
11748     Warn("element '%s' caused endless loop in game engine",
11749          EL_NAME(recursion_loop_element));
11750
11751     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11752
11753     recursion_loop_detected = FALSE;    // if game should be continued
11754
11755     free(message);
11756
11757     return;
11758   }
11759
11760   if (game.restart_level)
11761     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11762
11763   CheckLevelSolved();
11764
11765   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11766     GameWon();
11767
11768   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11769     TapeStop();
11770
11771   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11772     return;
11773
11774   game_frame_delay_value =
11775     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11776
11777   if (tape.playing && tape.warp_forward && !tape.pausing)
11778     game_frame_delay_value = 0;
11779
11780   SetVideoFrameDelay(game_frame_delay_value);
11781
11782   // (de)activate virtual buttons depending on current game status
11783   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11784   {
11785     if (game.all_players_gone)  // if no players there to be controlled anymore
11786       SetOverlayActive(FALSE);
11787     else if (!tape.playing)     // if game continues after tape stopped playing
11788       SetOverlayActive(TRUE);
11789   }
11790
11791 #if 0
11792 #if 0
11793   // ---------- main game synchronization point ----------
11794
11795   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11796
11797   Debug("game:playing:skip", "skip == %d", skip);
11798
11799 #else
11800   // ---------- main game synchronization point ----------
11801
11802   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11803 #endif
11804 #endif
11805
11806   if (network_playing && !network_player_action_received)
11807   {
11808     // try to get network player actions in time
11809
11810     // last chance to get network player actions without main loop delay
11811     HandleNetworking();
11812
11813     // game was quit by network peer
11814     if (game_status != GAME_MODE_PLAYING)
11815       return;
11816
11817     // check if network player actions still missing and game still running
11818     if (!network_player_action_received && !checkGameEnded())
11819       return;           // failed to get network player actions in time
11820
11821     // do not yet reset "network_player_action_received" (for tape.pausing)
11822   }
11823
11824   if (tape.pausing)
11825     return;
11826
11827   // at this point we know that we really continue executing the game
11828
11829   network_player_action_received = FALSE;
11830
11831   // when playing tape, read previously recorded player input from tape data
11832   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11833
11834   local_player->effective_mouse_action = local_player->mouse_action;
11835
11836   if (recorded_player_action != NULL)
11837     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11838                                  recorded_player_action);
11839
11840   // TapePlayAction() may return NULL when toggling to "pause before death"
11841   if (tape.pausing)
11842     return;
11843
11844   if (tape.set_centered_player)
11845   {
11846     game.centered_player_nr_next = tape.centered_player_nr_next;
11847     game.set_centered_player = TRUE;
11848   }
11849
11850   for (i = 0; i < MAX_PLAYERS; i++)
11851   {
11852     summarized_player_action |= stored_player[i].action;
11853
11854     if (!network_playing && (game.team_mode || tape.playing))
11855       stored_player[i].effective_action = stored_player[i].action;
11856   }
11857
11858   if (network_playing && !checkGameEnded())
11859     SendToServer_MovePlayer(summarized_player_action);
11860
11861   // summarize all actions at local players mapped input device position
11862   // (this allows using different input devices in single player mode)
11863   if (!network.enabled && !game.team_mode)
11864     stored_player[map_player_action[local_player->index_nr]].effective_action =
11865       summarized_player_action;
11866
11867   // summarize all actions at centered player in local team mode
11868   if (tape.recording &&
11869       setup.team_mode && !network.enabled &&
11870       setup.input_on_focus &&
11871       game.centered_player_nr != -1)
11872   {
11873     for (i = 0; i < MAX_PLAYERS; i++)
11874       stored_player[map_player_action[i]].effective_action =
11875         (i == game.centered_player_nr ? summarized_player_action : 0);
11876   }
11877
11878   if (recorded_player_action != NULL)
11879     for (i = 0; i < MAX_PLAYERS; i++)
11880       stored_player[i].effective_action = recorded_player_action[i];
11881
11882   for (i = 0; i < MAX_PLAYERS; i++)
11883   {
11884     tape_action[i] = stored_player[i].effective_action;
11885
11886     /* (this may happen in the RND game engine if a player was not present on
11887        the playfield on level start, but appeared later from a custom element */
11888     if (setup.team_mode &&
11889         tape.recording &&
11890         tape_action[i] &&
11891         !tape.player_participates[i])
11892       tape.player_participates[i] = TRUE;
11893   }
11894
11895   SetTapeActionFromMouseAction(tape_action,
11896                                &local_player->effective_mouse_action);
11897
11898   // only record actions from input devices, but not programmed actions
11899   if (tape.recording)
11900     TapeRecordAction(tape_action);
11901
11902   // remember if game was played (especially after tape stopped playing)
11903   if (!tape.playing && summarized_player_action && !checkGameFailed())
11904     game.GamePlayed = TRUE;
11905
11906 #if USE_NEW_PLAYER_ASSIGNMENTS
11907   // !!! also map player actions in single player mode !!!
11908   // if (game.team_mode)
11909   if (1)
11910   {
11911     byte mapped_action[MAX_PLAYERS];
11912
11913 #if DEBUG_PLAYER_ACTIONS
11914     for (i = 0; i < MAX_PLAYERS; i++)
11915       DebugContinued("", "%d, ", stored_player[i].effective_action);
11916 #endif
11917
11918     for (i = 0; i < MAX_PLAYERS; i++)
11919       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11920
11921     for (i = 0; i < MAX_PLAYERS; i++)
11922       stored_player[i].effective_action = mapped_action[i];
11923
11924 #if DEBUG_PLAYER_ACTIONS
11925     DebugContinued("", "=> ");
11926     for (i = 0; i < MAX_PLAYERS; i++)
11927       DebugContinued("", "%d, ", stored_player[i].effective_action);
11928     DebugContinued("game:playing:player", "\n");
11929 #endif
11930   }
11931 #if DEBUG_PLAYER_ACTIONS
11932   else
11933   {
11934     for (i = 0; i < MAX_PLAYERS; i++)
11935       DebugContinued("", "%d, ", stored_player[i].effective_action);
11936     DebugContinued("game:playing:player", "\n");
11937   }
11938 #endif
11939 #endif
11940
11941   for (i = 0; i < MAX_PLAYERS; i++)
11942   {
11943     // allow engine snapshot in case of changed movement attempt
11944     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11945         (stored_player[i].effective_action & KEY_MOTION))
11946       game.snapshot.changed_action = TRUE;
11947
11948     // allow engine snapshot in case of snapping/dropping attempt
11949     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11950         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11951       game.snapshot.changed_action = TRUE;
11952
11953     game.snapshot.last_action[i] = stored_player[i].effective_action;
11954   }
11955
11956   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11957   {
11958     GameActions_EM_Main();
11959   }
11960   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11961   {
11962     GameActions_SP_Main();
11963   }
11964   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11965   {
11966     GameActions_MM_Main();
11967   }
11968   else
11969   {
11970     GameActions_RND_Main();
11971   }
11972
11973   BlitScreenToBitmap(backbuffer);
11974
11975   CheckLevelSolved();
11976   CheckLevelTime();
11977
11978   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11979
11980   if (global.show_frames_per_second)
11981   {
11982     static unsigned int fps_counter = 0;
11983     static int fps_frames = 0;
11984     unsigned int fps_delay_ms = Counter() - fps_counter;
11985
11986     fps_frames++;
11987
11988     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11989     {
11990       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11991
11992       fps_frames = 0;
11993       fps_counter = Counter();
11994
11995       // always draw FPS to screen after FPS value was updated
11996       redraw_mask |= REDRAW_FPS;
11997     }
11998
11999     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12000     if (GetDrawDeactivationMask() == REDRAW_NONE)
12001       redraw_mask |= REDRAW_FPS;
12002   }
12003 }
12004
12005 static void GameActions_CheckSaveEngineSnapshot(void)
12006 {
12007   if (!game.snapshot.save_snapshot)
12008     return;
12009
12010   // clear flag for saving snapshot _before_ saving snapshot
12011   game.snapshot.save_snapshot = FALSE;
12012
12013   SaveEngineSnapshotToList();
12014 }
12015
12016 void GameActions(void)
12017 {
12018   GameActionsExt();
12019
12020   GameActions_CheckSaveEngineSnapshot();
12021 }
12022
12023 void GameActions_EM_Main(void)
12024 {
12025   byte effective_action[MAX_PLAYERS];
12026   int i;
12027
12028   for (i = 0; i < MAX_PLAYERS; i++)
12029     effective_action[i] = stored_player[i].effective_action;
12030
12031   GameActions_EM(effective_action);
12032 }
12033
12034 void GameActions_SP_Main(void)
12035 {
12036   byte effective_action[MAX_PLAYERS];
12037   int i;
12038
12039   for (i = 0; i < MAX_PLAYERS; i++)
12040     effective_action[i] = stored_player[i].effective_action;
12041
12042   GameActions_SP(effective_action);
12043
12044   for (i = 0; i < MAX_PLAYERS; i++)
12045   {
12046     if (stored_player[i].force_dropping)
12047       stored_player[i].action |= KEY_BUTTON_DROP;
12048
12049     stored_player[i].force_dropping = FALSE;
12050   }
12051 }
12052
12053 void GameActions_MM_Main(void)
12054 {
12055   AdvanceGfxFrame();
12056
12057   GameActions_MM(local_player->effective_mouse_action);
12058 }
12059
12060 void GameActions_RND_Main(void)
12061 {
12062   GameActions_RND();
12063 }
12064
12065 void GameActions_RND(void)
12066 {
12067   static struct MouseActionInfo mouse_action_last = { 0 };
12068   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12069   int magic_wall_x = 0, magic_wall_y = 0;
12070   int i, x, y, element, graphic, last_gfx_frame;
12071
12072   InitPlayfieldScanModeVars();
12073
12074   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12075   {
12076     SCAN_PLAYFIELD(x, y)
12077     {
12078       ChangeCount[x][y] = 0;
12079       ChangeEvent[x][y] = -1;
12080     }
12081   }
12082
12083   if (game.set_centered_player)
12084   {
12085     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12086
12087     // switching to "all players" only possible if all players fit to screen
12088     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12089     {
12090       game.centered_player_nr_next = game.centered_player_nr;
12091       game.set_centered_player = FALSE;
12092     }
12093
12094     // do not switch focus to non-existing (or non-active) player
12095     if (game.centered_player_nr_next >= 0 &&
12096         !stored_player[game.centered_player_nr_next].active)
12097     {
12098       game.centered_player_nr_next = game.centered_player_nr;
12099       game.set_centered_player = FALSE;
12100     }
12101   }
12102
12103   if (game.set_centered_player &&
12104       ScreenMovPos == 0)        // screen currently aligned at tile position
12105   {
12106     int sx, sy;
12107
12108     if (game.centered_player_nr_next == -1)
12109     {
12110       setScreenCenteredToAllPlayers(&sx, &sy);
12111     }
12112     else
12113     {
12114       sx = stored_player[game.centered_player_nr_next].jx;
12115       sy = stored_player[game.centered_player_nr_next].jy;
12116     }
12117
12118     game.centered_player_nr = game.centered_player_nr_next;
12119     game.set_centered_player = FALSE;
12120
12121     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12122     DrawGameDoorValues();
12123   }
12124
12125   // check single step mode (set flag and clear again if any player is active)
12126   game.enter_single_step_mode =
12127     (tape.single_step && tape.recording && !tape.pausing);
12128
12129   for (i = 0; i < MAX_PLAYERS; i++)
12130   {
12131     int actual_player_action = stored_player[i].effective_action;
12132
12133 #if 1
12134     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12135        - rnd_equinox_tetrachloride 048
12136        - rnd_equinox_tetrachloride_ii 096
12137        - rnd_emanuel_schmieg 002
12138        - doctor_sloan_ww 001, 020
12139     */
12140     if (stored_player[i].MovPos == 0)
12141       CheckGravityMovement(&stored_player[i]);
12142 #endif
12143
12144     // overwrite programmed action with tape action
12145     if (stored_player[i].programmed_action)
12146       actual_player_action = stored_player[i].programmed_action;
12147
12148     PlayerActions(&stored_player[i], actual_player_action);
12149
12150     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12151   }
12152
12153   // single step pause mode may already have been toggled by "ScrollPlayer()"
12154   if (game.enter_single_step_mode && !tape.pausing)
12155     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12156
12157   ScrollScreen(NULL, SCROLL_GO_ON);
12158
12159   /* for backwards compatibility, the following code emulates a fixed bug that
12160      occured when pushing elements (causing elements that just made their last
12161      pushing step to already (if possible) make their first falling step in the
12162      same game frame, which is bad); this code is also needed to use the famous
12163      "spring push bug" which is used in older levels and might be wanted to be
12164      used also in newer levels, but in this case the buggy pushing code is only
12165      affecting the "spring" element and no other elements */
12166
12167   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12168   {
12169     for (i = 0; i < MAX_PLAYERS; i++)
12170     {
12171       struct PlayerInfo *player = &stored_player[i];
12172       int x = player->jx;
12173       int y = player->jy;
12174
12175       if (player->active && player->is_pushing && player->is_moving &&
12176           IS_MOVING(x, y) &&
12177           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12178            Tile[x][y] == EL_SPRING))
12179       {
12180         ContinueMoving(x, y);
12181
12182         // continue moving after pushing (this is actually a bug)
12183         if (!IS_MOVING(x, y))
12184           Stop[x][y] = FALSE;
12185       }
12186     }
12187   }
12188
12189   SCAN_PLAYFIELD(x, y)
12190   {
12191     Last[x][y] = Tile[x][y];
12192
12193     ChangeCount[x][y] = 0;
12194     ChangeEvent[x][y] = -1;
12195
12196     // this must be handled before main playfield loop
12197     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12198     {
12199       MovDelay[x][y]--;
12200       if (MovDelay[x][y] <= 0)
12201         RemoveField(x, y);
12202     }
12203
12204     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12205     {
12206       MovDelay[x][y]--;
12207       if (MovDelay[x][y] <= 0)
12208       {
12209         int element = Store[x][y];
12210         int move_direction = MovDir[x][y];
12211         int player_index_bit = Store2[x][y];
12212
12213         Store[x][y] = 0;
12214         Store2[x][y] = 0;
12215
12216         RemoveField(x, y);
12217         TEST_DrawLevelField(x, y);
12218
12219         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12220
12221         if (IS_ENVELOPE(element))
12222           local_player->show_envelope = element;
12223       }
12224     }
12225
12226 #if DEBUG
12227     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12228     {
12229       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12230             x, y);
12231       Debug("game:playing:GameActions_RND", "This should never happen!");
12232
12233       ChangePage[x][y] = -1;
12234     }
12235 #endif
12236
12237     Stop[x][y] = FALSE;
12238     if (WasJustMoving[x][y] > 0)
12239       WasJustMoving[x][y]--;
12240     if (WasJustFalling[x][y] > 0)
12241       WasJustFalling[x][y]--;
12242     if (CheckCollision[x][y] > 0)
12243       CheckCollision[x][y]--;
12244     if (CheckImpact[x][y] > 0)
12245       CheckImpact[x][y]--;
12246
12247     GfxFrame[x][y]++;
12248
12249     /* reset finished pushing action (not done in ContinueMoving() to allow
12250        continuous pushing animation for elements with zero push delay) */
12251     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12252     {
12253       ResetGfxAnimation(x, y);
12254       TEST_DrawLevelField(x, y);
12255     }
12256
12257 #if DEBUG
12258     if (IS_BLOCKED(x, y))
12259     {
12260       int oldx, oldy;
12261
12262       Blocked2Moving(x, y, &oldx, &oldy);
12263       if (!IS_MOVING(oldx, oldy))
12264       {
12265         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12266         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12267         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12268         Debug("game:playing:GameActions_RND", "This should never happen!");
12269       }
12270     }
12271 #endif
12272   }
12273
12274   if (mouse_action.button)
12275   {
12276     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12277     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12278
12279     x = mouse_action.lx;
12280     y = mouse_action.ly;
12281     element = Tile[x][y];
12282
12283     if (new_button)
12284     {
12285       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12286       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12287                                          ch_button);
12288     }
12289
12290     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12291     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12292                                        ch_button);
12293
12294     if (level.use_step_counter)
12295     {
12296       boolean counted_click = FALSE;
12297
12298       // element clicked that can change when clicked/pressed
12299       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12300           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12301            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12302         counted_click = TRUE;
12303
12304       // element clicked that can trigger change when clicked/pressed
12305       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12306           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12307         counted_click = TRUE;
12308
12309       if (new_button && counted_click)
12310         CheckLevelTime_StepCounter();
12311     }
12312   }
12313
12314   SCAN_PLAYFIELD(x, y)
12315   {
12316     element = Tile[x][y];
12317     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12318     last_gfx_frame = GfxFrame[x][y];
12319
12320     if (element == EL_EMPTY)
12321       graphic = el2img(GfxElementEmpty[x][y]);
12322
12323     ResetGfxFrame(x, y);
12324
12325     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12326       DrawLevelGraphicAnimation(x, y, graphic);
12327
12328     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12329         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12330       ResetRandomAnimationValue(x, y);
12331
12332     SetRandomAnimationValue(x, y);
12333
12334     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12335
12336     if (IS_INACTIVE(element))
12337     {
12338       if (IS_ANIMATED(graphic))
12339         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12340
12341       continue;
12342     }
12343
12344     // this may take place after moving, so 'element' may have changed
12345     if (IS_CHANGING(x, y) &&
12346         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12347     {
12348       int page = element_info[element].event_page_nr[CE_DELAY];
12349
12350       HandleElementChange(x, y, page);
12351
12352       element = Tile[x][y];
12353       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12354     }
12355
12356     CheckNextToConditions(x, y);
12357
12358     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12359     {
12360       StartMoving(x, y);
12361
12362       element = Tile[x][y];
12363       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12364
12365       if (IS_ANIMATED(graphic) &&
12366           !IS_MOVING(x, y) &&
12367           !Stop[x][y])
12368         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12369
12370       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12371         TEST_DrawTwinkleOnField(x, y);
12372     }
12373     else if (element == EL_ACID)
12374     {
12375       if (!Stop[x][y])
12376         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12377     }
12378     else if ((element == EL_EXIT_OPEN ||
12379               element == EL_EM_EXIT_OPEN ||
12380               element == EL_SP_EXIT_OPEN ||
12381               element == EL_STEEL_EXIT_OPEN ||
12382               element == EL_EM_STEEL_EXIT_OPEN ||
12383               element == EL_SP_TERMINAL ||
12384               element == EL_SP_TERMINAL_ACTIVE ||
12385               element == EL_EXTRA_TIME ||
12386               element == EL_SHIELD_NORMAL ||
12387               element == EL_SHIELD_DEADLY) &&
12388              IS_ANIMATED(graphic))
12389       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12390     else if (IS_MOVING(x, y))
12391       ContinueMoving(x, y);
12392     else if (IS_ACTIVE_BOMB(element))
12393       CheckDynamite(x, y);
12394     else if (element == EL_AMOEBA_GROWING)
12395       AmoebaGrowing(x, y);
12396     else if (element == EL_AMOEBA_SHRINKING)
12397       AmoebaShrinking(x, y);
12398
12399 #if !USE_NEW_AMOEBA_CODE
12400     else if (IS_AMOEBALIVE(element))
12401       AmoebaReproduce(x, y);
12402 #endif
12403
12404     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12405       Life(x, y);
12406     else if (element == EL_EXIT_CLOSED)
12407       CheckExit(x, y);
12408     else if (element == EL_EM_EXIT_CLOSED)
12409       CheckExitEM(x, y);
12410     else if (element == EL_STEEL_EXIT_CLOSED)
12411       CheckExitSteel(x, y);
12412     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12413       CheckExitSteelEM(x, y);
12414     else if (element == EL_SP_EXIT_CLOSED)
12415       CheckExitSP(x, y);
12416     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12417              element == EL_EXPANDABLE_STEELWALL_GROWING)
12418       WallGrowing(x, y);
12419     else if (element == EL_EXPANDABLE_WALL ||
12420              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12421              element == EL_EXPANDABLE_WALL_VERTICAL ||
12422              element == EL_EXPANDABLE_WALL_ANY ||
12423              element == EL_BD_EXPANDABLE_WALL ||
12424              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12425              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12426              element == EL_EXPANDABLE_STEELWALL_ANY)
12427       CheckWallGrowing(x, y);
12428     else if (element == EL_FLAMES)
12429       CheckForDragon(x, y);
12430     else if (element == EL_EXPLOSION)
12431       ; // drawing of correct explosion animation is handled separately
12432     else if (element == EL_ELEMENT_SNAPPING ||
12433              element == EL_DIAGONAL_SHRINKING ||
12434              element == EL_DIAGONAL_GROWING)
12435     {
12436       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12437
12438       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12439     }
12440     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12441       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12442
12443     if (IS_BELT_ACTIVE(element))
12444       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12445
12446     if (game.magic_wall_active)
12447     {
12448       int jx = local_player->jx, jy = local_player->jy;
12449
12450       // play the element sound at the position nearest to the player
12451       if ((element == EL_MAGIC_WALL_FULL ||
12452            element == EL_MAGIC_WALL_ACTIVE ||
12453            element == EL_MAGIC_WALL_EMPTYING ||
12454            element == EL_BD_MAGIC_WALL_FULL ||
12455            element == EL_BD_MAGIC_WALL_ACTIVE ||
12456            element == EL_BD_MAGIC_WALL_EMPTYING ||
12457            element == EL_DC_MAGIC_WALL_FULL ||
12458            element == EL_DC_MAGIC_WALL_ACTIVE ||
12459            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12460           ABS(x - jx) + ABS(y - jy) <
12461           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12462       {
12463         magic_wall_x = x;
12464         magic_wall_y = y;
12465       }
12466     }
12467   }
12468
12469 #if USE_NEW_AMOEBA_CODE
12470   // new experimental amoeba growth stuff
12471   if (!(FrameCounter % 8))
12472   {
12473     static unsigned int random = 1684108901;
12474
12475     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12476     {
12477       x = RND(lev_fieldx);
12478       y = RND(lev_fieldy);
12479       element = Tile[x][y];
12480
12481       if (!IS_PLAYER(x,y) &&
12482           (element == EL_EMPTY ||
12483            CAN_GROW_INTO(element) ||
12484            element == EL_QUICKSAND_EMPTY ||
12485            element == EL_QUICKSAND_FAST_EMPTY ||
12486            element == EL_ACID_SPLASH_LEFT ||
12487            element == EL_ACID_SPLASH_RIGHT))
12488       {
12489         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12490             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12491             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12492             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12493           Tile[x][y] = EL_AMOEBA_DROP;
12494       }
12495
12496       random = random * 129 + 1;
12497     }
12498   }
12499 #endif
12500
12501   game.explosions_delayed = FALSE;
12502
12503   SCAN_PLAYFIELD(x, y)
12504   {
12505     element = Tile[x][y];
12506
12507     if (ExplodeField[x][y])
12508       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12509     else if (element == EL_EXPLOSION)
12510       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12511
12512     ExplodeField[x][y] = EX_TYPE_NONE;
12513   }
12514
12515   game.explosions_delayed = TRUE;
12516
12517   if (game.magic_wall_active)
12518   {
12519     if (!(game.magic_wall_time_left % 4))
12520     {
12521       int element = Tile[magic_wall_x][magic_wall_y];
12522
12523       if (element == EL_BD_MAGIC_WALL_FULL ||
12524           element == EL_BD_MAGIC_WALL_ACTIVE ||
12525           element == EL_BD_MAGIC_WALL_EMPTYING)
12526         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12527       else if (element == EL_DC_MAGIC_WALL_FULL ||
12528                element == EL_DC_MAGIC_WALL_ACTIVE ||
12529                element == EL_DC_MAGIC_WALL_EMPTYING)
12530         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12531       else
12532         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12533     }
12534
12535     if (game.magic_wall_time_left > 0)
12536     {
12537       game.magic_wall_time_left--;
12538
12539       if (!game.magic_wall_time_left)
12540       {
12541         SCAN_PLAYFIELD(x, y)
12542         {
12543           element = Tile[x][y];
12544
12545           if (element == EL_MAGIC_WALL_ACTIVE ||
12546               element == EL_MAGIC_WALL_FULL)
12547           {
12548             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12549             TEST_DrawLevelField(x, y);
12550           }
12551           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12552                    element == EL_BD_MAGIC_WALL_FULL)
12553           {
12554             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12555             TEST_DrawLevelField(x, y);
12556           }
12557           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12558                    element == EL_DC_MAGIC_WALL_FULL)
12559           {
12560             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12561             TEST_DrawLevelField(x, y);
12562           }
12563         }
12564
12565         game.magic_wall_active = FALSE;
12566       }
12567     }
12568   }
12569
12570   if (game.light_time_left > 0)
12571   {
12572     game.light_time_left--;
12573
12574     if (game.light_time_left == 0)
12575       RedrawAllLightSwitchesAndInvisibleElements();
12576   }
12577
12578   if (game.timegate_time_left > 0)
12579   {
12580     game.timegate_time_left--;
12581
12582     if (game.timegate_time_left == 0)
12583       CloseAllOpenTimegates();
12584   }
12585
12586   if (game.lenses_time_left > 0)
12587   {
12588     game.lenses_time_left--;
12589
12590     if (game.lenses_time_left == 0)
12591       RedrawAllInvisibleElementsForLenses();
12592   }
12593
12594   if (game.magnify_time_left > 0)
12595   {
12596     game.magnify_time_left--;
12597
12598     if (game.magnify_time_left == 0)
12599       RedrawAllInvisibleElementsForMagnifier();
12600   }
12601
12602   for (i = 0; i < MAX_PLAYERS; i++)
12603   {
12604     struct PlayerInfo *player = &stored_player[i];
12605
12606     if (SHIELD_ON(player))
12607     {
12608       if (player->shield_deadly_time_left)
12609         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12610       else if (player->shield_normal_time_left)
12611         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12612     }
12613   }
12614
12615 #if USE_DELAYED_GFX_REDRAW
12616   SCAN_PLAYFIELD(x, y)
12617   {
12618     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12619     {
12620       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12621          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12622
12623       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12624         DrawLevelField(x, y);
12625
12626       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12627         DrawLevelFieldCrumbled(x, y);
12628
12629       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12630         DrawLevelFieldCrumbledNeighbours(x, y);
12631
12632       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12633         DrawTwinkleOnField(x, y);
12634     }
12635
12636     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12637   }
12638 #endif
12639
12640   DrawAllPlayers();
12641   PlayAllPlayersSound();
12642
12643   for (i = 0; i < MAX_PLAYERS; i++)
12644   {
12645     struct PlayerInfo *player = &stored_player[i];
12646
12647     if (player->show_envelope != 0 && (!player->active ||
12648                                        player->MovPos == 0))
12649     {
12650       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12651
12652       player->show_envelope = 0;
12653     }
12654   }
12655
12656   // use random number generator in every frame to make it less predictable
12657   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12658     RND(1);
12659
12660   mouse_action_last = mouse_action;
12661 }
12662
12663 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12664 {
12665   int min_x = x, min_y = y, max_x = x, max_y = y;
12666   int scr_fieldx = getScreenFieldSizeX();
12667   int scr_fieldy = getScreenFieldSizeY();
12668   int i;
12669
12670   for (i = 0; i < MAX_PLAYERS; i++)
12671   {
12672     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12673
12674     if (!stored_player[i].active || &stored_player[i] == player)
12675       continue;
12676
12677     min_x = MIN(min_x, jx);
12678     min_y = MIN(min_y, jy);
12679     max_x = MAX(max_x, jx);
12680     max_y = MAX(max_y, jy);
12681   }
12682
12683   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12684 }
12685
12686 static boolean AllPlayersInVisibleScreen(void)
12687 {
12688   int i;
12689
12690   for (i = 0; i < MAX_PLAYERS; i++)
12691   {
12692     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12693
12694     if (!stored_player[i].active)
12695       continue;
12696
12697     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12698       return FALSE;
12699   }
12700
12701   return TRUE;
12702 }
12703
12704 void ScrollLevel(int dx, int dy)
12705 {
12706   int scroll_offset = 2 * TILEX_VAR;
12707   int x, y;
12708
12709   BlitBitmap(drawto_field, drawto_field,
12710              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12711              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12712              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12713              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12714              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12715              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12716
12717   if (dx != 0)
12718   {
12719     x = (dx == 1 ? BX1 : BX2);
12720     for (y = BY1; y <= BY2; y++)
12721       DrawScreenField(x, y);
12722   }
12723
12724   if (dy != 0)
12725   {
12726     y = (dy == 1 ? BY1 : BY2);
12727     for (x = BX1; x <= BX2; x++)
12728       DrawScreenField(x, y);
12729   }
12730
12731   redraw_mask |= REDRAW_FIELD;
12732 }
12733
12734 static boolean canFallDown(struct PlayerInfo *player)
12735 {
12736   int jx = player->jx, jy = player->jy;
12737
12738   return (IN_LEV_FIELD(jx, jy + 1) &&
12739           (IS_FREE(jx, jy + 1) ||
12740            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12741           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12742           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12743 }
12744
12745 static boolean canPassField(int x, int y, int move_dir)
12746 {
12747   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12748   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12749   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12750   int nextx = x + dx;
12751   int nexty = y + dy;
12752   int element = Tile[x][y];
12753
12754   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12755           !CAN_MOVE(element) &&
12756           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12757           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12758           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12759 }
12760
12761 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12762 {
12763   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12764   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12765   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12766   int newx = x + dx;
12767   int newy = y + dy;
12768
12769   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12770           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12771           (IS_DIGGABLE(Tile[newx][newy]) ||
12772            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12773            canPassField(newx, newy, move_dir)));
12774 }
12775
12776 static void CheckGravityMovement(struct PlayerInfo *player)
12777 {
12778   if (player->gravity && !player->programmed_action)
12779   {
12780     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12781     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12782     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12783     int jx = player->jx, jy = player->jy;
12784     boolean player_is_moving_to_valid_field =
12785       (!player_is_snapping &&
12786        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12787         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12788     boolean player_can_fall_down = canFallDown(player);
12789
12790     if (player_can_fall_down &&
12791         !player_is_moving_to_valid_field)
12792       player->programmed_action = MV_DOWN;
12793   }
12794 }
12795
12796 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12797 {
12798   return CheckGravityMovement(player);
12799
12800   if (player->gravity && !player->programmed_action)
12801   {
12802     int jx = player->jx, jy = player->jy;
12803     boolean field_under_player_is_free =
12804       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12805     boolean player_is_standing_on_valid_field =
12806       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12807        (IS_WALKABLE(Tile[jx][jy]) &&
12808         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12809
12810     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12811       player->programmed_action = MV_DOWN;
12812   }
12813 }
12814
12815 /*
12816   MovePlayerOneStep()
12817   -----------------------------------------------------------------------------
12818   dx, dy:               direction (non-diagonal) to try to move the player to
12819   real_dx, real_dy:     direction as read from input device (can be diagonal)
12820 */
12821
12822 boolean MovePlayerOneStep(struct PlayerInfo *player,
12823                           int dx, int dy, int real_dx, int real_dy)
12824 {
12825   int jx = player->jx, jy = player->jy;
12826   int new_jx = jx + dx, new_jy = jy + dy;
12827   int can_move;
12828   boolean player_can_move = !player->cannot_move;
12829
12830   if (!player->active || (!dx && !dy))
12831     return MP_NO_ACTION;
12832
12833   player->MovDir = (dx < 0 ? MV_LEFT :
12834                     dx > 0 ? MV_RIGHT :
12835                     dy < 0 ? MV_UP :
12836                     dy > 0 ? MV_DOWN :  MV_NONE);
12837
12838   if (!IN_LEV_FIELD(new_jx, new_jy))
12839     return MP_NO_ACTION;
12840
12841   if (!player_can_move)
12842   {
12843     if (player->MovPos == 0)
12844     {
12845       player->is_moving = FALSE;
12846       player->is_digging = FALSE;
12847       player->is_collecting = FALSE;
12848       player->is_snapping = FALSE;
12849       player->is_pushing = FALSE;
12850     }
12851   }
12852
12853   if (!network.enabled && game.centered_player_nr == -1 &&
12854       !AllPlayersInSight(player, new_jx, new_jy))
12855     return MP_NO_ACTION;
12856
12857   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12858   if (can_move != MP_MOVING)
12859     return can_move;
12860
12861   // check if DigField() has caused relocation of the player
12862   if (player->jx != jx || player->jy != jy)
12863     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12864
12865   StorePlayer[jx][jy] = 0;
12866   player->last_jx = jx;
12867   player->last_jy = jy;
12868   player->jx = new_jx;
12869   player->jy = new_jy;
12870   StorePlayer[new_jx][new_jy] = player->element_nr;
12871
12872   if (player->move_delay_value_next != -1)
12873   {
12874     player->move_delay_value = player->move_delay_value_next;
12875     player->move_delay_value_next = -1;
12876   }
12877
12878   player->MovPos =
12879     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12880
12881   player->step_counter++;
12882
12883   PlayerVisit[jx][jy] = FrameCounter;
12884
12885   player->is_moving = TRUE;
12886
12887 #if 1
12888   // should better be called in MovePlayer(), but this breaks some tapes
12889   ScrollPlayer(player, SCROLL_INIT);
12890 #endif
12891
12892   return MP_MOVING;
12893 }
12894
12895 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12896 {
12897   int jx = player->jx, jy = player->jy;
12898   int old_jx = jx, old_jy = jy;
12899   int moved = MP_NO_ACTION;
12900
12901   if (!player->active)
12902     return FALSE;
12903
12904   if (!dx && !dy)
12905   {
12906     if (player->MovPos == 0)
12907     {
12908       player->is_moving = FALSE;
12909       player->is_digging = FALSE;
12910       player->is_collecting = FALSE;
12911       player->is_snapping = FALSE;
12912       player->is_pushing = FALSE;
12913     }
12914
12915     return FALSE;
12916   }
12917
12918   if (player->move_delay > 0)
12919     return FALSE;
12920
12921   player->move_delay = -1;              // set to "uninitialized" value
12922
12923   // store if player is automatically moved to next field
12924   player->is_auto_moving = (player->programmed_action != MV_NONE);
12925
12926   // remove the last programmed player action
12927   player->programmed_action = 0;
12928
12929   if (player->MovPos)
12930   {
12931     // should only happen if pre-1.2 tape recordings are played
12932     // this is only for backward compatibility
12933
12934     int original_move_delay_value = player->move_delay_value;
12935
12936 #if DEBUG
12937     Debug("game:playing:MovePlayer",
12938           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12939           tape.counter);
12940 #endif
12941
12942     // scroll remaining steps with finest movement resolution
12943     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12944
12945     while (player->MovPos)
12946     {
12947       ScrollPlayer(player, SCROLL_GO_ON);
12948       ScrollScreen(NULL, SCROLL_GO_ON);
12949
12950       AdvanceFrameAndPlayerCounters(player->index_nr);
12951
12952       DrawAllPlayers();
12953       BackToFront_WithFrameDelay(0);
12954     }
12955
12956     player->move_delay_value = original_move_delay_value;
12957   }
12958
12959   player->is_active = FALSE;
12960
12961   if (player->last_move_dir & MV_HORIZONTAL)
12962   {
12963     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12964       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12965   }
12966   else
12967   {
12968     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12969       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12970   }
12971
12972   if (!moved && !player->is_active)
12973   {
12974     player->is_moving = FALSE;
12975     player->is_digging = FALSE;
12976     player->is_collecting = FALSE;
12977     player->is_snapping = FALSE;
12978     player->is_pushing = FALSE;
12979   }
12980
12981   jx = player->jx;
12982   jy = player->jy;
12983
12984   if (moved & MP_MOVING && !ScreenMovPos &&
12985       (player->index_nr == game.centered_player_nr ||
12986        game.centered_player_nr == -1))
12987   {
12988     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12989
12990     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12991     {
12992       // actual player has left the screen -- scroll in that direction
12993       if (jx != old_jx)         // player has moved horizontally
12994         scroll_x += (jx - old_jx);
12995       else                      // player has moved vertically
12996         scroll_y += (jy - old_jy);
12997     }
12998     else
12999     {
13000       int offset_raw = game.scroll_delay_value;
13001
13002       if (jx != old_jx)         // player has moved horizontally
13003       {
13004         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13005         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13006         int new_scroll_x = jx - MIDPOSX + offset_x;
13007
13008         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13009             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13010           scroll_x = new_scroll_x;
13011
13012         // don't scroll over playfield boundaries
13013         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13014
13015         // don't scroll more than one field at a time
13016         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13017
13018         // don't scroll against the player's moving direction
13019         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13020             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13021           scroll_x = old_scroll_x;
13022       }
13023       else                      // player has moved vertically
13024       {
13025         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13026         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13027         int new_scroll_y = jy - MIDPOSY + offset_y;
13028
13029         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13030             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13031           scroll_y = new_scroll_y;
13032
13033         // don't scroll over playfield boundaries
13034         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13035
13036         // don't scroll more than one field at a time
13037         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13038
13039         // don't scroll against the player's moving direction
13040         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13041             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13042           scroll_y = old_scroll_y;
13043       }
13044     }
13045
13046     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13047     {
13048       if (!network.enabled && game.centered_player_nr == -1 &&
13049           !AllPlayersInVisibleScreen())
13050       {
13051         scroll_x = old_scroll_x;
13052         scroll_y = old_scroll_y;
13053       }
13054       else
13055       {
13056         ScrollScreen(player, SCROLL_INIT);
13057         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13058       }
13059     }
13060   }
13061
13062   player->StepFrame = 0;
13063
13064   if (moved & MP_MOVING)
13065   {
13066     if (old_jx != jx && old_jy == jy)
13067       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13068     else if (old_jx == jx && old_jy != jy)
13069       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13070
13071     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13072
13073     player->last_move_dir = player->MovDir;
13074     player->is_moving = TRUE;
13075     player->is_snapping = FALSE;
13076     player->is_switching = FALSE;
13077     player->is_dropping = FALSE;
13078     player->is_dropping_pressed = FALSE;
13079     player->drop_pressed_delay = 0;
13080
13081 #if 0
13082     // should better be called here than above, but this breaks some tapes
13083     ScrollPlayer(player, SCROLL_INIT);
13084 #endif
13085   }
13086   else
13087   {
13088     CheckGravityMovementWhenNotMoving(player);
13089
13090     player->is_moving = FALSE;
13091
13092     /* at this point, the player is allowed to move, but cannot move right now
13093        (e.g. because of something blocking the way) -- ensure that the player
13094        is also allowed to move in the next frame (in old versions before 3.1.1,
13095        the player was forced to wait again for eight frames before next try) */
13096
13097     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13098       player->move_delay = 0;   // allow direct movement in the next frame
13099   }
13100
13101   if (player->move_delay == -1)         // not yet initialized by DigField()
13102     player->move_delay = player->move_delay_value;
13103
13104   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13105   {
13106     TestIfPlayerTouchesBadThing(jx, jy);
13107     TestIfPlayerTouchesCustomElement(jx, jy);
13108   }
13109
13110   if (!player->active)
13111     RemovePlayer(player);
13112
13113   return moved;
13114 }
13115
13116 void ScrollPlayer(struct PlayerInfo *player, int mode)
13117 {
13118   int jx = player->jx, jy = player->jy;
13119   int last_jx = player->last_jx, last_jy = player->last_jy;
13120   int move_stepsize = TILEX / player->move_delay_value;
13121
13122   if (!player->active)
13123     return;
13124
13125   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13126     return;
13127
13128   if (mode == SCROLL_INIT)
13129   {
13130     player->actual_frame_counter.count = FrameCounter;
13131     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13132
13133     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13134         Tile[last_jx][last_jy] == EL_EMPTY)
13135     {
13136       int last_field_block_delay = 0;   // start with no blocking at all
13137       int block_delay_adjustment = player->block_delay_adjustment;
13138
13139       // if player blocks last field, add delay for exactly one move
13140       if (player->block_last_field)
13141       {
13142         last_field_block_delay += player->move_delay_value;
13143
13144         // when blocking enabled, prevent moving up despite gravity
13145         if (player->gravity && player->MovDir == MV_UP)
13146           block_delay_adjustment = -1;
13147       }
13148
13149       // add block delay adjustment (also possible when not blocking)
13150       last_field_block_delay += block_delay_adjustment;
13151
13152       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13153       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13154     }
13155
13156     if (player->MovPos != 0)    // player has not yet reached destination
13157       return;
13158   }
13159   else if (!FrameReached(&player->actual_frame_counter))
13160     return;
13161
13162   if (player->MovPos != 0)
13163   {
13164     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13165     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13166
13167     // before DrawPlayer() to draw correct player graphic for this case
13168     if (player->MovPos == 0)
13169       CheckGravityMovement(player);
13170   }
13171
13172   if (player->MovPos == 0)      // player reached destination field
13173   {
13174     if (player->move_delay_reset_counter > 0)
13175     {
13176       player->move_delay_reset_counter--;
13177
13178       if (player->move_delay_reset_counter == 0)
13179       {
13180         // continue with normal speed after quickly moving through gate
13181         HALVE_PLAYER_SPEED(player);
13182
13183         // be able to make the next move without delay
13184         player->move_delay = 0;
13185       }
13186     }
13187
13188     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13189         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13190         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13191         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13192         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13193         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13194         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13195         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13196     {
13197       ExitPlayer(player);
13198
13199       if (game.players_still_needed == 0 &&
13200           (game.friends_still_needed == 0 ||
13201            IS_SP_ELEMENT(Tile[jx][jy])))
13202         LevelSolved();
13203     }
13204
13205     player->last_jx = jx;
13206     player->last_jy = jy;
13207
13208     // this breaks one level: "machine", level 000
13209     {
13210       int move_direction = player->MovDir;
13211       int enter_side = MV_DIR_OPPOSITE(move_direction);
13212       int leave_side = move_direction;
13213       int old_jx = last_jx;
13214       int old_jy = last_jy;
13215       int old_element = Tile[old_jx][old_jy];
13216       int new_element = Tile[jx][jy];
13217
13218       if (IS_CUSTOM_ELEMENT(old_element))
13219         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13220                                    CE_LEFT_BY_PLAYER,
13221                                    player->index_bit, leave_side);
13222
13223       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13224                                           CE_PLAYER_LEAVES_X,
13225                                           player->index_bit, leave_side);
13226
13227       if (IS_CUSTOM_ELEMENT(new_element))
13228         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13229                                    player->index_bit, enter_side);
13230
13231       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13232                                           CE_PLAYER_ENTERS_X,
13233                                           player->index_bit, enter_side);
13234
13235       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13236                                         CE_MOVE_OF_X, move_direction);
13237     }
13238
13239     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13240     {
13241       TestIfPlayerTouchesBadThing(jx, jy);
13242       TestIfPlayerTouchesCustomElement(jx, jy);
13243
13244       /* needed because pushed element has not yet reached its destination,
13245          so it would trigger a change event at its previous field location */
13246       if (!player->is_pushing)
13247         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13248
13249       if (level.finish_dig_collect &&
13250           (player->is_digging || player->is_collecting))
13251       {
13252         int last_element = player->last_removed_element;
13253         int move_direction = player->MovDir;
13254         int enter_side = MV_DIR_OPPOSITE(move_direction);
13255         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13256                             CE_PLAYER_COLLECTS_X);
13257
13258         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13259                                             player->index_bit, enter_side);
13260
13261         player->last_removed_element = EL_UNDEFINED;
13262       }
13263
13264       if (!player->active)
13265         RemovePlayer(player);
13266     }
13267
13268     if (level.use_step_counter)
13269       CheckLevelTime_StepCounter();
13270
13271     if (tape.single_step && tape.recording && !tape.pausing &&
13272         !player->programmed_action)
13273       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13274
13275     if (!player->programmed_action)
13276       CheckSaveEngineSnapshot(player);
13277   }
13278 }
13279
13280 void ScrollScreen(struct PlayerInfo *player, int mode)
13281 {
13282   static DelayCounter screen_frame_counter = { 0 };
13283
13284   if (mode == SCROLL_INIT)
13285   {
13286     // set scrolling step size according to actual player's moving speed
13287     ScrollStepSize = TILEX / player->move_delay_value;
13288
13289     screen_frame_counter.count = FrameCounter;
13290     screen_frame_counter.value = 1;
13291
13292     ScreenMovDir = player->MovDir;
13293     ScreenMovPos = player->MovPos;
13294     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13295     return;
13296   }
13297   else if (!FrameReached(&screen_frame_counter))
13298     return;
13299
13300   if (ScreenMovPos)
13301   {
13302     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13303     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13304     redraw_mask |= REDRAW_FIELD;
13305   }
13306   else
13307     ScreenMovDir = MV_NONE;
13308 }
13309
13310 void CheckNextToConditions(int x, int y)
13311 {
13312   int element = Tile[x][y];
13313
13314   if (IS_PLAYER(x, y))
13315     TestIfPlayerNextToCustomElement(x, y);
13316
13317   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13318       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13319     TestIfElementNextToCustomElement(x, y);
13320 }
13321
13322 void TestIfPlayerNextToCustomElement(int x, int y)
13323 {
13324   struct XY *xy = xy_topdown;
13325   static int trigger_sides[4][2] =
13326   {
13327     // center side       border side
13328     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13329     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13330     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13331     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13332   };
13333   int i;
13334
13335   if (!IS_PLAYER(x, y))
13336     return;
13337
13338   struct PlayerInfo *player = PLAYERINFO(x, y);
13339
13340   if (player->is_moving)
13341     return;
13342
13343   for (i = 0; i < NUM_DIRECTIONS; i++)
13344   {
13345     int xx = x + xy[i].x;
13346     int yy = y + xy[i].y;
13347     int border_side = trigger_sides[i][1];
13348     int border_element;
13349
13350     if (!IN_LEV_FIELD(xx, yy))
13351       continue;
13352
13353     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13354       continue;         // center and border element not connected
13355
13356     border_element = Tile[xx][yy];
13357
13358     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13359                                player->index_bit, border_side);
13360     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13361                                         CE_PLAYER_NEXT_TO_X,
13362                                         player->index_bit, border_side);
13363
13364     /* use player element that is initially defined in the level playfield,
13365        not the player element that corresponds to the runtime player number
13366        (example: a level that contains EL_PLAYER_3 as the only player would
13367        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13368
13369     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13370                              CE_NEXT_TO_X, border_side);
13371   }
13372 }
13373
13374 void TestIfPlayerTouchesCustomElement(int x, int y)
13375 {
13376   struct XY *xy = xy_topdown;
13377   static int trigger_sides[4][2] =
13378   {
13379     // center side       border side
13380     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13381     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13382     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13383     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13384   };
13385   static int touch_dir[4] =
13386   {
13387     MV_LEFT | MV_RIGHT,
13388     MV_UP   | MV_DOWN,
13389     MV_UP   | MV_DOWN,
13390     MV_LEFT | MV_RIGHT
13391   };
13392   int center_element = Tile[x][y];      // should always be non-moving!
13393   int i;
13394
13395   for (i = 0; i < NUM_DIRECTIONS; i++)
13396   {
13397     int xx = x + xy[i].x;
13398     int yy = y + xy[i].y;
13399     int center_side = trigger_sides[i][0];
13400     int border_side = trigger_sides[i][1];
13401     int border_element;
13402
13403     if (!IN_LEV_FIELD(xx, yy))
13404       continue;
13405
13406     if (IS_PLAYER(x, y))                // player found at center element
13407     {
13408       struct PlayerInfo *player = PLAYERINFO(x, y);
13409
13410       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13411         border_element = Tile[xx][yy];          // may be moving!
13412       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13413         border_element = Tile[xx][yy];
13414       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13415         border_element = MovingOrBlocked2Element(xx, yy);
13416       else
13417         continue;               // center and border element do not touch
13418
13419       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13420                                  player->index_bit, border_side);
13421       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13422                                           CE_PLAYER_TOUCHES_X,
13423                                           player->index_bit, border_side);
13424
13425       {
13426         /* use player element that is initially defined in the level playfield,
13427            not the player element that corresponds to the runtime player number
13428            (example: a level that contains EL_PLAYER_3 as the only player would
13429            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13430         int player_element = PLAYERINFO(x, y)->initial_element;
13431
13432         CheckElementChangeBySide(xx, yy, border_element, player_element,
13433                                  CE_TOUCHING_X, border_side);
13434       }
13435     }
13436     else if (IS_PLAYER(xx, yy))         // player found at border element
13437     {
13438       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13439
13440       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13441       {
13442         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13443           continue;             // center and border element do not touch
13444       }
13445
13446       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13447                                  player->index_bit, center_side);
13448       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13449                                           CE_PLAYER_TOUCHES_X,
13450                                           player->index_bit, center_side);
13451
13452       {
13453         /* use player element that is initially defined in the level playfield,
13454            not the player element that corresponds to the runtime player number
13455            (example: a level that contains EL_PLAYER_3 as the only player would
13456            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13457         int player_element = PLAYERINFO(xx, yy)->initial_element;
13458
13459         CheckElementChangeBySide(x, y, center_element, player_element,
13460                                  CE_TOUCHING_X, center_side);
13461       }
13462
13463       break;
13464     }
13465   }
13466 }
13467
13468 void TestIfElementNextToCustomElement(int x, int y)
13469 {
13470   struct XY *xy = xy_topdown;
13471   static int trigger_sides[4][2] =
13472   {
13473     // center side      border side
13474     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13475     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13476     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13477     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13478   };
13479   int center_element = Tile[x][y];      // should always be non-moving!
13480   int i;
13481
13482   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13483     return;
13484
13485   for (i = 0; i < NUM_DIRECTIONS; i++)
13486   {
13487     int xx = x + xy[i].x;
13488     int yy = y + xy[i].y;
13489     int border_side = trigger_sides[i][1];
13490     int border_element;
13491
13492     if (!IN_LEV_FIELD(xx, yy))
13493       continue;
13494
13495     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13496       continue;                 // center and border element not connected
13497
13498     border_element = Tile[xx][yy];
13499
13500     // check for change of center element (but change it only once)
13501     if (CheckElementChangeBySide(x, y, center_element, border_element,
13502                                  CE_NEXT_TO_X, border_side))
13503       break;
13504   }
13505 }
13506
13507 void TestIfElementTouchesCustomElement(int x, int y)
13508 {
13509   struct XY *xy = xy_topdown;
13510   static int trigger_sides[4][2] =
13511   {
13512     // center side      border side
13513     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13514     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13515     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13516     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13517   };
13518   static int touch_dir[4] =
13519   {
13520     MV_LEFT | MV_RIGHT,
13521     MV_UP   | MV_DOWN,
13522     MV_UP   | MV_DOWN,
13523     MV_LEFT | MV_RIGHT
13524   };
13525   boolean change_center_element = FALSE;
13526   int center_element = Tile[x][y];      // should always be non-moving!
13527   int border_element_old[NUM_DIRECTIONS];
13528   int i;
13529
13530   for (i = 0; i < NUM_DIRECTIONS; i++)
13531   {
13532     int xx = x + xy[i].x;
13533     int yy = y + xy[i].y;
13534     int border_element;
13535
13536     border_element_old[i] = -1;
13537
13538     if (!IN_LEV_FIELD(xx, yy))
13539       continue;
13540
13541     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13542       border_element = Tile[xx][yy];    // may be moving!
13543     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13544       border_element = Tile[xx][yy];
13545     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13546       border_element = MovingOrBlocked2Element(xx, yy);
13547     else
13548       continue;                 // center and border element do not touch
13549
13550     border_element_old[i] = border_element;
13551   }
13552
13553   for (i = 0; i < NUM_DIRECTIONS; i++)
13554   {
13555     int xx = x + xy[i].x;
13556     int yy = y + xy[i].y;
13557     int center_side = trigger_sides[i][0];
13558     int border_element = border_element_old[i];
13559
13560     if (border_element == -1)
13561       continue;
13562
13563     // check for change of border element
13564     CheckElementChangeBySide(xx, yy, border_element, center_element,
13565                              CE_TOUCHING_X, center_side);
13566
13567     // (center element cannot be player, so we dont have to check this here)
13568   }
13569
13570   for (i = 0; i < NUM_DIRECTIONS; i++)
13571   {
13572     int xx = x + xy[i].x;
13573     int yy = y + xy[i].y;
13574     int border_side = trigger_sides[i][1];
13575     int border_element = border_element_old[i];
13576
13577     if (border_element == -1)
13578       continue;
13579
13580     // check for change of center element (but change it only once)
13581     if (!change_center_element)
13582       change_center_element =
13583         CheckElementChangeBySide(x, y, center_element, border_element,
13584                                  CE_TOUCHING_X, border_side);
13585
13586     if (IS_PLAYER(xx, yy))
13587     {
13588       /* use player element that is initially defined in the level playfield,
13589          not the player element that corresponds to the runtime player number
13590          (example: a level that contains EL_PLAYER_3 as the only player would
13591          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13592       int player_element = PLAYERINFO(xx, yy)->initial_element;
13593
13594       CheckElementChangeBySide(x, y, center_element, player_element,
13595                                CE_TOUCHING_X, border_side);
13596     }
13597   }
13598 }
13599
13600 void TestIfElementHitsCustomElement(int x, int y, int direction)
13601 {
13602   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13603   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13604   int hitx = x + dx, hity = y + dy;
13605   int hitting_element = Tile[x][y];
13606   int touched_element;
13607
13608   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13609     return;
13610
13611   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13612                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13613
13614   if (IN_LEV_FIELD(hitx, hity))
13615   {
13616     int opposite_direction = MV_DIR_OPPOSITE(direction);
13617     int hitting_side = direction;
13618     int touched_side = opposite_direction;
13619     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13620                           MovDir[hitx][hity] != direction ||
13621                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13622
13623     object_hit = TRUE;
13624
13625     if (object_hit)
13626     {
13627       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13628                                CE_HITTING_X, touched_side);
13629
13630       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13631                                CE_HIT_BY_X, hitting_side);
13632
13633       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13634                                CE_HIT_BY_SOMETHING, opposite_direction);
13635
13636       if (IS_PLAYER(hitx, hity))
13637       {
13638         /* use player element that is initially defined in the level playfield,
13639            not the player element that corresponds to the runtime player number
13640            (example: a level that contains EL_PLAYER_3 as the only player would
13641            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13642         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13643
13644         CheckElementChangeBySide(x, y, hitting_element, player_element,
13645                                  CE_HITTING_X, touched_side);
13646       }
13647     }
13648   }
13649
13650   // "hitting something" is also true when hitting the playfield border
13651   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13652                            CE_HITTING_SOMETHING, direction);
13653 }
13654
13655 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13656 {
13657   int i, kill_x = -1, kill_y = -1;
13658
13659   int bad_element = -1;
13660   struct XY *test_xy = xy_topdown;
13661   static int test_dir[4] =
13662   {
13663     MV_UP,
13664     MV_LEFT,
13665     MV_RIGHT,
13666     MV_DOWN
13667   };
13668
13669   for (i = 0; i < NUM_DIRECTIONS; i++)
13670   {
13671     int test_x, test_y, test_move_dir, test_element;
13672
13673     test_x = good_x + test_xy[i].x;
13674     test_y = good_y + test_xy[i].y;
13675
13676     if (!IN_LEV_FIELD(test_x, test_y))
13677       continue;
13678
13679     test_move_dir =
13680       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13681
13682     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13683
13684     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13685        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13686     */
13687     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13688         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13689     {
13690       kill_x = test_x;
13691       kill_y = test_y;
13692       bad_element = test_element;
13693
13694       break;
13695     }
13696   }
13697
13698   if (kill_x != -1 || kill_y != -1)
13699   {
13700     if (IS_PLAYER(good_x, good_y))
13701     {
13702       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13703
13704       if (player->shield_deadly_time_left > 0 &&
13705           !IS_INDESTRUCTIBLE(bad_element))
13706         Bang(kill_x, kill_y);
13707       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13708         KillPlayer(player);
13709     }
13710     else
13711       Bang(good_x, good_y);
13712   }
13713 }
13714
13715 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13716 {
13717   int i, kill_x = -1, kill_y = -1;
13718   int bad_element = Tile[bad_x][bad_y];
13719   struct XY *test_xy = xy_topdown;
13720   static int touch_dir[4] =
13721   {
13722     MV_LEFT | MV_RIGHT,
13723     MV_UP   | MV_DOWN,
13724     MV_UP   | MV_DOWN,
13725     MV_LEFT | MV_RIGHT
13726   };
13727   static int test_dir[4] =
13728   {
13729     MV_UP,
13730     MV_LEFT,
13731     MV_RIGHT,
13732     MV_DOWN
13733   };
13734
13735   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13736     return;
13737
13738   for (i = 0; i < NUM_DIRECTIONS; i++)
13739   {
13740     int test_x, test_y, test_move_dir, test_element;
13741
13742     test_x = bad_x + test_xy[i].x;
13743     test_y = bad_y + test_xy[i].y;
13744
13745     if (!IN_LEV_FIELD(test_x, test_y))
13746       continue;
13747
13748     test_move_dir =
13749       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13750
13751     test_element = Tile[test_x][test_y];
13752
13753     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13754        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13755     */
13756     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13757         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13758     {
13759       // good thing is player or penguin that does not move away
13760       if (IS_PLAYER(test_x, test_y))
13761       {
13762         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13763
13764         if (bad_element == EL_ROBOT && player->is_moving)
13765           continue;     // robot does not kill player if he is moving
13766
13767         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13768         {
13769           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13770             continue;           // center and border element do not touch
13771         }
13772
13773         kill_x = test_x;
13774         kill_y = test_y;
13775
13776         break;
13777       }
13778       else if (test_element == EL_PENGUIN)
13779       {
13780         kill_x = test_x;
13781         kill_y = test_y;
13782
13783         break;
13784       }
13785     }
13786   }
13787
13788   if (kill_x != -1 || kill_y != -1)
13789   {
13790     if (IS_PLAYER(kill_x, kill_y))
13791     {
13792       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13793
13794       if (player->shield_deadly_time_left > 0 &&
13795           !IS_INDESTRUCTIBLE(bad_element))
13796         Bang(bad_x, bad_y);
13797       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13798         KillPlayer(player);
13799     }
13800     else
13801       Bang(kill_x, kill_y);
13802   }
13803 }
13804
13805 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13806 {
13807   int bad_element = Tile[bad_x][bad_y];
13808   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13809   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13810   int test_x = bad_x + dx, test_y = bad_y + dy;
13811   int test_move_dir, test_element;
13812   int kill_x = -1, kill_y = -1;
13813
13814   if (!IN_LEV_FIELD(test_x, test_y))
13815     return;
13816
13817   test_move_dir =
13818     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13819
13820   test_element = Tile[test_x][test_y];
13821
13822   if (test_move_dir != bad_move_dir)
13823   {
13824     // good thing can be player or penguin that does not move away
13825     if (IS_PLAYER(test_x, test_y))
13826     {
13827       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13828
13829       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13830          player as being hit when he is moving towards the bad thing, because
13831          the "get hit by" condition would be lost after the player stops) */
13832       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13833         return;         // player moves away from bad thing
13834
13835       kill_x = test_x;
13836       kill_y = test_y;
13837     }
13838     else if (test_element == EL_PENGUIN)
13839     {
13840       kill_x = test_x;
13841       kill_y = test_y;
13842     }
13843   }
13844
13845   if (kill_x != -1 || kill_y != -1)
13846   {
13847     if (IS_PLAYER(kill_x, kill_y))
13848     {
13849       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13850
13851       if (player->shield_deadly_time_left > 0 &&
13852           !IS_INDESTRUCTIBLE(bad_element))
13853         Bang(bad_x, bad_y);
13854       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13855         KillPlayer(player);
13856     }
13857     else
13858       Bang(kill_x, kill_y);
13859   }
13860 }
13861
13862 void TestIfPlayerTouchesBadThing(int x, int y)
13863 {
13864   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13865 }
13866
13867 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13868 {
13869   TestIfGoodThingHitsBadThing(x, y, move_dir);
13870 }
13871
13872 void TestIfBadThingTouchesPlayer(int x, int y)
13873 {
13874   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13875 }
13876
13877 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13878 {
13879   TestIfBadThingHitsGoodThing(x, y, move_dir);
13880 }
13881
13882 void TestIfFriendTouchesBadThing(int x, int y)
13883 {
13884   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13885 }
13886
13887 void TestIfBadThingTouchesFriend(int x, int y)
13888 {
13889   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13890 }
13891
13892 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13893 {
13894   int i, kill_x = bad_x, kill_y = bad_y;
13895   struct XY *xy = xy_topdown;
13896
13897   for (i = 0; i < NUM_DIRECTIONS; i++)
13898   {
13899     int x, y, element;
13900
13901     x = bad_x + xy[i].x;
13902     y = bad_y + xy[i].y;
13903     if (!IN_LEV_FIELD(x, y))
13904       continue;
13905
13906     element = Tile[x][y];
13907     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13908         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13909     {
13910       kill_x = x;
13911       kill_y = y;
13912       break;
13913     }
13914   }
13915
13916   if (kill_x != bad_x || kill_y != bad_y)
13917     Bang(bad_x, bad_y);
13918 }
13919
13920 void KillPlayer(struct PlayerInfo *player)
13921 {
13922   int jx = player->jx, jy = player->jy;
13923
13924   if (!player->active)
13925     return;
13926
13927 #if 0
13928   Debug("game:playing:KillPlayer",
13929         "0: killed == %d, active == %d, reanimated == %d",
13930         player->killed, player->active, player->reanimated);
13931 #endif
13932
13933   /* the following code was introduced to prevent an infinite loop when calling
13934      -> Bang()
13935      -> CheckTriggeredElementChangeExt()
13936      -> ExecuteCustomElementAction()
13937      -> KillPlayer()
13938      -> (infinitely repeating the above sequence of function calls)
13939      which occurs when killing the player while having a CE with the setting
13940      "kill player X when explosion of <player X>"; the solution using a new
13941      field "player->killed" was chosen for backwards compatibility, although
13942      clever use of the fields "player->active" etc. would probably also work */
13943 #if 1
13944   if (player->killed)
13945     return;
13946 #endif
13947
13948   player->killed = TRUE;
13949
13950   // remove accessible field at the player's position
13951   RemoveField(jx, jy);
13952
13953   // deactivate shield (else Bang()/Explode() would not work right)
13954   player->shield_normal_time_left = 0;
13955   player->shield_deadly_time_left = 0;
13956
13957 #if 0
13958   Debug("game:playing:KillPlayer",
13959         "1: killed == %d, active == %d, reanimated == %d",
13960         player->killed, player->active, player->reanimated);
13961 #endif
13962
13963   Bang(jx, jy);
13964
13965 #if 0
13966   Debug("game:playing:KillPlayer",
13967         "2: killed == %d, active == %d, reanimated == %d",
13968         player->killed, player->active, player->reanimated);
13969 #endif
13970
13971   if (player->reanimated)       // killed player may have been reanimated
13972     player->killed = player->reanimated = FALSE;
13973   else
13974     BuryPlayer(player);
13975 }
13976
13977 static void KillPlayerUnlessEnemyProtected(int x, int y)
13978 {
13979   if (!PLAYER_ENEMY_PROTECTED(x, y))
13980     KillPlayer(PLAYERINFO(x, y));
13981 }
13982
13983 static void KillPlayerUnlessExplosionProtected(int x, int y)
13984 {
13985   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13986     KillPlayer(PLAYERINFO(x, y));
13987 }
13988
13989 void BuryPlayer(struct PlayerInfo *player)
13990 {
13991   int jx = player->jx, jy = player->jy;
13992
13993   if (!player->active)
13994     return;
13995
13996   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13997
13998   RemovePlayer(player);
13999
14000   player->buried = TRUE;
14001
14002   if (game.all_players_gone)
14003     game.GameOver = TRUE;
14004 }
14005
14006 void RemovePlayer(struct PlayerInfo *player)
14007 {
14008   int jx = player->jx, jy = player->jy;
14009   int i, found = FALSE;
14010
14011   player->present = FALSE;
14012   player->active = FALSE;
14013
14014   // required for some CE actions (even if the player is not active anymore)
14015   player->MovPos = 0;
14016
14017   if (!ExplodeField[jx][jy])
14018     StorePlayer[jx][jy] = 0;
14019
14020   if (player->is_moving)
14021     TEST_DrawLevelField(player->last_jx, player->last_jy);
14022
14023   for (i = 0; i < MAX_PLAYERS; i++)
14024     if (stored_player[i].active)
14025       found = TRUE;
14026
14027   if (!found)
14028   {
14029     game.all_players_gone = TRUE;
14030     game.GameOver = TRUE;
14031   }
14032
14033   game.exit_x = game.robot_wheel_x = jx;
14034   game.exit_y = game.robot_wheel_y = jy;
14035 }
14036
14037 void ExitPlayer(struct PlayerInfo *player)
14038 {
14039   DrawPlayer(player);   // needed here only to cleanup last field
14040   RemovePlayer(player);
14041
14042   if (game.players_still_needed > 0)
14043     game.players_still_needed--;
14044 }
14045
14046 static void SetFieldForSnapping(int x, int y, int element, int direction,
14047                                 int player_index_bit)
14048 {
14049   struct ElementInfo *ei = &element_info[element];
14050   int direction_bit = MV_DIR_TO_BIT(direction);
14051   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14052   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14053                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14054
14055   Tile[x][y] = EL_ELEMENT_SNAPPING;
14056   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14057   MovDir[x][y] = direction;
14058   Store[x][y] = element;
14059   Store2[x][y] = player_index_bit;
14060
14061   ResetGfxAnimation(x, y);
14062
14063   GfxElement[x][y] = element;
14064   GfxAction[x][y] = action;
14065   GfxDir[x][y] = direction;
14066   GfxFrame[x][y] = -1;
14067 }
14068
14069 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14070                                    int player_index_bit)
14071 {
14072   TestIfElementTouchesCustomElement(x, y);      // for empty space
14073
14074   if (level.finish_dig_collect)
14075   {
14076     int dig_side = MV_DIR_OPPOSITE(direction);
14077     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14078                         CE_PLAYER_COLLECTS_X);
14079
14080     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14081                                         player_index_bit, dig_side);
14082     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14083                                         player_index_bit, dig_side);
14084   }
14085 }
14086
14087 /*
14088   =============================================================================
14089   checkDiagonalPushing()
14090   -----------------------------------------------------------------------------
14091   check if diagonal input device direction results in pushing of object
14092   (by checking if the alternative direction is walkable, diggable, ...)
14093   =============================================================================
14094 */
14095
14096 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14097                                     int x, int y, int real_dx, int real_dy)
14098 {
14099   int jx, jy, dx, dy, xx, yy;
14100
14101   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14102     return TRUE;
14103
14104   // diagonal direction: check alternative direction
14105   jx = player->jx;
14106   jy = player->jy;
14107   dx = x - jx;
14108   dy = y - jy;
14109   xx = jx + (dx == 0 ? real_dx : 0);
14110   yy = jy + (dy == 0 ? real_dy : 0);
14111
14112   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14113 }
14114
14115 /*
14116   =============================================================================
14117   DigField()
14118   -----------------------------------------------------------------------------
14119   x, y:                 field next to player (non-diagonal) to try to dig to
14120   real_dx, real_dy:     direction as read from input device (can be diagonal)
14121   =============================================================================
14122 */
14123
14124 static int DigField(struct PlayerInfo *player,
14125                     int oldx, int oldy, int x, int y,
14126                     int real_dx, int real_dy, int mode)
14127 {
14128   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14129   boolean player_was_pushing = player->is_pushing;
14130   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14131   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14132   int jx = oldx, jy = oldy;
14133   int dx = x - jx, dy = y - jy;
14134   int nextx = x + dx, nexty = y + dy;
14135   int move_direction = (dx == -1 ? MV_LEFT  :
14136                         dx == +1 ? MV_RIGHT :
14137                         dy == -1 ? MV_UP    :
14138                         dy == +1 ? MV_DOWN  : MV_NONE);
14139   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14140   int dig_side = MV_DIR_OPPOSITE(move_direction);
14141   int old_element = Tile[jx][jy];
14142   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14143   int collect_count;
14144
14145   if (is_player)                // function can also be called by EL_PENGUIN
14146   {
14147     if (player->MovPos == 0)
14148     {
14149       player->is_digging = FALSE;
14150       player->is_collecting = FALSE;
14151     }
14152
14153     if (player->MovPos == 0)    // last pushing move finished
14154       player->is_pushing = FALSE;
14155
14156     if (mode == DF_NO_PUSH)     // player just stopped pushing
14157     {
14158       player->is_switching = FALSE;
14159       player->push_delay = -1;
14160
14161       return MP_NO_ACTION;
14162     }
14163   }
14164   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14165     old_element = Back[jx][jy];
14166
14167   // in case of element dropped at player position, check background
14168   else if (Back[jx][jy] != EL_EMPTY &&
14169            game.engine_version >= VERSION_IDENT(2,2,0,0))
14170     old_element = Back[jx][jy];
14171
14172   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14173     return MP_NO_ACTION;        // field has no opening in this direction
14174
14175   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14176     return MP_NO_ACTION;        // field has no opening in this direction
14177
14178   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14179   {
14180     SplashAcid(x, y);
14181
14182     Tile[jx][jy] = player->artwork_element;
14183     InitMovingField(jx, jy, MV_DOWN);
14184     Store[jx][jy] = EL_ACID;
14185     ContinueMoving(jx, jy);
14186     BuryPlayer(player);
14187
14188     return MP_DONT_RUN_INTO;
14189   }
14190
14191   if (player_can_move && DONT_RUN_INTO(element))
14192   {
14193     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14194
14195     return MP_DONT_RUN_INTO;
14196   }
14197
14198   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14199     return MP_NO_ACTION;
14200
14201   collect_count = element_info[element].collect_count_initial;
14202
14203   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14204     return MP_NO_ACTION;
14205
14206   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14207     player_can_move = player_can_move_or_snap;
14208
14209   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14210       game.engine_version >= VERSION_IDENT(2,2,0,0))
14211   {
14212     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14213                                player->index_bit, dig_side);
14214     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14215                                         player->index_bit, dig_side);
14216
14217     if (element == EL_DC_LANDMINE)
14218       Bang(x, y);
14219
14220     if (Tile[x][y] != element)          // field changed by snapping
14221       return MP_ACTION;
14222
14223     return MP_NO_ACTION;
14224   }
14225
14226   if (player->gravity && is_player && !player->is_auto_moving &&
14227       canFallDown(player) && move_direction != MV_DOWN &&
14228       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14229     return MP_NO_ACTION;        // player cannot walk here due to gravity
14230
14231   if (player_can_move &&
14232       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14233   {
14234     int sound_element = SND_ELEMENT(element);
14235     int sound_action = ACTION_WALKING;
14236
14237     if (IS_RND_GATE(element))
14238     {
14239       if (!player->key[RND_GATE_NR(element)])
14240         return MP_NO_ACTION;
14241     }
14242     else if (IS_RND_GATE_GRAY(element))
14243     {
14244       if (!player->key[RND_GATE_GRAY_NR(element)])
14245         return MP_NO_ACTION;
14246     }
14247     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14248     {
14249       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14250         return MP_NO_ACTION;
14251     }
14252     else if (element == EL_EXIT_OPEN ||
14253              element == EL_EM_EXIT_OPEN ||
14254              element == EL_EM_EXIT_OPENING ||
14255              element == EL_STEEL_EXIT_OPEN ||
14256              element == EL_EM_STEEL_EXIT_OPEN ||
14257              element == EL_EM_STEEL_EXIT_OPENING ||
14258              element == EL_SP_EXIT_OPEN ||
14259              element == EL_SP_EXIT_OPENING)
14260     {
14261       sound_action = ACTION_PASSING;    // player is passing exit
14262     }
14263     else if (element == EL_EMPTY)
14264     {
14265       sound_action = ACTION_MOVING;             // nothing to walk on
14266     }
14267
14268     // play sound from background or player, whatever is available
14269     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14270       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14271     else
14272       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14273   }
14274   else if (player_can_move &&
14275            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14276   {
14277     if (!ACCESS_FROM(element, opposite_direction))
14278       return MP_NO_ACTION;      // field not accessible from this direction
14279
14280     if (CAN_MOVE(element))      // only fixed elements can be passed!
14281       return MP_NO_ACTION;
14282
14283     if (IS_EM_GATE(element))
14284     {
14285       if (!player->key[EM_GATE_NR(element)])
14286         return MP_NO_ACTION;
14287     }
14288     else if (IS_EM_GATE_GRAY(element))
14289     {
14290       if (!player->key[EM_GATE_GRAY_NR(element)])
14291         return MP_NO_ACTION;
14292     }
14293     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14294     {
14295       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14296         return MP_NO_ACTION;
14297     }
14298     else if (IS_EMC_GATE(element))
14299     {
14300       if (!player->key[EMC_GATE_NR(element)])
14301         return MP_NO_ACTION;
14302     }
14303     else if (IS_EMC_GATE_GRAY(element))
14304     {
14305       if (!player->key[EMC_GATE_GRAY_NR(element)])
14306         return MP_NO_ACTION;
14307     }
14308     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14309     {
14310       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14311         return MP_NO_ACTION;
14312     }
14313     else if (element == EL_DC_GATE_WHITE ||
14314              element == EL_DC_GATE_WHITE_GRAY ||
14315              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14316     {
14317       if (player->num_white_keys == 0)
14318         return MP_NO_ACTION;
14319
14320       player->num_white_keys--;
14321     }
14322     else if (IS_SP_PORT(element))
14323     {
14324       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14325           element == EL_SP_GRAVITY_PORT_RIGHT ||
14326           element == EL_SP_GRAVITY_PORT_UP ||
14327           element == EL_SP_GRAVITY_PORT_DOWN)
14328         player->gravity = !player->gravity;
14329       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14330                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14331                element == EL_SP_GRAVITY_ON_PORT_UP ||
14332                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14333         player->gravity = TRUE;
14334       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14335                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14336                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14337                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14338         player->gravity = FALSE;
14339     }
14340
14341     // automatically move to the next field with double speed
14342     player->programmed_action = move_direction;
14343
14344     if (player->move_delay_reset_counter == 0)
14345     {
14346       player->move_delay_reset_counter = 2;     // two double speed steps
14347
14348       DOUBLE_PLAYER_SPEED(player);
14349     }
14350
14351     PlayLevelSoundAction(x, y, ACTION_PASSING);
14352   }
14353   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14354   {
14355     RemoveField(x, y);
14356
14357     if (mode != DF_SNAP)
14358     {
14359       GfxElement[x][y] = GFX_ELEMENT(element);
14360       player->is_digging = TRUE;
14361     }
14362
14363     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14364
14365     // use old behaviour for old levels (digging)
14366     if (!level.finish_dig_collect)
14367     {
14368       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14369                                           player->index_bit, dig_side);
14370
14371       // if digging triggered player relocation, finish digging tile
14372       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14373         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14374     }
14375
14376     if (mode == DF_SNAP)
14377     {
14378       if (level.block_snap_field)
14379         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14380       else
14381         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14382
14383       // use old behaviour for old levels (snapping)
14384       if (!level.finish_dig_collect)
14385         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14386                                             player->index_bit, dig_side);
14387     }
14388   }
14389   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14390   {
14391     RemoveField(x, y);
14392
14393     if (is_player && mode != DF_SNAP)
14394     {
14395       GfxElement[x][y] = element;
14396       player->is_collecting = TRUE;
14397     }
14398
14399     if (element == EL_SPEED_PILL)
14400     {
14401       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14402     }
14403     else if (element == EL_EXTRA_TIME && level.time > 0)
14404     {
14405       TimeLeft += level.extra_time;
14406
14407       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14408
14409       DisplayGameControlValues();
14410     }
14411     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14412     {
14413       int shield_time = (element == EL_SHIELD_DEADLY ?
14414                          level.shield_deadly_time :
14415                          level.shield_normal_time);
14416
14417       player->shield_normal_time_left += shield_time;
14418       if (element == EL_SHIELD_DEADLY)
14419         player->shield_deadly_time_left += shield_time;
14420     }
14421     else if (element == EL_DYNAMITE ||
14422              element == EL_EM_DYNAMITE ||
14423              element == EL_SP_DISK_RED)
14424     {
14425       if (player->inventory_size < MAX_INVENTORY_SIZE)
14426         player->inventory_element[player->inventory_size++] = element;
14427
14428       DrawGameDoorValues();
14429     }
14430     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14431     {
14432       player->dynabomb_count++;
14433       player->dynabombs_left++;
14434     }
14435     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14436     {
14437       player->dynabomb_size++;
14438     }
14439     else if (element == EL_DYNABOMB_INCREASE_POWER)
14440     {
14441       player->dynabomb_xl = TRUE;
14442     }
14443     else if (IS_KEY(element))
14444     {
14445       player->key[KEY_NR(element)] = TRUE;
14446
14447       DrawGameDoorValues();
14448     }
14449     else if (element == EL_DC_KEY_WHITE)
14450     {
14451       player->num_white_keys++;
14452
14453       // display white keys?
14454       // DrawGameDoorValues();
14455     }
14456     else if (IS_ENVELOPE(element))
14457     {
14458       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14459
14460       if (!wait_for_snapping)
14461         player->show_envelope = element;
14462     }
14463     else if (element == EL_EMC_LENSES)
14464     {
14465       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14466
14467       RedrawAllInvisibleElementsForLenses();
14468     }
14469     else if (element == EL_EMC_MAGNIFIER)
14470     {
14471       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14472
14473       RedrawAllInvisibleElementsForMagnifier();
14474     }
14475     else if (IS_DROPPABLE(element) ||
14476              IS_THROWABLE(element))     // can be collected and dropped
14477     {
14478       int i;
14479
14480       if (collect_count == 0)
14481         player->inventory_infinite_element = element;
14482       else
14483         for (i = 0; i < collect_count; i++)
14484           if (player->inventory_size < MAX_INVENTORY_SIZE)
14485             player->inventory_element[player->inventory_size++] = element;
14486
14487       DrawGameDoorValues();
14488     }
14489     else if (collect_count > 0)
14490     {
14491       game.gems_still_needed -= collect_count;
14492       if (game.gems_still_needed < 0)
14493         game.gems_still_needed = 0;
14494
14495       game.snapshot.collected_item = TRUE;
14496
14497       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14498
14499       DisplayGameControlValues();
14500     }
14501
14502     RaiseScoreElement(element);
14503     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14504
14505     // use old behaviour for old levels (collecting)
14506     if (!level.finish_dig_collect && is_player)
14507     {
14508       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14509                                           player->index_bit, dig_side);
14510
14511       // if collecting triggered player relocation, finish collecting tile
14512       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14513         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14514     }
14515
14516     if (mode == DF_SNAP)
14517     {
14518       if (level.block_snap_field)
14519         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14520       else
14521         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14522
14523       // use old behaviour for old levels (snapping)
14524       if (!level.finish_dig_collect)
14525         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14526                                             player->index_bit, dig_side);
14527     }
14528   }
14529   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14530   {
14531     if (mode == DF_SNAP && element != EL_BD_ROCK)
14532       return MP_NO_ACTION;
14533
14534     if (CAN_FALL(element) && dy)
14535       return MP_NO_ACTION;
14536
14537     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14538         !(element == EL_SPRING && level.use_spring_bug))
14539       return MP_NO_ACTION;
14540
14541     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14542         ((move_direction & MV_VERTICAL &&
14543           ((element_info[element].move_pattern & MV_LEFT &&
14544             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14545            (element_info[element].move_pattern & MV_RIGHT &&
14546             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14547          (move_direction & MV_HORIZONTAL &&
14548           ((element_info[element].move_pattern & MV_UP &&
14549             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14550            (element_info[element].move_pattern & MV_DOWN &&
14551             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14552       return MP_NO_ACTION;
14553
14554     // do not push elements already moving away faster than player
14555     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14556         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14557       return MP_NO_ACTION;
14558
14559     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14560     {
14561       if (player->push_delay_value == -1 || !player_was_pushing)
14562         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14563     }
14564     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14565     {
14566       if (player->push_delay_value == -1)
14567         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14568     }
14569     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14570     {
14571       if (!player->is_pushing)
14572         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14573     }
14574
14575     player->is_pushing = TRUE;
14576     player->is_active = TRUE;
14577
14578     if (!(IN_LEV_FIELD(nextx, nexty) &&
14579           (IS_FREE(nextx, nexty) ||
14580            (IS_SB_ELEMENT(element) &&
14581             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14582            (IS_CUSTOM_ELEMENT(element) &&
14583             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14584       return MP_NO_ACTION;
14585
14586     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14587       return MP_NO_ACTION;
14588
14589     if (player->push_delay == -1)       // new pushing; restart delay
14590       player->push_delay = 0;
14591
14592     if (player->push_delay < player->push_delay_value &&
14593         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14594         element != EL_SPRING && element != EL_BALLOON)
14595     {
14596       // make sure that there is no move delay before next try to push
14597       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14598         player->move_delay = 0;
14599
14600       return MP_NO_ACTION;
14601     }
14602
14603     if (IS_CUSTOM_ELEMENT(element) &&
14604         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14605     {
14606       if (!DigFieldByCE(nextx, nexty, element))
14607         return MP_NO_ACTION;
14608     }
14609
14610     if (IS_SB_ELEMENT(element))
14611     {
14612       boolean sokoban_task_solved = FALSE;
14613
14614       if (element == EL_SOKOBAN_FIELD_FULL)
14615       {
14616         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14617
14618         IncrementSokobanFieldsNeeded();
14619         IncrementSokobanObjectsNeeded();
14620       }
14621
14622       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14623       {
14624         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14625
14626         DecrementSokobanFieldsNeeded();
14627         DecrementSokobanObjectsNeeded();
14628
14629         // sokoban object was pushed from empty field to sokoban field
14630         if (Back[x][y] == EL_EMPTY)
14631           sokoban_task_solved = TRUE;
14632       }
14633
14634       Tile[x][y] = EL_SOKOBAN_OBJECT;
14635
14636       if (Back[x][y] == Back[nextx][nexty])
14637         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14638       else if (Back[x][y] != 0)
14639         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14640                                     ACTION_EMPTYING);
14641       else
14642         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14643                                     ACTION_FILLING);
14644
14645       if (sokoban_task_solved &&
14646           game.sokoban_fields_still_needed == 0 &&
14647           game.sokoban_objects_still_needed == 0 &&
14648           level.auto_exit_sokoban)
14649       {
14650         game.players_still_needed = 0;
14651
14652         LevelSolved();
14653
14654         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14655       }
14656     }
14657     else
14658       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14659
14660     InitMovingField(x, y, move_direction);
14661     GfxAction[x][y] = ACTION_PUSHING;
14662
14663     if (mode == DF_SNAP)
14664       ContinueMoving(x, y);
14665     else
14666       MovPos[x][y] = (dx != 0 ? dx : dy);
14667
14668     Pushed[x][y] = TRUE;
14669     Pushed[nextx][nexty] = TRUE;
14670
14671     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14672       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14673     else
14674       player->push_delay_value = -1;    // get new value later
14675
14676     // check for element change _after_ element has been pushed
14677     if (game.use_change_when_pushing_bug)
14678     {
14679       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14680                                  player->index_bit, dig_side);
14681       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14682                                           player->index_bit, dig_side);
14683     }
14684   }
14685   else if (IS_SWITCHABLE(element))
14686   {
14687     if (PLAYER_SWITCHING(player, x, y))
14688     {
14689       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14690                                           player->index_bit, dig_side);
14691
14692       return MP_ACTION;
14693     }
14694
14695     player->is_switching = TRUE;
14696     player->switch_x = x;
14697     player->switch_y = y;
14698
14699     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14700
14701     if (element == EL_ROBOT_WHEEL)
14702     {
14703       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14704
14705       game.robot_wheel_x = x;
14706       game.robot_wheel_y = y;
14707       game.robot_wheel_active = TRUE;
14708
14709       TEST_DrawLevelField(x, y);
14710     }
14711     else if (element == EL_SP_TERMINAL)
14712     {
14713       int xx, yy;
14714
14715       SCAN_PLAYFIELD(xx, yy)
14716       {
14717         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14718         {
14719           Bang(xx, yy);
14720         }
14721         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14722         {
14723           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14724
14725           ResetGfxAnimation(xx, yy);
14726           TEST_DrawLevelField(xx, yy);
14727         }
14728       }
14729     }
14730     else if (IS_BELT_SWITCH(element))
14731     {
14732       ToggleBeltSwitch(x, y);
14733     }
14734     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14735              element == EL_SWITCHGATE_SWITCH_DOWN ||
14736              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14737              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14738     {
14739       ToggleSwitchgateSwitch();
14740     }
14741     else if (element == EL_LIGHT_SWITCH ||
14742              element == EL_LIGHT_SWITCH_ACTIVE)
14743     {
14744       ToggleLightSwitch(x, y);
14745     }
14746     else if (element == EL_TIMEGATE_SWITCH ||
14747              element == EL_DC_TIMEGATE_SWITCH)
14748     {
14749       ActivateTimegateSwitch(x, y);
14750     }
14751     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14752              element == EL_BALLOON_SWITCH_RIGHT ||
14753              element == EL_BALLOON_SWITCH_UP    ||
14754              element == EL_BALLOON_SWITCH_DOWN  ||
14755              element == EL_BALLOON_SWITCH_NONE  ||
14756              element == EL_BALLOON_SWITCH_ANY)
14757     {
14758       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14759                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14760                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14761                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14762                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14763                              move_direction);
14764     }
14765     else if (element == EL_LAMP)
14766     {
14767       Tile[x][y] = EL_LAMP_ACTIVE;
14768       game.lights_still_needed--;
14769
14770       ResetGfxAnimation(x, y);
14771       TEST_DrawLevelField(x, y);
14772     }
14773     else if (element == EL_TIME_ORB_FULL)
14774     {
14775       Tile[x][y] = EL_TIME_ORB_EMPTY;
14776
14777       if (level.time > 0 || level.use_time_orb_bug)
14778       {
14779         TimeLeft += level.time_orb_time;
14780         game.no_level_time_limit = FALSE;
14781
14782         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14783
14784         DisplayGameControlValues();
14785       }
14786
14787       ResetGfxAnimation(x, y);
14788       TEST_DrawLevelField(x, y);
14789     }
14790     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14791              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14792     {
14793       int xx, yy;
14794
14795       game.ball_active = !game.ball_active;
14796
14797       SCAN_PLAYFIELD(xx, yy)
14798       {
14799         int e = Tile[xx][yy];
14800
14801         if (game.ball_active)
14802         {
14803           if (e == EL_EMC_MAGIC_BALL)
14804             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14805           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14806             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14807         }
14808         else
14809         {
14810           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14811             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14812           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14813             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14814         }
14815       }
14816     }
14817
14818     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14819                                         player->index_bit, dig_side);
14820
14821     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14822                                         player->index_bit, dig_side);
14823
14824     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14825                                         player->index_bit, dig_side);
14826
14827     return MP_ACTION;
14828   }
14829   else
14830   {
14831     if (!PLAYER_SWITCHING(player, x, y))
14832     {
14833       player->is_switching = TRUE;
14834       player->switch_x = x;
14835       player->switch_y = y;
14836
14837       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14838                                  player->index_bit, dig_side);
14839       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14840                                           player->index_bit, dig_side);
14841
14842       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14843                                  player->index_bit, dig_side);
14844       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14845                                           player->index_bit, dig_side);
14846     }
14847
14848     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14849                                player->index_bit, dig_side);
14850     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14851                                         player->index_bit, dig_side);
14852
14853     return MP_NO_ACTION;
14854   }
14855
14856   player->push_delay = -1;
14857
14858   if (is_player)                // function can also be called by EL_PENGUIN
14859   {
14860     if (Tile[x][y] != element)          // really digged/collected something
14861     {
14862       player->is_collecting = !player->is_digging;
14863       player->is_active = TRUE;
14864
14865       player->last_removed_element = element;
14866     }
14867   }
14868
14869   return MP_MOVING;
14870 }
14871
14872 static boolean DigFieldByCE(int x, int y, int digging_element)
14873 {
14874   int element = Tile[x][y];
14875
14876   if (!IS_FREE(x, y))
14877   {
14878     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14879                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14880                   ACTION_BREAKING);
14881
14882     // no element can dig solid indestructible elements
14883     if (IS_INDESTRUCTIBLE(element) &&
14884         !IS_DIGGABLE(element) &&
14885         !IS_COLLECTIBLE(element))
14886       return FALSE;
14887
14888     if (AmoebaNr[x][y] &&
14889         (element == EL_AMOEBA_FULL ||
14890          element == EL_BD_AMOEBA ||
14891          element == EL_AMOEBA_GROWING))
14892     {
14893       AmoebaCnt[AmoebaNr[x][y]]--;
14894       AmoebaCnt2[AmoebaNr[x][y]]--;
14895     }
14896
14897     if (IS_MOVING(x, y))
14898       RemoveMovingField(x, y);
14899     else
14900     {
14901       RemoveField(x, y);
14902       TEST_DrawLevelField(x, y);
14903     }
14904
14905     // if digged element was about to explode, prevent the explosion
14906     ExplodeField[x][y] = EX_TYPE_NONE;
14907
14908     PlayLevelSoundAction(x, y, action);
14909   }
14910
14911   Store[x][y] = EL_EMPTY;
14912
14913   // this makes it possible to leave the removed element again
14914   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14915     Store[x][y] = element;
14916
14917   return TRUE;
14918 }
14919
14920 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14921 {
14922   int jx = player->jx, jy = player->jy;
14923   int x = jx + dx, y = jy + dy;
14924   int snap_direction = (dx == -1 ? MV_LEFT  :
14925                         dx == +1 ? MV_RIGHT :
14926                         dy == -1 ? MV_UP    :
14927                         dy == +1 ? MV_DOWN  : MV_NONE);
14928   boolean can_continue_snapping = (level.continuous_snapping &&
14929                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14930
14931   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14932     return FALSE;
14933
14934   if (!player->active || !IN_LEV_FIELD(x, y))
14935     return FALSE;
14936
14937   if (dx && dy)
14938     return FALSE;
14939
14940   if (!dx && !dy)
14941   {
14942     if (player->MovPos == 0)
14943       player->is_pushing = FALSE;
14944
14945     player->is_snapping = FALSE;
14946
14947     if (player->MovPos == 0)
14948     {
14949       player->is_moving = FALSE;
14950       player->is_digging = FALSE;
14951       player->is_collecting = FALSE;
14952     }
14953
14954     return FALSE;
14955   }
14956
14957   // prevent snapping with already pressed snap key when not allowed
14958   if (player->is_snapping && !can_continue_snapping)
14959     return FALSE;
14960
14961   player->MovDir = snap_direction;
14962
14963   if (player->MovPos == 0)
14964   {
14965     player->is_moving = FALSE;
14966     player->is_digging = FALSE;
14967     player->is_collecting = FALSE;
14968   }
14969
14970   player->is_dropping = FALSE;
14971   player->is_dropping_pressed = FALSE;
14972   player->drop_pressed_delay = 0;
14973
14974   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14975     return FALSE;
14976
14977   player->is_snapping = TRUE;
14978   player->is_active = TRUE;
14979
14980   if (player->MovPos == 0)
14981   {
14982     player->is_moving = FALSE;
14983     player->is_digging = FALSE;
14984     player->is_collecting = FALSE;
14985   }
14986
14987   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14988     TEST_DrawLevelField(player->last_jx, player->last_jy);
14989
14990   TEST_DrawLevelField(x, y);
14991
14992   return TRUE;
14993 }
14994
14995 static boolean DropElement(struct PlayerInfo *player)
14996 {
14997   int old_element, new_element;
14998   int dropx = player->jx, dropy = player->jy;
14999   int drop_direction = player->MovDir;
15000   int drop_side = drop_direction;
15001   int drop_element = get_next_dropped_element(player);
15002
15003   /* do not drop an element on top of another element; when holding drop key
15004      pressed without moving, dropped element must move away before the next
15005      element can be dropped (this is especially important if the next element
15006      is dynamite, which can be placed on background for historical reasons) */
15007   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15008     return MP_ACTION;
15009
15010   if (IS_THROWABLE(drop_element))
15011   {
15012     dropx += GET_DX_FROM_DIR(drop_direction);
15013     dropy += GET_DY_FROM_DIR(drop_direction);
15014
15015     if (!IN_LEV_FIELD(dropx, dropy))
15016       return FALSE;
15017   }
15018
15019   old_element = Tile[dropx][dropy];     // old element at dropping position
15020   new_element = drop_element;           // default: no change when dropping
15021
15022   // check if player is active, not moving and ready to drop
15023   if (!player->active || player->MovPos || player->drop_delay > 0)
15024     return FALSE;
15025
15026   // check if player has anything that can be dropped
15027   if (new_element == EL_UNDEFINED)
15028     return FALSE;
15029
15030   // only set if player has anything that can be dropped
15031   player->is_dropping_pressed = TRUE;
15032
15033   // check if drop key was pressed long enough for EM style dynamite
15034   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15035     return FALSE;
15036
15037   // check if anything can be dropped at the current position
15038   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15039     return FALSE;
15040
15041   // collected custom elements can only be dropped on empty fields
15042   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15043     return FALSE;
15044
15045   if (old_element != EL_EMPTY)
15046     Back[dropx][dropy] = old_element;   // store old element on this field
15047
15048   ResetGfxAnimation(dropx, dropy);
15049   ResetRandomAnimationValue(dropx, dropy);
15050
15051   if (player->inventory_size > 0 ||
15052       player->inventory_infinite_element != EL_UNDEFINED)
15053   {
15054     if (player->inventory_size > 0)
15055     {
15056       player->inventory_size--;
15057
15058       DrawGameDoorValues();
15059
15060       if (new_element == EL_DYNAMITE)
15061         new_element = EL_DYNAMITE_ACTIVE;
15062       else if (new_element == EL_EM_DYNAMITE)
15063         new_element = EL_EM_DYNAMITE_ACTIVE;
15064       else if (new_element == EL_SP_DISK_RED)
15065         new_element = EL_SP_DISK_RED_ACTIVE;
15066     }
15067
15068     Tile[dropx][dropy] = new_element;
15069
15070     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15071       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15072                           el2img(Tile[dropx][dropy]), 0);
15073
15074     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15075
15076     // needed if previous element just changed to "empty" in the last frame
15077     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15078
15079     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15080                                player->index_bit, drop_side);
15081     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15082                                         CE_PLAYER_DROPS_X,
15083                                         player->index_bit, drop_side);
15084
15085     TestIfElementTouchesCustomElement(dropx, dropy);
15086   }
15087   else          // player is dropping a dyna bomb
15088   {
15089     player->dynabombs_left--;
15090
15091     Tile[dropx][dropy] = new_element;
15092
15093     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15094       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15095                           el2img(Tile[dropx][dropy]), 0);
15096
15097     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15098   }
15099
15100   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15101     InitField_WithBug1(dropx, dropy, FALSE);
15102
15103   new_element = Tile[dropx][dropy];     // element might have changed
15104
15105   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15106       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15107   {
15108     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15109       MovDir[dropx][dropy] = drop_direction;
15110
15111     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15112
15113     // do not cause impact style collision by dropping elements that can fall
15114     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15115   }
15116
15117   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15118   player->is_dropping = TRUE;
15119
15120   player->drop_pressed_delay = 0;
15121   player->is_dropping_pressed = FALSE;
15122
15123   player->drop_x = dropx;
15124   player->drop_y = dropy;
15125
15126   return TRUE;
15127 }
15128
15129 // ----------------------------------------------------------------------------
15130 // game sound playing functions
15131 // ----------------------------------------------------------------------------
15132
15133 static int *loop_sound_frame = NULL;
15134 static int *loop_sound_volume = NULL;
15135
15136 void InitPlayLevelSound(void)
15137 {
15138   int num_sounds = getSoundListSize();
15139
15140   checked_free(loop_sound_frame);
15141   checked_free(loop_sound_volume);
15142
15143   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15144   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15145 }
15146
15147 static void PlayLevelSound(int x, int y, int nr)
15148 {
15149   int sx = SCREENX(x), sy = SCREENY(y);
15150   int volume, stereo_position;
15151   int max_distance = 8;
15152   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15153
15154   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15155       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15156     return;
15157
15158   if (!IN_LEV_FIELD(x, y) ||
15159       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15160       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15161     return;
15162
15163   volume = SOUND_MAX_VOLUME;
15164
15165   if (!IN_SCR_FIELD(sx, sy))
15166   {
15167     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15168     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15169
15170     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15171   }
15172
15173   stereo_position = (SOUND_MAX_LEFT +
15174                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15175                      (SCR_FIELDX + 2 * max_distance));
15176
15177   if (IS_LOOP_SOUND(nr))
15178   {
15179     /* This assures that quieter loop sounds do not overwrite louder ones,
15180        while restarting sound volume comparison with each new game frame. */
15181
15182     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15183       return;
15184
15185     loop_sound_volume[nr] = volume;
15186     loop_sound_frame[nr] = FrameCounter;
15187   }
15188
15189   PlaySoundExt(nr, volume, stereo_position, type);
15190 }
15191
15192 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15193 {
15194   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15195                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15196                  y < LEVELY(BY1) ? LEVELY(BY1) :
15197                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15198                  sound_action);
15199 }
15200
15201 static void PlayLevelSoundAction(int x, int y, int action)
15202 {
15203   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15204 }
15205
15206 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15207 {
15208   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15209
15210   if (sound_effect != SND_UNDEFINED)
15211     PlayLevelSound(x, y, sound_effect);
15212 }
15213
15214 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15215                                               int action)
15216 {
15217   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15218
15219   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15220     PlayLevelSound(x, y, sound_effect);
15221 }
15222
15223 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15224 {
15225   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15226
15227   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15228     PlayLevelSound(x, y, sound_effect);
15229 }
15230
15231 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15232 {
15233   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15234
15235   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15236     StopSound(sound_effect);
15237 }
15238
15239 static int getLevelMusicNr(void)
15240 {
15241   if (levelset.music[level_nr] != MUS_UNDEFINED)
15242     return levelset.music[level_nr];            // from config file
15243   else
15244     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15245 }
15246
15247 static void FadeLevelSounds(void)
15248 {
15249   FadeSounds();
15250 }
15251
15252 static void FadeLevelMusic(void)
15253 {
15254   int music_nr = getLevelMusicNr();
15255   char *curr_music = getCurrentlyPlayingMusicFilename();
15256   char *next_music = getMusicInfoEntryFilename(music_nr);
15257
15258   if (!strEqual(curr_music, next_music))
15259     FadeMusic();
15260 }
15261
15262 void FadeLevelSoundsAndMusic(void)
15263 {
15264   FadeLevelSounds();
15265   FadeLevelMusic();
15266 }
15267
15268 static void PlayLevelMusic(void)
15269 {
15270   int music_nr = getLevelMusicNr();
15271   char *curr_music = getCurrentlyPlayingMusicFilename();
15272   char *next_music = getMusicInfoEntryFilename(music_nr);
15273
15274   if (!strEqual(curr_music, next_music))
15275     PlayMusicLoop(music_nr);
15276 }
15277
15278 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15279 {
15280   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15281   int offset = 0;
15282   int x = xx - offset;
15283   int y = yy - offset;
15284
15285   switch (sample)
15286   {
15287     case SOUND_blank:
15288       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15289       break;
15290
15291     case SOUND_roll:
15292       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15293       break;
15294
15295     case SOUND_stone:
15296       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15297       break;
15298
15299     case SOUND_nut:
15300       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15301       break;
15302
15303     case SOUND_crack:
15304       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15305       break;
15306
15307     case SOUND_bug:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15309       break;
15310
15311     case SOUND_tank:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15313       break;
15314
15315     case SOUND_android_clone:
15316       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15317       break;
15318
15319     case SOUND_android_move:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15321       break;
15322
15323     case SOUND_spring:
15324       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15325       break;
15326
15327     case SOUND_slurp:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15329       break;
15330
15331     case SOUND_eater:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15333       break;
15334
15335     case SOUND_eater_eat:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15337       break;
15338
15339     case SOUND_alien:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15341       break;
15342
15343     case SOUND_collect:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15345       break;
15346
15347     case SOUND_diamond:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15349       break;
15350
15351     case SOUND_squash:
15352       // !!! CHECK THIS !!!
15353 #if 1
15354       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15355 #else
15356       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15357 #endif
15358       break;
15359
15360     case SOUND_wonderfall:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15362       break;
15363
15364     case SOUND_drip:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15366       break;
15367
15368     case SOUND_push:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15370       break;
15371
15372     case SOUND_dirt:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15374       break;
15375
15376     case SOUND_acid:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15378       break;
15379
15380     case SOUND_ball:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15382       break;
15383
15384     case SOUND_slide:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15386       break;
15387
15388     case SOUND_wonder:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15390       break;
15391
15392     case SOUND_door:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15394       break;
15395
15396     case SOUND_exit_open:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15398       break;
15399
15400     case SOUND_exit_leave:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15402       break;
15403
15404     case SOUND_dynamite:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15406       break;
15407
15408     case SOUND_tick:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15410       break;
15411
15412     case SOUND_press:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15414       break;
15415
15416     case SOUND_wheel:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15418       break;
15419
15420     case SOUND_boom:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15422       break;
15423
15424     case SOUND_die:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15426       break;
15427
15428     case SOUND_time:
15429       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15430       break;
15431
15432     default:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15434       break;
15435   }
15436 }
15437
15438 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15439 {
15440   int element = map_element_SP_to_RND(element_sp);
15441   int action = map_action_SP_to_RND(action_sp);
15442   int offset = (setup.sp_show_border_elements ? 0 : 1);
15443   int x = xx - offset;
15444   int y = yy - offset;
15445
15446   PlayLevelSoundElementAction(x, y, element, action);
15447 }
15448
15449 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15450 {
15451   int element = map_element_MM_to_RND(element_mm);
15452   int action = map_action_MM_to_RND(action_mm);
15453   int offset = 0;
15454   int x = xx - offset;
15455   int y = yy - offset;
15456
15457   if (!IS_MM_ELEMENT(element))
15458     element = EL_MM_DEFAULT;
15459
15460   PlayLevelSoundElementAction(x, y, element, action);
15461 }
15462
15463 void PlaySound_MM(int sound_mm)
15464 {
15465   int sound = map_sound_MM_to_RND(sound_mm);
15466
15467   if (sound == SND_UNDEFINED)
15468     return;
15469
15470   PlaySound(sound);
15471 }
15472
15473 void PlaySoundLoop_MM(int sound_mm)
15474 {
15475   int sound = map_sound_MM_to_RND(sound_mm);
15476
15477   if (sound == SND_UNDEFINED)
15478     return;
15479
15480   PlaySoundLoop(sound);
15481 }
15482
15483 void StopSound_MM(int sound_mm)
15484 {
15485   int sound = map_sound_MM_to_RND(sound_mm);
15486
15487   if (sound == SND_UNDEFINED)
15488     return;
15489
15490   StopSound(sound);
15491 }
15492
15493 void RaiseScore(int value)
15494 {
15495   game.score += value;
15496
15497   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15498
15499   DisplayGameControlValues();
15500 }
15501
15502 void RaiseScoreElement(int element)
15503 {
15504   switch (element)
15505   {
15506     case EL_EMERALD:
15507     case EL_BD_DIAMOND:
15508     case EL_EMERALD_YELLOW:
15509     case EL_EMERALD_RED:
15510     case EL_EMERALD_PURPLE:
15511     case EL_SP_INFOTRON:
15512       RaiseScore(level.score[SC_EMERALD]);
15513       break;
15514     case EL_DIAMOND:
15515       RaiseScore(level.score[SC_DIAMOND]);
15516       break;
15517     case EL_CRYSTAL:
15518       RaiseScore(level.score[SC_CRYSTAL]);
15519       break;
15520     case EL_PEARL:
15521       RaiseScore(level.score[SC_PEARL]);
15522       break;
15523     case EL_BUG:
15524     case EL_BD_BUTTERFLY:
15525     case EL_SP_ELECTRON:
15526       RaiseScore(level.score[SC_BUG]);
15527       break;
15528     case EL_SPACESHIP:
15529     case EL_BD_FIREFLY:
15530     case EL_SP_SNIKSNAK:
15531       RaiseScore(level.score[SC_SPACESHIP]);
15532       break;
15533     case EL_YAMYAM:
15534     case EL_DARK_YAMYAM:
15535       RaiseScore(level.score[SC_YAMYAM]);
15536       break;
15537     case EL_ROBOT:
15538       RaiseScore(level.score[SC_ROBOT]);
15539       break;
15540     case EL_PACMAN:
15541       RaiseScore(level.score[SC_PACMAN]);
15542       break;
15543     case EL_NUT:
15544       RaiseScore(level.score[SC_NUT]);
15545       break;
15546     case EL_DYNAMITE:
15547     case EL_EM_DYNAMITE:
15548     case EL_SP_DISK_RED:
15549     case EL_DYNABOMB_INCREASE_NUMBER:
15550     case EL_DYNABOMB_INCREASE_SIZE:
15551     case EL_DYNABOMB_INCREASE_POWER:
15552       RaiseScore(level.score[SC_DYNAMITE]);
15553       break;
15554     case EL_SHIELD_NORMAL:
15555     case EL_SHIELD_DEADLY:
15556       RaiseScore(level.score[SC_SHIELD]);
15557       break;
15558     case EL_EXTRA_TIME:
15559       RaiseScore(level.extra_time_score);
15560       break;
15561     case EL_KEY_1:
15562     case EL_KEY_2:
15563     case EL_KEY_3:
15564     case EL_KEY_4:
15565     case EL_EM_KEY_1:
15566     case EL_EM_KEY_2:
15567     case EL_EM_KEY_3:
15568     case EL_EM_KEY_4:
15569     case EL_EMC_KEY_5:
15570     case EL_EMC_KEY_6:
15571     case EL_EMC_KEY_7:
15572     case EL_EMC_KEY_8:
15573     case EL_DC_KEY_WHITE:
15574       RaiseScore(level.score[SC_KEY]);
15575       break;
15576     default:
15577       RaiseScore(element_info[element].collect_score);
15578       break;
15579   }
15580 }
15581
15582 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15583 {
15584   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15585   {
15586     if (!quick_quit)
15587     {
15588       // prevent short reactivation of overlay buttons while closing door
15589       SetOverlayActive(FALSE);
15590       UnmapGameButtons();
15591
15592       // door may still be open due to skipped or envelope style request
15593       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15594     }
15595
15596     if (network.enabled)
15597       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15598     else
15599     {
15600       if (quick_quit)
15601         FadeSkipNextFadeIn();
15602
15603       SetGameStatus(GAME_MODE_MAIN);
15604
15605       DrawMainMenu();
15606     }
15607   }
15608   else          // continue playing the game
15609   {
15610     if (tape.playing && tape.deactivate_display)
15611       TapeDeactivateDisplayOff(TRUE);
15612
15613     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15614
15615     if (tape.playing && tape.deactivate_display)
15616       TapeDeactivateDisplayOn();
15617   }
15618 }
15619
15620 void RequestQuitGame(boolean escape_key_pressed)
15621 {
15622   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15623   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15624                         level_editor_test_game);
15625   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15626                           quick_quit || score_info_tape_play);
15627
15628   RequestQuitGameExt(skip_request, quick_quit,
15629                      "Do you really want to quit the game?");
15630 }
15631
15632 static char *getRestartGameMessage(void)
15633 {
15634   boolean play_again = hasStartedNetworkGame();
15635   static char message[MAX_OUTPUT_LINESIZE];
15636   char *game_over_text = "Game over!";
15637   char *play_again_text = " Play it again?";
15638
15639   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15640       game_mm.game_over_message != NULL)
15641     game_over_text = game_mm.game_over_message;
15642
15643   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15644            (play_again ? play_again_text : ""));
15645
15646   return message;
15647 }
15648
15649 static void RequestRestartGame(void)
15650 {
15651   char *message = getRestartGameMessage();
15652   boolean has_started_game = hasStartedNetworkGame();
15653   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15654   int door_state = DOOR_CLOSE_1;
15655
15656   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15657   {
15658     CloseDoor(door_state);
15659
15660     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15661   }
15662   else
15663   {
15664     // if game was invoked from level editor, also close tape recorder door
15665     if (level_editor_test_game)
15666       door_state = DOOR_CLOSE_ALL;
15667
15668     CloseDoor(door_state);
15669
15670     SetGameStatus(GAME_MODE_MAIN);
15671
15672     DrawMainMenu();
15673   }
15674 }
15675
15676 boolean CheckRestartGame(void)
15677 {
15678   static int game_over_delay = 0;
15679   int game_over_delay_value = 50;
15680   boolean game_over = checkGameFailed();
15681
15682   if (!game_over)
15683   {
15684     game_over_delay = game_over_delay_value;
15685
15686     return FALSE;
15687   }
15688
15689   if (game_over_delay > 0)
15690   {
15691     if (game_over_delay == game_over_delay_value / 2)
15692       PlaySound(SND_GAME_LOSING);
15693
15694     game_over_delay--;
15695
15696     return FALSE;
15697   }
15698
15699   // do not handle game over if request dialog is already active
15700   if (game.request_active)
15701     return FALSE;
15702
15703   // do not ask to play again if game was never actually played
15704   if (!game.GamePlayed)
15705     return FALSE;
15706
15707   // do not ask to play again if this was disabled in setup menu
15708   if (!setup.ask_on_game_over)
15709     return FALSE;
15710
15711   RequestRestartGame();
15712
15713   return TRUE;
15714 }
15715
15716 boolean checkGameSolved(void)
15717 {
15718   // set for all game engines if level was solved
15719   return game.LevelSolved_GameEnd;
15720 }
15721
15722 boolean checkGameFailed(void)
15723 {
15724   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15725     return (game_em.game_over && !game_em.level_solved);
15726   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15727     return (game_sp.game_over && !game_sp.level_solved);
15728   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15729     return (game_mm.game_over && !game_mm.level_solved);
15730   else                          // GAME_ENGINE_TYPE_RND
15731     return (game.GameOver && !game.LevelSolved);
15732 }
15733
15734 boolean checkGameEnded(void)
15735 {
15736   return (checkGameSolved() || checkGameFailed());
15737 }
15738
15739
15740 // ----------------------------------------------------------------------------
15741 // random generator functions
15742 // ----------------------------------------------------------------------------
15743
15744 unsigned int InitEngineRandom_RND(int seed)
15745 {
15746   game.num_random_calls = 0;
15747
15748   return InitEngineRandom(seed);
15749 }
15750
15751 unsigned int RND(int max)
15752 {
15753   if (max > 0)
15754   {
15755     game.num_random_calls++;
15756
15757     return GetEngineRandom(max);
15758   }
15759
15760   return 0;
15761 }
15762
15763
15764 // ----------------------------------------------------------------------------
15765 // game engine snapshot handling functions
15766 // ----------------------------------------------------------------------------
15767
15768 struct EngineSnapshotInfo
15769 {
15770   // runtime values for custom element collect score
15771   int collect_score[NUM_CUSTOM_ELEMENTS];
15772
15773   // runtime values for group element choice position
15774   int choice_pos[NUM_GROUP_ELEMENTS];
15775
15776   // runtime values for belt position animations
15777   int belt_graphic[4][NUM_BELT_PARTS];
15778   int belt_anim_mode[4][NUM_BELT_PARTS];
15779 };
15780
15781 static struct EngineSnapshotInfo engine_snapshot_rnd;
15782 static char *snapshot_level_identifier = NULL;
15783 static int snapshot_level_nr = -1;
15784
15785 static void SaveEngineSnapshotValues_RND(void)
15786 {
15787   static int belt_base_active_element[4] =
15788   {
15789     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15790     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15791     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15792     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15793   };
15794   int i, j;
15795
15796   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15797   {
15798     int element = EL_CUSTOM_START + i;
15799
15800     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15801   }
15802
15803   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15804   {
15805     int element = EL_GROUP_START + i;
15806
15807     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15808   }
15809
15810   for (i = 0; i < 4; i++)
15811   {
15812     for (j = 0; j < NUM_BELT_PARTS; j++)
15813     {
15814       int element = belt_base_active_element[i] + j;
15815       int graphic = el2img(element);
15816       int anim_mode = graphic_info[graphic].anim_mode;
15817
15818       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15819       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15820     }
15821   }
15822 }
15823
15824 static void LoadEngineSnapshotValues_RND(void)
15825 {
15826   unsigned int num_random_calls = game.num_random_calls;
15827   int i, j;
15828
15829   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15830   {
15831     int element = EL_CUSTOM_START + i;
15832
15833     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15834   }
15835
15836   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15837   {
15838     int element = EL_GROUP_START + i;
15839
15840     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15841   }
15842
15843   for (i = 0; i < 4; i++)
15844   {
15845     for (j = 0; j < NUM_BELT_PARTS; j++)
15846     {
15847       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15848       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15849
15850       graphic_info[graphic].anim_mode = anim_mode;
15851     }
15852   }
15853
15854   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15855   {
15856     InitRND(tape.random_seed);
15857     for (i = 0; i < num_random_calls; i++)
15858       RND(1);
15859   }
15860
15861   if (game.num_random_calls != num_random_calls)
15862   {
15863     Error("number of random calls out of sync");
15864     Error("number of random calls should be %d", num_random_calls);
15865     Error("number of random calls is %d", game.num_random_calls);
15866
15867     Fail("this should not happen -- please debug");
15868   }
15869 }
15870
15871 void FreeEngineSnapshotSingle(void)
15872 {
15873   FreeSnapshotSingle();
15874
15875   setString(&snapshot_level_identifier, NULL);
15876   snapshot_level_nr = -1;
15877 }
15878
15879 void FreeEngineSnapshotList(void)
15880 {
15881   FreeSnapshotList();
15882 }
15883
15884 static ListNode *SaveEngineSnapshotBuffers(void)
15885 {
15886   ListNode *buffers = NULL;
15887
15888   // copy some special values to a structure better suited for the snapshot
15889
15890   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15891     SaveEngineSnapshotValues_RND();
15892   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15893     SaveEngineSnapshotValues_EM();
15894   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15895     SaveEngineSnapshotValues_SP(&buffers);
15896   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15897     SaveEngineSnapshotValues_MM();
15898
15899   // save values stored in special snapshot structure
15900
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15902     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15904     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15906     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15908     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15909
15910   // save further RND engine values
15911
15912   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15914   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15915
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15917   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15918   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15921
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15925
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15927
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15930
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15949
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15952
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15959
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15969
15970 #if 0
15971   ListNode *node = engine_snapshot_list_rnd;
15972   int num_bytes = 0;
15973
15974   while (node != NULL)
15975   {
15976     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15977
15978     node = node->next;
15979   }
15980
15981   Debug("game:playing:SaveEngineSnapshotBuffers",
15982         "size of engine snapshot: %d bytes", num_bytes);
15983 #endif
15984
15985   return buffers;
15986 }
15987
15988 void SaveEngineSnapshotSingle(void)
15989 {
15990   ListNode *buffers = SaveEngineSnapshotBuffers();
15991
15992   // finally save all snapshot buffers to single snapshot
15993   SaveSnapshotSingle(buffers);
15994
15995   // save level identification information
15996   setString(&snapshot_level_identifier, leveldir_current->identifier);
15997   snapshot_level_nr = level_nr;
15998 }
15999
16000 boolean CheckSaveEngineSnapshotToList(void)
16001 {
16002   boolean save_snapshot =
16003     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16004      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16005       game.snapshot.changed_action) ||
16006      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16007       game.snapshot.collected_item));
16008
16009   game.snapshot.changed_action = FALSE;
16010   game.snapshot.collected_item = FALSE;
16011   game.snapshot.save_snapshot = save_snapshot;
16012
16013   return save_snapshot;
16014 }
16015
16016 void SaveEngineSnapshotToList(void)
16017 {
16018   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16019       tape.quick_resume)
16020     return;
16021
16022   ListNode *buffers = SaveEngineSnapshotBuffers();
16023
16024   // finally save all snapshot buffers to snapshot list
16025   SaveSnapshotToList(buffers);
16026 }
16027
16028 void SaveEngineSnapshotToListInitial(void)
16029 {
16030   FreeEngineSnapshotList();
16031
16032   SaveEngineSnapshotToList();
16033 }
16034
16035 static void LoadEngineSnapshotValues(void)
16036 {
16037   // restore special values from snapshot structure
16038
16039   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16040     LoadEngineSnapshotValues_RND();
16041   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16042     LoadEngineSnapshotValues_EM();
16043   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16044     LoadEngineSnapshotValues_SP();
16045   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16046     LoadEngineSnapshotValues_MM();
16047 }
16048
16049 void LoadEngineSnapshotSingle(void)
16050 {
16051   LoadSnapshotSingle();
16052
16053   LoadEngineSnapshotValues();
16054 }
16055
16056 static void LoadEngineSnapshot_Undo(int steps)
16057 {
16058   LoadSnapshotFromList_Older(steps);
16059
16060   LoadEngineSnapshotValues();
16061 }
16062
16063 static void LoadEngineSnapshot_Redo(int steps)
16064 {
16065   LoadSnapshotFromList_Newer(steps);
16066
16067   LoadEngineSnapshotValues();
16068 }
16069
16070 boolean CheckEngineSnapshotSingle(void)
16071 {
16072   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16073           snapshot_level_nr == level_nr);
16074 }
16075
16076 boolean CheckEngineSnapshotList(void)
16077 {
16078   return CheckSnapshotList();
16079 }
16080
16081
16082 // ---------- new game button stuff -------------------------------------------
16083
16084 static struct
16085 {
16086   int graphic;
16087   struct XY *pos;
16088   int gadget_id;
16089   boolean *setup_value;
16090   boolean allowed_on_tape;
16091   boolean is_touch_button;
16092   char *infotext;
16093 } gamebutton_info[NUM_GAME_BUTTONS] =
16094 {
16095   {
16096     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16097     GAME_CTRL_ID_STOP,                          NULL,
16098     TRUE, FALSE,                                "stop game"
16099   },
16100   {
16101     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16102     GAME_CTRL_ID_PAUSE,                         NULL,
16103     TRUE, FALSE,                                "pause game"
16104   },
16105   {
16106     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16107     GAME_CTRL_ID_PLAY,                          NULL,
16108     TRUE, FALSE,                                "play game"
16109   },
16110   {
16111     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16112     GAME_CTRL_ID_UNDO,                          NULL,
16113     TRUE, FALSE,                                "undo step"
16114   },
16115   {
16116     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16117     GAME_CTRL_ID_REDO,                          NULL,
16118     TRUE, FALSE,                                "redo step"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16122     GAME_CTRL_ID_SAVE,                          NULL,
16123     TRUE, FALSE,                                "save game"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16127     GAME_CTRL_ID_PAUSE2,                        NULL,
16128     TRUE, FALSE,                                "pause game"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16132     GAME_CTRL_ID_LOAD,                          NULL,
16133     TRUE, FALSE,                                "load game"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16137     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16138     FALSE, FALSE,                               "stop game"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16142     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16143     FALSE, FALSE,                               "pause game"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16147     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16148     FALSE, FALSE,                               "play game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16152     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16153     FALSE, TRUE,                                "stop game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16157     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16158     FALSE, TRUE,                                "pause game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16162     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16163     TRUE, FALSE,                                "background music on/off"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16167     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16168     TRUE, FALSE,                                "sound loops on/off"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16172     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16173     TRUE, FALSE,                                "normal sounds on/off"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16177     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16178     FALSE, FALSE,                               "background music on/off"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16182     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16183     FALSE, FALSE,                               "sound loops on/off"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16187     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16188     FALSE, FALSE,                               "normal sounds on/off"
16189   }
16190 };
16191
16192 void CreateGameButtons(void)
16193 {
16194   int i;
16195
16196   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16197   {
16198     int graphic = gamebutton_info[i].graphic;
16199     struct GraphicInfo *gfx = &graphic_info[graphic];
16200     struct XY *pos = gamebutton_info[i].pos;
16201     struct GadgetInfo *gi;
16202     int button_type;
16203     boolean checked;
16204     unsigned int event_mask;
16205     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16206     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16207     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16208     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16209     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16210     int gd_x   = gfx->src_x;
16211     int gd_y   = gfx->src_y;
16212     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16213     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16214     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16215     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16216     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16217     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16218     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16219     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16220     int id = i;
16221
16222     // do not use touch buttons if overlay touch buttons are disabled
16223     if (is_touch_button && !setup.touch.overlay_buttons)
16224       continue;
16225
16226     if (gfx->bitmap == NULL)
16227     {
16228       game_gadget[id] = NULL;
16229
16230       continue;
16231     }
16232
16233     if (id == GAME_CTRL_ID_STOP ||
16234         id == GAME_CTRL_ID_PANEL_STOP ||
16235         id == GAME_CTRL_ID_TOUCH_STOP ||
16236         id == GAME_CTRL_ID_PLAY ||
16237         id == GAME_CTRL_ID_PANEL_PLAY ||
16238         id == GAME_CTRL_ID_SAVE ||
16239         id == GAME_CTRL_ID_LOAD)
16240     {
16241       button_type = GD_TYPE_NORMAL_BUTTON;
16242       checked = FALSE;
16243       event_mask = GD_EVENT_RELEASED;
16244     }
16245     else if (id == GAME_CTRL_ID_UNDO ||
16246              id == GAME_CTRL_ID_REDO)
16247     {
16248       button_type = GD_TYPE_NORMAL_BUTTON;
16249       checked = FALSE;
16250       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16251     }
16252     else
16253     {
16254       button_type = GD_TYPE_CHECK_BUTTON;
16255       checked = (gamebutton_info[i].setup_value != NULL ?
16256                  *gamebutton_info[i].setup_value : FALSE);
16257       event_mask = GD_EVENT_PRESSED;
16258     }
16259
16260     gi = CreateGadget(GDI_CUSTOM_ID, id,
16261                       GDI_IMAGE_ID, graphic,
16262                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16263                       GDI_X, base_x + x,
16264                       GDI_Y, base_y + y,
16265                       GDI_WIDTH, gfx->width,
16266                       GDI_HEIGHT, gfx->height,
16267                       GDI_TYPE, button_type,
16268                       GDI_STATE, GD_BUTTON_UNPRESSED,
16269                       GDI_CHECKED, checked,
16270                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16271                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16272                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16273                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16274                       GDI_DIRECT_DRAW, FALSE,
16275                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16276                       GDI_EVENT_MASK, event_mask,
16277                       GDI_CALLBACK_ACTION, HandleGameButtons,
16278                       GDI_END);
16279
16280     if (gi == NULL)
16281       Fail("cannot create gadget");
16282
16283     game_gadget[id] = gi;
16284   }
16285 }
16286
16287 void FreeGameButtons(void)
16288 {
16289   int i;
16290
16291   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16292     FreeGadget(game_gadget[i]);
16293 }
16294
16295 static void UnmapGameButtonsAtSamePosition(int id)
16296 {
16297   int i;
16298
16299   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16300     if (i != id &&
16301         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16302         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16303       UnmapGadget(game_gadget[i]);
16304 }
16305
16306 static void UnmapGameButtonsAtSamePosition_All(void)
16307 {
16308   if (setup.show_load_save_buttons)
16309   {
16310     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16311     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16312     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16313   }
16314   else if (setup.show_undo_redo_buttons)
16315   {
16316     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16317     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16319   }
16320   else
16321   {
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16325
16326     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16329   }
16330 }
16331
16332 void MapLoadSaveButtons(void)
16333 {
16334   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16335   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16336
16337   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16338   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16339 }
16340
16341 void MapUndoRedoButtons(void)
16342 {
16343   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16344   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16345
16346   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16347   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16348 }
16349
16350 void ModifyPauseButtons(void)
16351 {
16352   static int ids[] =
16353   {
16354     GAME_CTRL_ID_PAUSE,
16355     GAME_CTRL_ID_PAUSE2,
16356     GAME_CTRL_ID_PANEL_PAUSE,
16357     GAME_CTRL_ID_TOUCH_PAUSE,
16358     -1
16359   };
16360   int i;
16361
16362   for (i = 0; ids[i] > -1; i++)
16363     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16364 }
16365
16366 static void MapGameButtonsExt(boolean on_tape)
16367 {
16368   int i;
16369
16370   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16371   {
16372     if ((i == GAME_CTRL_ID_UNDO ||
16373          i == GAME_CTRL_ID_REDO) &&
16374         game_status != GAME_MODE_PLAYING)
16375       continue;
16376
16377     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16378       MapGadget(game_gadget[i]);
16379   }
16380
16381   UnmapGameButtonsAtSamePosition_All();
16382
16383   RedrawGameButtons();
16384 }
16385
16386 static void UnmapGameButtonsExt(boolean on_tape)
16387 {
16388   int i;
16389
16390   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16391     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16392       UnmapGadget(game_gadget[i]);
16393 }
16394
16395 static void RedrawGameButtonsExt(boolean on_tape)
16396 {
16397   int i;
16398
16399   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16400     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16401       RedrawGadget(game_gadget[i]);
16402 }
16403
16404 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16405 {
16406   if (gi == NULL)
16407     return;
16408
16409   gi->checked = state;
16410 }
16411
16412 static void RedrawSoundButtonGadget(int id)
16413 {
16414   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16415              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16416              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16417              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16418              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16419              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16420              id);
16421
16422   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16423   RedrawGadget(game_gadget[id2]);
16424 }
16425
16426 void MapGameButtons(void)
16427 {
16428   MapGameButtonsExt(FALSE);
16429 }
16430
16431 void UnmapGameButtons(void)
16432 {
16433   UnmapGameButtonsExt(FALSE);
16434 }
16435
16436 void RedrawGameButtons(void)
16437 {
16438   RedrawGameButtonsExt(FALSE);
16439 }
16440
16441 void MapGameButtonsOnTape(void)
16442 {
16443   MapGameButtonsExt(TRUE);
16444 }
16445
16446 void UnmapGameButtonsOnTape(void)
16447 {
16448   UnmapGameButtonsExt(TRUE);
16449 }
16450
16451 void RedrawGameButtonsOnTape(void)
16452 {
16453   RedrawGameButtonsExt(TRUE);
16454 }
16455
16456 static void GameUndoRedoExt(void)
16457 {
16458   ClearPlayerAction();
16459
16460   tape.pausing = TRUE;
16461
16462   RedrawPlayfield();
16463   UpdateAndDisplayGameControlValues();
16464
16465   DrawCompleteVideoDisplay();
16466   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16467   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16468   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16469
16470   ModifyPauseButtons();
16471
16472   BackToFront();
16473 }
16474
16475 static void GameUndo(int steps)
16476 {
16477   if (!CheckEngineSnapshotList())
16478     return;
16479
16480   int tape_property_bits = tape.property_bits;
16481
16482   LoadEngineSnapshot_Undo(steps);
16483
16484   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16485
16486   GameUndoRedoExt();
16487 }
16488
16489 static void GameRedo(int steps)
16490 {
16491   if (!CheckEngineSnapshotList())
16492     return;
16493
16494   int tape_property_bits = tape.property_bits;
16495
16496   LoadEngineSnapshot_Redo(steps);
16497
16498   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16499
16500   GameUndoRedoExt();
16501 }
16502
16503 static void HandleGameButtonsExt(int id, int button)
16504 {
16505   static boolean game_undo_executed = FALSE;
16506   int steps = BUTTON_STEPSIZE(button);
16507   boolean handle_game_buttons =
16508     (game_status == GAME_MODE_PLAYING ||
16509      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16510
16511   if (!handle_game_buttons)
16512     return;
16513
16514   switch (id)
16515   {
16516     case GAME_CTRL_ID_STOP:
16517     case GAME_CTRL_ID_PANEL_STOP:
16518     case GAME_CTRL_ID_TOUCH_STOP:
16519       TapeStopGame();
16520
16521       break;
16522
16523     case GAME_CTRL_ID_PAUSE:
16524     case GAME_CTRL_ID_PAUSE2:
16525     case GAME_CTRL_ID_PANEL_PAUSE:
16526     case GAME_CTRL_ID_TOUCH_PAUSE:
16527       if (network.enabled && game_status == GAME_MODE_PLAYING)
16528       {
16529         if (tape.pausing)
16530           SendToServer_ContinuePlaying();
16531         else
16532           SendToServer_PausePlaying();
16533       }
16534       else
16535         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16536
16537       game_undo_executed = FALSE;
16538
16539       break;
16540
16541     case GAME_CTRL_ID_PLAY:
16542     case GAME_CTRL_ID_PANEL_PLAY:
16543       if (game_status == GAME_MODE_MAIN)
16544       {
16545         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16546       }
16547       else if (tape.pausing)
16548       {
16549         if (network.enabled)
16550           SendToServer_ContinuePlaying();
16551         else
16552           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16553       }
16554       break;
16555
16556     case GAME_CTRL_ID_UNDO:
16557       // Important: When using "save snapshot when collecting an item" mode,
16558       // load last (current) snapshot for first "undo" after pressing "pause"
16559       // (else the last-but-one snapshot would be loaded, because the snapshot
16560       // pointer already points to the last snapshot when pressing "pause",
16561       // which is fine for "every step/move" mode, but not for "every collect")
16562       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16563           !game_undo_executed)
16564         steps--;
16565
16566       game_undo_executed = TRUE;
16567
16568       GameUndo(steps);
16569       break;
16570
16571     case GAME_CTRL_ID_REDO:
16572       GameRedo(steps);
16573       break;
16574
16575     case GAME_CTRL_ID_SAVE:
16576       TapeQuickSave();
16577       break;
16578
16579     case GAME_CTRL_ID_LOAD:
16580       TapeQuickLoad();
16581       break;
16582
16583     case SOUND_CTRL_ID_MUSIC:
16584     case SOUND_CTRL_ID_PANEL_MUSIC:
16585       if (setup.sound_music)
16586       { 
16587         setup.sound_music = FALSE;
16588
16589         FadeMusic();
16590       }
16591       else if (audio.music_available)
16592       { 
16593         setup.sound = setup.sound_music = TRUE;
16594
16595         SetAudioMode(setup.sound);
16596
16597         if (game_status == GAME_MODE_PLAYING)
16598           PlayLevelMusic();
16599       }
16600
16601       RedrawSoundButtonGadget(id);
16602
16603       break;
16604
16605     case SOUND_CTRL_ID_LOOPS:
16606     case SOUND_CTRL_ID_PANEL_LOOPS:
16607       if (setup.sound_loops)
16608         setup.sound_loops = FALSE;
16609       else if (audio.loops_available)
16610       {
16611         setup.sound = setup.sound_loops = TRUE;
16612
16613         SetAudioMode(setup.sound);
16614       }
16615
16616       RedrawSoundButtonGadget(id);
16617
16618       break;
16619
16620     case SOUND_CTRL_ID_SIMPLE:
16621     case SOUND_CTRL_ID_PANEL_SIMPLE:
16622       if (setup.sound_simple)
16623         setup.sound_simple = FALSE;
16624       else if (audio.sound_available)
16625       {
16626         setup.sound = setup.sound_simple = TRUE;
16627
16628         SetAudioMode(setup.sound);
16629       }
16630
16631       RedrawSoundButtonGadget(id);
16632
16633       break;
16634
16635     default:
16636       break;
16637   }
16638 }
16639
16640 static void HandleGameButtons(struct GadgetInfo *gi)
16641 {
16642   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16643 }
16644
16645 void HandleSoundButtonKeys(Key key)
16646 {
16647   if (key == setup.shortcut.sound_simple)
16648     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16649   else if (key == setup.shortcut.sound_loops)
16650     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16651   else if (key == setup.shortcut.sound_music)
16652     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16653 }