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