white space changes
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576   boolean restarting = (game_status == GAME_MODE_PLAYING);
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   if (restarting)
3589   {
3590     // force fading out global animations displayed during game play
3591     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3592   }
3593   else
3594   {
3595     SetGameStatus(GAME_MODE_PLAYING);
3596   }
3597
3598   if (level_editor_test_game)
3599     FadeSkipNextFadeOut();
3600   else
3601     FadeSetEnterScreen();
3602
3603   if (CheckFadeAll())
3604     fade_mask = REDRAW_ALL;
3605
3606   FadeLevelSoundsAndMusic();
3607
3608   ExpireSoundLoops(TRUE);
3609
3610   FadeOut(fade_mask);
3611
3612   if (restarting)
3613   {
3614     // force restarting global animations displayed during game play
3615     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3616
3617     SetGameStatus(GAME_MODE_PLAYING);
3618   }
3619
3620   if (level_editor_test_game)
3621     FadeSkipNextFadeIn();
3622
3623   // needed if different viewport properties defined for playing
3624   ChangeViewportPropertiesIfNeeded();
3625
3626   ClearField();
3627
3628   DrawCompleteVideoDisplay();
3629
3630   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3631
3632   InitGameEngine();
3633   InitGameControlValues();
3634
3635   if (tape.recording)
3636   {
3637     // initialize tape actions from game when recording tape
3638     tape.use_key_actions   = game.use_key_actions;
3639     tape.use_mouse_actions = game.use_mouse_actions;
3640
3641     // initialize visible playfield size when recording tape (for team mode)
3642     tape.scr_fieldx = SCR_FIELDX;
3643     tape.scr_fieldy = SCR_FIELDY;
3644   }
3645
3646   // don't play tapes over network
3647   network_playing = (network.enabled && !tape.playing);
3648
3649   for (i = 0; i < MAX_PLAYERS; i++)
3650   {
3651     struct PlayerInfo *player = &stored_player[i];
3652
3653     player->index_nr = i;
3654     player->index_bit = (1 << i);
3655     player->element_nr = EL_PLAYER_1 + i;
3656
3657     player->present = FALSE;
3658     player->active = FALSE;
3659     player->mapped = FALSE;
3660
3661     player->killed = FALSE;
3662     player->reanimated = FALSE;
3663     player->buried = FALSE;
3664
3665     player->action = 0;
3666     player->effective_action = 0;
3667     player->programmed_action = 0;
3668     player->snap_action = 0;
3669
3670     player->mouse_action.lx = 0;
3671     player->mouse_action.ly = 0;
3672     player->mouse_action.button = 0;
3673     player->mouse_action.button_hint = 0;
3674
3675     player->effective_mouse_action.lx = 0;
3676     player->effective_mouse_action.ly = 0;
3677     player->effective_mouse_action.button = 0;
3678     player->effective_mouse_action.button_hint = 0;
3679
3680     for (j = 0; j < MAX_NUM_KEYS; j++)
3681       player->key[j] = FALSE;
3682
3683     player->num_white_keys = 0;
3684
3685     player->dynabomb_count = 0;
3686     player->dynabomb_size = 1;
3687     player->dynabombs_left = 0;
3688     player->dynabomb_xl = FALSE;
3689
3690     player->MovDir = initial_move_dir;
3691     player->MovPos = 0;
3692     player->GfxPos = 0;
3693     player->GfxDir = initial_move_dir;
3694     player->GfxAction = ACTION_DEFAULT;
3695     player->Frame = 0;
3696     player->StepFrame = 0;
3697
3698     player->initial_element = player->element_nr;
3699     player->artwork_element =
3700       (level.use_artwork_element[i] ? level.artwork_element[i] :
3701        player->element_nr);
3702     player->use_murphy = FALSE;
3703
3704     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3705     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3706
3707     player->gravity = level.initial_player_gravity[i];
3708
3709     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3710
3711     player->actual_frame_counter.count = 0;
3712     player->actual_frame_counter.value = 1;
3713
3714     player->step_counter = 0;
3715
3716     player->last_move_dir = initial_move_dir;
3717
3718     player->is_active = FALSE;
3719
3720     player->is_waiting = FALSE;
3721     player->is_moving = FALSE;
3722     player->is_auto_moving = FALSE;
3723     player->is_digging = FALSE;
3724     player->is_snapping = FALSE;
3725     player->is_collecting = FALSE;
3726     player->is_pushing = FALSE;
3727     player->is_switching = FALSE;
3728     player->is_dropping = FALSE;
3729     player->is_dropping_pressed = FALSE;
3730
3731     player->is_bored = FALSE;
3732     player->is_sleeping = FALSE;
3733
3734     player->was_waiting = TRUE;
3735     player->was_moving = FALSE;
3736     player->was_snapping = FALSE;
3737     player->was_dropping = FALSE;
3738
3739     player->force_dropping = FALSE;
3740
3741     player->frame_counter_bored = -1;
3742     player->frame_counter_sleeping = -1;
3743
3744     player->anim_delay_counter = 0;
3745     player->post_delay_counter = 0;
3746
3747     player->dir_waiting = initial_move_dir;
3748     player->action_waiting = ACTION_DEFAULT;
3749     player->last_action_waiting = ACTION_DEFAULT;
3750     player->special_action_bored = ACTION_DEFAULT;
3751     player->special_action_sleeping = ACTION_DEFAULT;
3752
3753     player->switch_x = -1;
3754     player->switch_y = -1;
3755
3756     player->drop_x = -1;
3757     player->drop_y = -1;
3758
3759     player->show_envelope = 0;
3760
3761     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3762
3763     player->push_delay       = -1;      // initialized when pushing starts
3764     player->push_delay_value = game.initial_push_delay_value;
3765
3766     player->drop_delay = 0;
3767     player->drop_pressed_delay = 0;
3768
3769     player->last_jx = -1;
3770     player->last_jy = -1;
3771     player->jx = -1;
3772     player->jy = -1;
3773
3774     player->shield_normal_time_left = 0;
3775     player->shield_deadly_time_left = 0;
3776
3777     player->last_removed_element = EL_UNDEFINED;
3778
3779     player->inventory_infinite_element = EL_UNDEFINED;
3780     player->inventory_size = 0;
3781
3782     if (level.use_initial_inventory[i])
3783     {
3784       for (j = 0; j < level.initial_inventory_size[i]; j++)
3785       {
3786         int element = level.initial_inventory_content[i][j];
3787         int collect_count = element_info[element].collect_count_initial;
3788         int k;
3789
3790         if (!IS_CUSTOM_ELEMENT(element))
3791           collect_count = 1;
3792
3793         if (collect_count == 0)
3794           player->inventory_infinite_element = element;
3795         else
3796           for (k = 0; k < collect_count; k++)
3797             if (player->inventory_size < MAX_INVENTORY_SIZE)
3798               player->inventory_element[player->inventory_size++] = element;
3799       }
3800     }
3801
3802     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3803     SnapField(player, 0, 0);
3804
3805     map_player_action[i] = i;
3806   }
3807
3808   network_player_action_received = FALSE;
3809
3810   // initial null action
3811   if (network_playing)
3812     SendToServer_MovePlayer(MV_NONE);
3813
3814   FrameCounter = 0;
3815   TimeFrames = 0;
3816   TimePlayed = 0;
3817   TimeLeft = level.time;
3818   TapeTime = 0;
3819
3820   ScreenMovDir = MV_NONE;
3821   ScreenMovPos = 0;
3822   ScreenGfxPos = 0;
3823
3824   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3825
3826   game.robot_wheel_x = -1;
3827   game.robot_wheel_y = -1;
3828
3829   game.exit_x = -1;
3830   game.exit_y = -1;
3831
3832   game.all_players_gone = FALSE;
3833
3834   game.LevelSolved = FALSE;
3835   game.GameOver = FALSE;
3836
3837   game.GamePlayed = !tape.playing;
3838
3839   game.LevelSolved_GameWon = FALSE;
3840   game.LevelSolved_GameEnd = FALSE;
3841   game.LevelSolved_SaveTape = FALSE;
3842   game.LevelSolved_SaveScore = FALSE;
3843
3844   game.LevelSolved_CountingTime = 0;
3845   game.LevelSolved_CountingScore = 0;
3846   game.LevelSolved_CountingHealth = 0;
3847
3848   game.panel.active = TRUE;
3849
3850   game.no_level_time_limit = (level.time == 0);
3851   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3852
3853   game.yamyam_content_nr = 0;
3854   game.robot_wheel_active = FALSE;
3855   game.magic_wall_active = FALSE;
3856   game.magic_wall_time_left = 0;
3857   game.light_time_left = 0;
3858   game.timegate_time_left = 0;
3859   game.switchgate_pos = 0;
3860   game.wind_direction = level.wind_direction_initial;
3861
3862   game.time_final = 0;
3863   game.score_time_final = 0;
3864
3865   game.score = 0;
3866   game.score_final = 0;
3867
3868   game.health = MAX_HEALTH;
3869   game.health_final = MAX_HEALTH;
3870
3871   game.gems_still_needed = level.gems_needed;
3872   game.sokoban_fields_still_needed = 0;
3873   game.sokoban_objects_still_needed = 0;
3874   game.lights_still_needed = 0;
3875   game.players_still_needed = 0;
3876   game.friends_still_needed = 0;
3877
3878   game.lenses_time_left = 0;
3879   game.magnify_time_left = 0;
3880
3881   game.ball_active = level.ball_active_initial;
3882   game.ball_content_nr = 0;
3883
3884   game.explosions_delayed = TRUE;
3885
3886   game.envelope_active = FALSE;
3887
3888   // special case: set custom artwork setting to initial value
3889   game.use_masked_elements = game.use_masked_elements_initial;
3890
3891   for (i = 0; i < NUM_BELTS; i++)
3892   {
3893     game.belt_dir[i] = MV_NONE;
3894     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3895   }
3896
3897   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3898     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3899
3900 #if DEBUG_INIT_PLAYER
3901   DebugPrintPlayerStatus("Player status at level initialization");
3902 #endif
3903
3904   SCAN_PLAYFIELD(x, y)
3905   {
3906     Tile[x][y] = Last[x][y] = level.field[x][y];
3907     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3908     ChangeDelay[x][y] = 0;
3909     ChangePage[x][y] = -1;
3910     CustomValue[x][y] = 0;              // initialized in InitField()
3911     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3912     AmoebaNr[x][y] = 0;
3913     WasJustMoving[x][y] = 0;
3914     WasJustFalling[x][y] = 0;
3915     CheckCollision[x][y] = 0;
3916     CheckImpact[x][y] = 0;
3917     Stop[x][y] = FALSE;
3918     Pushed[x][y] = FALSE;
3919
3920     ChangeCount[x][y] = 0;
3921     ChangeEvent[x][y] = -1;
3922
3923     ExplodePhase[x][y] = 0;
3924     ExplodeDelay[x][y] = 0;
3925     ExplodeField[x][y] = EX_TYPE_NONE;
3926
3927     RunnerVisit[x][y] = 0;
3928     PlayerVisit[x][y] = 0;
3929
3930     GfxFrame[x][y] = 0;
3931     GfxRandom[x][y] = INIT_GFX_RANDOM();
3932     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3933     GfxElement[x][y] = EL_UNDEFINED;
3934     GfxElementEmpty[x][y] = EL_EMPTY;
3935     GfxAction[x][y] = ACTION_DEFAULT;
3936     GfxDir[x][y] = MV_NONE;
3937     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3938   }
3939
3940   SCAN_PLAYFIELD(x, y)
3941   {
3942     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3943       emulate_bd = FALSE;
3944     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3945       emulate_sp = FALSE;
3946
3947     InitField(x, y, TRUE);
3948
3949     ResetGfxAnimation(x, y);
3950   }
3951
3952   InitBeltMovement();
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955   {
3956     struct PlayerInfo *player = &stored_player[i];
3957
3958     // set number of special actions for bored and sleeping animation
3959     player->num_special_action_bored =
3960       get_num_special_action(player->artwork_element,
3961                              ACTION_BORING_1, ACTION_BORING_LAST);
3962     player->num_special_action_sleeping =
3963       get_num_special_action(player->artwork_element,
3964                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3965   }
3966
3967   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3968                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3969
3970   // initialize type of slippery elements
3971   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3972   {
3973     if (!IS_CUSTOM_ELEMENT(i))
3974     {
3975       // default: elements slip down either to the left or right randomly
3976       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3977
3978       // SP style elements prefer to slip down on the left side
3979       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3980         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3981
3982       // BD style elements prefer to slip down on the left side
3983       if (game.emulation == EMU_BOULDERDASH)
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985     }
3986   }
3987
3988   // initialize explosion and ignition delay
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       int num_phase = 8;
3994       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3995                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3996                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3997       int last_phase = (num_phase + 1) * delay;
3998       int half_phase = (num_phase / 2) * delay;
3999
4000       element_info[i].explosion_delay = last_phase - 1;
4001       element_info[i].ignition_delay = half_phase;
4002
4003       if (i == EL_BLACK_ORB)
4004         element_info[i].ignition_delay = 1;
4005     }
4006   }
4007
4008   // correct non-moving belts to start moving left
4009   for (i = 0; i < NUM_BELTS; i++)
4010     if (game.belt_dir[i] == MV_NONE)
4011       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4012
4013 #if USE_NEW_PLAYER_ASSIGNMENTS
4014   // use preferred player also in local single-player mode
4015   if (!network.enabled && !game.team_mode)
4016   {
4017     int new_index_nr = setup.network_player_nr;
4018
4019     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4020     {
4021       for (i = 0; i < MAX_PLAYERS; i++)
4022         stored_player[i].connected_locally = FALSE;
4023
4024       stored_player[new_index_nr].connected_locally = TRUE;
4025     }
4026   }
4027
4028   for (i = 0; i < MAX_PLAYERS; i++)
4029   {
4030     stored_player[i].connected = FALSE;
4031
4032     // in network game mode, the local player might not be the first player
4033     if (stored_player[i].connected_locally)
4034       local_player = &stored_player[i];
4035   }
4036
4037   if (!network.enabled)
4038     local_player->connected = TRUE;
4039
4040   if (tape.playing)
4041   {
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043       stored_player[i].connected = tape.player_participates[i];
4044   }
4045   else if (network.enabled)
4046   {
4047     // add team mode players connected over the network (needed for correct
4048     // assignment of player figures from level to locally playing players)
4049
4050     for (i = 0; i < MAX_PLAYERS; i++)
4051       if (stored_player[i].connected_network)
4052         stored_player[i].connected = TRUE;
4053   }
4054   else if (game.team_mode)
4055   {
4056     // try to guess locally connected team mode players (needed for correct
4057     // assignment of player figures from level to locally playing players)
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060       if (setup.input[i].use_joystick ||
4061           setup.input[i].key.left != KSYM_UNDEFINED)
4062         stored_player[i].connected = TRUE;
4063   }
4064
4065 #if DEBUG_INIT_PLAYER
4066   DebugPrintPlayerStatus("Player status after level initialization");
4067 #endif
4068
4069 #if DEBUG_INIT_PLAYER
4070   Debug("game:init:player", "Reassigning players ...");
4071 #endif
4072
4073   // check if any connected player was not found in playfield
4074   for (i = 0; i < MAX_PLAYERS; i++)
4075   {
4076     struct PlayerInfo *player = &stored_player[i];
4077
4078     if (player->connected && !player->present)
4079     {
4080       struct PlayerInfo *field_player = NULL;
4081
4082 #if DEBUG_INIT_PLAYER
4083       Debug("game:init:player",
4084             "- looking for field player for player %d ...", i + 1);
4085 #endif
4086
4087       // assign first free player found that is present in the playfield
4088
4089       // first try: look for unmapped playfield player that is not connected
4090       for (j = 0; j < MAX_PLAYERS; j++)
4091         if (field_player == NULL &&
4092             stored_player[j].present &&
4093             !stored_player[j].mapped &&
4094             !stored_player[j].connected)
4095           field_player = &stored_player[j];
4096
4097       // second try: look for *any* unmapped playfield player
4098       for (j = 0; j < MAX_PLAYERS; j++)
4099         if (field_player == NULL &&
4100             stored_player[j].present &&
4101             !stored_player[j].mapped)
4102           field_player = &stored_player[j];
4103
4104       if (field_player != NULL)
4105       {
4106         int jx = field_player->jx, jy = field_player->jy;
4107
4108 #if DEBUG_INIT_PLAYER
4109         Debug("game:init:player", "- found player %d",
4110               field_player->index_nr + 1);
4111 #endif
4112
4113         player->present = FALSE;
4114         player->active = FALSE;
4115
4116         field_player->present = TRUE;
4117         field_player->active = TRUE;
4118
4119         /*
4120         player->initial_element = field_player->initial_element;
4121         player->artwork_element = field_player->artwork_element;
4122
4123         player->block_last_field       = field_player->block_last_field;
4124         player->block_delay_adjustment = field_player->block_delay_adjustment;
4125         */
4126
4127         StorePlayer[jx][jy] = field_player->element_nr;
4128
4129         field_player->jx = field_player->last_jx = jx;
4130         field_player->jy = field_player->last_jy = jy;
4131
4132         if (local_player == player)
4133           local_player = field_player;
4134
4135         map_player_action[field_player->index_nr] = i;
4136
4137         field_player->mapped = TRUE;
4138
4139 #if DEBUG_INIT_PLAYER
4140         Debug("game:init:player", "- map_player_action[%d] == %d",
4141               field_player->index_nr + 1, i + 1);
4142 #endif
4143       }
4144     }
4145
4146     if (player->connected && player->present)
4147       player->mapped = TRUE;
4148   }
4149
4150 #if DEBUG_INIT_PLAYER
4151   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4152 #endif
4153
4154 #else
4155
4156   // check if any connected player was not found in playfield
4157   for (i = 0; i < MAX_PLAYERS; i++)
4158   {
4159     struct PlayerInfo *player = &stored_player[i];
4160
4161     if (player->connected && !player->present)
4162     {
4163       for (j = 0; j < MAX_PLAYERS; j++)
4164       {
4165         struct PlayerInfo *field_player = &stored_player[j];
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168         // assign first free player found that is present in the playfield
4169         if (field_player->present && !field_player->connected)
4170         {
4171           player->present = TRUE;
4172           player->active = TRUE;
4173
4174           field_player->present = FALSE;
4175           field_player->active = FALSE;
4176
4177           player->initial_element = field_player->initial_element;
4178           player->artwork_element = field_player->artwork_element;
4179
4180           player->block_last_field       = field_player->block_last_field;
4181           player->block_delay_adjustment = field_player->block_delay_adjustment;
4182
4183           StorePlayer[jx][jy] = player->element_nr;
4184
4185           player->jx = player->last_jx = jx;
4186           player->jy = player->last_jy = jy;
4187
4188           break;
4189         }
4190       }
4191     }
4192   }
4193 #endif
4194
4195 #if 0
4196   Debug("game:init:player", "local_player->present == %d",
4197         local_player->present);
4198 #endif
4199
4200   // set focus to local player for network games, else to all players
4201   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4202   game.centered_player_nr_next = game.centered_player_nr;
4203   game.set_centered_player = FALSE;
4204   game.set_centered_player_wrap = FALSE;
4205
4206   if (network_playing && tape.recording)
4207   {
4208     // store client dependent player focus when recording network games
4209     tape.centered_player_nr_next = game.centered_player_nr_next;
4210     tape.set_centered_player = TRUE;
4211   }
4212
4213   if (tape.playing)
4214   {
4215     // when playing a tape, eliminate all players who do not participate
4216
4217 #if USE_NEW_PLAYER_ASSIGNMENTS
4218
4219     if (!game.team_mode)
4220     {
4221       for (i = 0; i < MAX_PLAYERS; i++)
4222       {
4223         if (stored_player[i].active &&
4224             !tape.player_participates[map_player_action[i]])
4225         {
4226           struct PlayerInfo *player = &stored_player[i];
4227           int jx = player->jx, jy = player->jy;
4228
4229 #if DEBUG_INIT_PLAYER
4230           Debug("game:init:player", "Removing player %d at (%d, %d)",
4231                 i + 1, jx, jy);
4232 #endif
4233
4234           player->active = FALSE;
4235           StorePlayer[jx][jy] = 0;
4236           Tile[jx][jy] = EL_EMPTY;
4237         }
4238       }
4239     }
4240
4241 #else
4242
4243     for (i = 0; i < MAX_PLAYERS; i++)
4244     {
4245       if (stored_player[i].active &&
4246           !tape.player_participates[i])
4247       {
4248         struct PlayerInfo *player = &stored_player[i];
4249         int jx = player->jx, jy = player->jy;
4250
4251         player->active = FALSE;
4252         StorePlayer[jx][jy] = 0;
4253         Tile[jx][jy] = EL_EMPTY;
4254       }
4255     }
4256 #endif
4257   }
4258   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4259   {
4260     // when in single player mode, eliminate all but the local player
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       struct PlayerInfo *player = &stored_player[i];
4265
4266       if (player->active && player != local_player)
4267       {
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         player->present = FALSE;
4272
4273         StorePlayer[jx][jy] = 0;
4274         Tile[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277   }
4278
4279   for (i = 0; i < MAX_PLAYERS; i++)
4280     if (stored_player[i].active)
4281       game.players_still_needed++;
4282
4283   if (level.solved_by_one_player)
4284     game.players_still_needed = 1;
4285
4286   // when recording the game, store which players take part in the game
4287   if (tape.recording)
4288   {
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291       if (stored_player[i].connected)
4292         tape.player_participates[i] = TRUE;
4293 #else
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].active)
4296         tape.player_participates[i] = TRUE;
4297 #endif
4298   }
4299
4300 #if DEBUG_INIT_PLAYER
4301   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4302 #endif
4303
4304   if (BorderElement == EL_EMPTY)
4305   {
4306     SBX_Left = 0;
4307     SBX_Right = lev_fieldx - SCR_FIELDX;
4308     SBY_Upper = 0;
4309     SBY_Lower = lev_fieldy - SCR_FIELDY;
4310   }
4311   else
4312   {
4313     SBX_Left = -1;
4314     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4315     SBY_Upper = -1;
4316     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4317   }
4318
4319   if (full_lev_fieldx <= SCR_FIELDX)
4320     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4321   if (full_lev_fieldy <= SCR_FIELDY)
4322     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4323
4324   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4325     SBX_Left--;
4326   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4327     SBY_Upper--;
4328
4329   // if local player not found, look for custom element that might create
4330   // the player (make some assumptions about the right custom element)
4331   if (!local_player->present)
4332   {
4333     int start_x = 0, start_y = 0;
4334     int found_rating = 0;
4335     int found_element = EL_UNDEFINED;
4336     int player_nr = local_player->index_nr;
4337
4338     SCAN_PLAYFIELD(x, y)
4339     {
4340       int element = Tile[x][y];
4341       int content;
4342       int xx, yy;
4343       boolean is_player;
4344
4345       if (level.use_start_element[player_nr] &&
4346           level.start_element[player_nr] == element &&
4347           found_rating < 4)
4348       {
4349         start_x = x;
4350         start_y = y;
4351
4352         found_rating = 4;
4353         found_element = element;
4354       }
4355
4356       if (!IS_CUSTOM_ELEMENT(element))
4357         continue;
4358
4359       if (CAN_CHANGE(element))
4360       {
4361         for (i = 0; i < element_info[element].num_change_pages; i++)
4362         {
4363           // check for player created from custom element as single target
4364           content = element_info[element].change_page[i].target_element;
4365           is_player = IS_PLAYER_ELEMENT(content);
4366
4367           if (is_player && (found_rating < 3 ||
4368                             (found_rating == 3 && element < found_element)))
4369           {
4370             start_x = x;
4371             start_y = y;
4372
4373             found_rating = 3;
4374             found_element = element;
4375           }
4376         }
4377       }
4378
4379       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4380       {
4381         // check for player created from custom element as explosion content
4382         content = element_info[element].content.e[xx][yy];
4383         is_player = IS_PLAYER_ELEMENT(content);
4384
4385         if (is_player && (found_rating < 2 ||
4386                           (found_rating == 2 && element < found_element)))
4387         {
4388           start_x = x + xx - 1;
4389           start_y = y + yy - 1;
4390
4391           found_rating = 2;
4392           found_element = element;
4393         }
4394
4395         if (!CAN_CHANGE(element))
4396           continue;
4397
4398         for (i = 0; i < element_info[element].num_change_pages; i++)
4399         {
4400           // check for player created from custom element as extended target
4401           content =
4402             element_info[element].change_page[i].target_content.e[xx][yy];
4403
4404           is_player = IS_PLAYER_ELEMENT(content);
4405
4406           if (is_player && (found_rating < 1 ||
4407                             (found_rating == 1 && element < found_element)))
4408           {
4409             start_x = x + xx - 1;
4410             start_y = y + yy - 1;
4411
4412             found_rating = 1;
4413             found_element = element;
4414           }
4415         }
4416       }
4417     }
4418
4419     scroll_x = SCROLL_POSITION_X(start_x);
4420     scroll_y = SCROLL_POSITION_Y(start_y);
4421   }
4422   else
4423   {
4424     scroll_x = SCROLL_POSITION_X(local_player->jx);
4425     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4426   }
4427
4428   // !!! FIX THIS (START) !!!
4429   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4430   {
4431     InitGameEngine_EM();
4432   }
4433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4434   {
4435     InitGameEngine_SP();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4438   {
4439     InitGameEngine_MM();
4440   }
4441   else
4442   {
4443     DrawLevel(REDRAW_FIELD);
4444     DrawAllPlayers();
4445
4446     // after drawing the level, correct some elements
4447     if (game.timegate_time_left == 0)
4448       CloseAllOpenTimegates();
4449   }
4450
4451   // blit playfield from scroll buffer to normal back buffer for fading in
4452   BlitScreenToBitmap(backbuffer);
4453   // !!! FIX THIS (END) !!!
4454
4455   DrawMaskedBorder(fade_mask);
4456
4457   FadeIn(fade_mask);
4458
4459 #if 1
4460   // full screen redraw is required at this point in the following cases:
4461   // - special editor door undrawn when game was started from level editor
4462   // - drawing area (playfield) was changed and has to be removed completely
4463   redraw_mask = REDRAW_ALL;
4464   BackToFront();
4465 #endif
4466
4467   if (!game.restart_level)
4468   {
4469     // copy default game door content to main double buffer
4470
4471     // !!! CHECK AGAIN !!!
4472     SetPanelBackground();
4473     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4474     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4475   }
4476
4477   SetPanelBackground();
4478   SetDrawBackgroundMask(REDRAW_DOOR_1);
4479
4480   UpdateAndDisplayGameControlValues();
4481
4482   if (!game.restart_level)
4483   {
4484     UnmapGameButtons();
4485     UnmapTapeButtons();
4486
4487     FreeGameButtons();
4488     CreateGameButtons();
4489
4490     MapGameButtons();
4491     MapTapeButtons();
4492
4493     // copy actual game door content to door double buffer for OpenDoor()
4494     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4495
4496     OpenDoor(DOOR_OPEN_ALL);
4497
4498     KeyboardAutoRepeatOffUnlessAutoplay();
4499
4500 #if DEBUG_INIT_PLAYER
4501     DebugPrintPlayerStatus("Player status (final)");
4502 #endif
4503   }
4504
4505   UnmapAllGadgets();
4506
4507   MapGameButtons();
4508   MapTapeButtons();
4509
4510   if (!game.restart_level && !tape.playing)
4511   {
4512     LevelStats_incPlayed(level_nr);
4513
4514     SaveLevelSetup_SeriesInfo();
4515   }
4516
4517   game.restart_level = FALSE;
4518
4519   game.request_active = FALSE;
4520   game.request_active_or_moving = FALSE;
4521
4522   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4523     InitGameActions_MM();
4524
4525   SaveEngineSnapshotToListInitial();
4526
4527   if (!game.restart_level)
4528   {
4529     PlaySound(SND_GAME_STARTING);
4530
4531     if (setup.sound_music)
4532       PlayLevelMusic();
4533   }
4534
4535   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4536 }
4537
4538 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4539                         int actual_player_x, int actual_player_y)
4540 {
4541   // this is used for non-R'n'D game engines to update certain engine values
4542
4543   // needed to determine if sounds are played within the visible screen area
4544   scroll_x = actual_scroll_x;
4545   scroll_y = actual_scroll_y;
4546
4547   // needed to get player position for "follow finger" playing input method
4548   local_player->jx = actual_player_x;
4549   local_player->jy = actual_player_y;
4550 }
4551
4552 void InitMovDir(int x, int y)
4553 {
4554   int i, element = Tile[x][y];
4555   static int xy[4][2] =
4556   {
4557     {  0, +1 },
4558     { +1,  0 },
4559     {  0, -1 },
4560     { -1,  0 }
4561   };
4562   static int direction[3][4] =
4563   {
4564     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4565     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4566     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4567   };
4568
4569   switch (element)
4570   {
4571     case EL_BUG_RIGHT:
4572     case EL_BUG_UP:
4573     case EL_BUG_LEFT:
4574     case EL_BUG_DOWN:
4575       Tile[x][y] = EL_BUG;
4576       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4577       break;
4578
4579     case EL_SPACESHIP_RIGHT:
4580     case EL_SPACESHIP_UP:
4581     case EL_SPACESHIP_LEFT:
4582     case EL_SPACESHIP_DOWN:
4583       Tile[x][y] = EL_SPACESHIP;
4584       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4585       break;
4586
4587     case EL_BD_BUTTERFLY_RIGHT:
4588     case EL_BD_BUTTERFLY_UP:
4589     case EL_BD_BUTTERFLY_LEFT:
4590     case EL_BD_BUTTERFLY_DOWN:
4591       Tile[x][y] = EL_BD_BUTTERFLY;
4592       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4593       break;
4594
4595     case EL_BD_FIREFLY_RIGHT:
4596     case EL_BD_FIREFLY_UP:
4597     case EL_BD_FIREFLY_LEFT:
4598     case EL_BD_FIREFLY_DOWN:
4599       Tile[x][y] = EL_BD_FIREFLY;
4600       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4601       break;
4602
4603     case EL_PACMAN_RIGHT:
4604     case EL_PACMAN_UP:
4605     case EL_PACMAN_LEFT:
4606     case EL_PACMAN_DOWN:
4607       Tile[x][y] = EL_PACMAN;
4608       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4609       break;
4610
4611     case EL_YAMYAM_LEFT:
4612     case EL_YAMYAM_RIGHT:
4613     case EL_YAMYAM_UP:
4614     case EL_YAMYAM_DOWN:
4615       Tile[x][y] = EL_YAMYAM;
4616       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4617       break;
4618
4619     case EL_SP_SNIKSNAK:
4620       MovDir[x][y] = MV_UP;
4621       break;
4622
4623     case EL_SP_ELECTRON:
4624       MovDir[x][y] = MV_LEFT;
4625       break;
4626
4627     case EL_MOLE_LEFT:
4628     case EL_MOLE_RIGHT:
4629     case EL_MOLE_UP:
4630     case EL_MOLE_DOWN:
4631       Tile[x][y] = EL_MOLE;
4632       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4633       break;
4634
4635     case EL_SPRING_LEFT:
4636     case EL_SPRING_RIGHT:
4637       Tile[x][y] = EL_SPRING;
4638       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4639       break;
4640
4641     default:
4642       if (IS_CUSTOM_ELEMENT(element))
4643       {
4644         struct ElementInfo *ei = &element_info[element];
4645         int move_direction_initial = ei->move_direction_initial;
4646         int move_pattern = ei->move_pattern;
4647
4648         if (move_direction_initial == MV_START_PREVIOUS)
4649         {
4650           if (MovDir[x][y] != MV_NONE)
4651             return;
4652
4653           move_direction_initial = MV_START_AUTOMATIC;
4654         }
4655
4656         if (move_direction_initial == MV_START_RANDOM)
4657           MovDir[x][y] = 1 << RND(4);
4658         else if (move_direction_initial & MV_ANY_DIRECTION)
4659           MovDir[x][y] = move_direction_initial;
4660         else if (move_pattern == MV_ALL_DIRECTIONS ||
4661                  move_pattern == MV_TURNING_LEFT ||
4662                  move_pattern == MV_TURNING_RIGHT ||
4663                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4664                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4665                  move_pattern == MV_TURNING_RANDOM)
4666           MovDir[x][y] = 1 << RND(4);
4667         else if (move_pattern == MV_HORIZONTAL)
4668           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4669         else if (move_pattern == MV_VERTICAL)
4670           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4671         else if (move_pattern & MV_ANY_DIRECTION)
4672           MovDir[x][y] = element_info[element].move_pattern;
4673         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4674                  move_pattern == MV_ALONG_RIGHT_SIDE)
4675         {
4676           // use random direction as default start direction
4677           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4678             MovDir[x][y] = 1 << RND(4);
4679
4680           for (i = 0; i < NUM_DIRECTIONS; i++)
4681           {
4682             int x1 = x + xy[i][0];
4683             int y1 = y + xy[i][1];
4684
4685             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4686             {
4687               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4688                 MovDir[x][y] = direction[0][i];
4689               else
4690                 MovDir[x][y] = direction[1][i];
4691
4692               break;
4693             }
4694           }
4695         }                
4696       }
4697       else
4698       {
4699         MovDir[x][y] = 1 << RND(4);
4700
4701         if (element != EL_BUG &&
4702             element != EL_SPACESHIP &&
4703             element != EL_BD_BUTTERFLY &&
4704             element != EL_BD_FIREFLY)
4705           break;
4706
4707         for (i = 0; i < NUM_DIRECTIONS; i++)
4708         {
4709           int x1 = x + xy[i][0];
4710           int y1 = y + xy[i][1];
4711
4712           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4713           {
4714             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4715             {
4716               MovDir[x][y] = direction[0][i];
4717               break;
4718             }
4719             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4720                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4721             {
4722               MovDir[x][y] = direction[1][i];
4723               break;
4724             }
4725           }
4726         }
4727       }
4728       break;
4729   }
4730
4731   GfxDir[x][y] = MovDir[x][y];
4732 }
4733
4734 void InitAmoebaNr(int x, int y)
4735 {
4736   int i;
4737   int group_nr = AmoebaNeighbourNr(x, y);
4738
4739   if (group_nr == 0)
4740   {
4741     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4742     {
4743       if (AmoebaCnt[i] == 0)
4744       {
4745         group_nr = i;
4746         break;
4747       }
4748     }
4749   }
4750
4751   AmoebaNr[x][y] = group_nr;
4752   AmoebaCnt[group_nr]++;
4753   AmoebaCnt2[group_nr]++;
4754 }
4755
4756 static void LevelSolved_SetFinalGameValues(void)
4757 {
4758   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4759   game.score_time_final = (level.use_step_counter ? TimePlayed :
4760                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4761
4762   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4763                       game_em.lev->score :
4764                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4765                       game_mm.score :
4766                       game.score);
4767
4768   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4769                        MM_HEALTH(game_mm.laser_overload_value) :
4770                        game.health);
4771
4772   game.LevelSolved_CountingTime = game.time_final;
4773   game.LevelSolved_CountingScore = game.score_final;
4774   game.LevelSolved_CountingHealth = game.health_final;
4775 }
4776
4777 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4778 {
4779   game.LevelSolved_CountingTime = time;
4780   game.LevelSolved_CountingScore = score;
4781   game.LevelSolved_CountingHealth = health;
4782
4783   game_panel_controls[GAME_PANEL_TIME].value = time;
4784   game_panel_controls[GAME_PANEL_SCORE].value = score;
4785   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4786
4787   DisplayGameControlValues();
4788 }
4789
4790 static void LevelSolved(void)
4791 {
4792   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4793       game.players_still_needed > 0)
4794     return;
4795
4796   game.LevelSolved = TRUE;
4797   game.GameOver = TRUE;
4798
4799   tape.solved = TRUE;
4800
4801   // needed here to display correct panel values while player walks into exit
4802   LevelSolved_SetFinalGameValues();
4803 }
4804
4805 void GameWon(void)
4806 {
4807   static int time_count_steps;
4808   static int time, time_final;
4809   static float score, score_final; // needed for time score < 10 for 10 seconds
4810   static int health, health_final;
4811   static int game_over_delay_1 = 0;
4812   static int game_over_delay_2 = 0;
4813   static int game_over_delay_3 = 0;
4814   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4815   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4816
4817   if (!game.LevelSolved_GameWon)
4818   {
4819     int i;
4820
4821     // do not start end game actions before the player stops moving (to exit)
4822     if (local_player->active && local_player->MovPos)
4823       return;
4824
4825     // calculate final game values after player finished walking into exit
4826     LevelSolved_SetFinalGameValues();
4827
4828     game.LevelSolved_GameWon = TRUE;
4829     game.LevelSolved_SaveTape = tape.recording;
4830     game.LevelSolved_SaveScore = !tape.playing;
4831
4832     if (!tape.playing)
4833     {
4834       LevelStats_incSolved(level_nr);
4835
4836       SaveLevelSetup_SeriesInfo();
4837     }
4838
4839     if (tape.auto_play)         // tape might already be stopped here
4840       tape.auto_play_level_solved = TRUE;
4841
4842     TapeStop();
4843
4844     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4845     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4846     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4847
4848     time = time_final = game.time_final;
4849     score = score_final = game.score_final;
4850     health = health_final = game.health_final;
4851
4852     // update game panel values before (delayed) counting of score (if any)
4853     LevelSolved_DisplayFinalGameValues(time, score, health);
4854
4855     // if level has time score defined, calculate new final game values
4856     if (time_score > 0)
4857     {
4858       int time_final_max = 999;
4859       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4860       int time_frames = 0;
4861       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4862       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4863
4864       if (TimeLeft > 0)
4865       {
4866         time_final = 0;
4867         time_frames = time_frames_left;
4868       }
4869       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4870       {
4871         time_final = time_final_max;
4872         time_frames = time_frames_final_max - time_frames_played;
4873       }
4874
4875       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4876
4877       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4878
4879       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4880       {
4881         health_final = 0;
4882         score_final += health * time_score;
4883       }
4884
4885       game.score_final = score_final;
4886       game.health_final = health_final;
4887     }
4888
4889     // if not counting score after game, immediately update game panel values
4890     if (level_editor_test_game || !setup.count_score_after_game)
4891     {
4892       time = time_final;
4893       score = score_final;
4894
4895       LevelSolved_DisplayFinalGameValues(time, score, health);
4896     }
4897
4898     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4899     {
4900       // check if last player has left the level
4901       if (game.exit_x >= 0 &&
4902           game.exit_y >= 0)
4903       {
4904         int x = game.exit_x;
4905         int y = game.exit_y;
4906         int element = Tile[x][y];
4907
4908         // close exit door after last player
4909         if ((game.all_players_gone &&
4910              (element == EL_EXIT_OPEN ||
4911               element == EL_SP_EXIT_OPEN ||
4912               element == EL_STEEL_EXIT_OPEN)) ||
4913             element == EL_EM_EXIT_OPEN ||
4914             element == EL_EM_STEEL_EXIT_OPEN)
4915         {
4916
4917           Tile[x][y] =
4918             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4919              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4920              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4921              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4922              EL_EM_STEEL_EXIT_CLOSING);
4923
4924           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4925         }
4926
4927         // player disappears
4928         DrawLevelField(x, y);
4929       }
4930
4931       for (i = 0; i < MAX_PLAYERS; i++)
4932       {
4933         struct PlayerInfo *player = &stored_player[i];
4934
4935         if (player->present)
4936         {
4937           RemovePlayer(player);
4938
4939           // player disappears
4940           DrawLevelField(player->jx, player->jy);
4941         }
4942       }
4943     }
4944
4945     PlaySound(SND_GAME_WINNING);
4946   }
4947
4948   if (setup.count_score_after_game)
4949   {
4950     if (time != time_final)
4951     {
4952       if (game_over_delay_1 > 0)
4953       {
4954         game_over_delay_1--;
4955
4956         return;
4957       }
4958
4959       int time_to_go = ABS(time_final - time);
4960       int time_count_dir = (time < time_final ? +1 : -1);
4961
4962       if (time_to_go < time_count_steps)
4963         time_count_steps = 1;
4964
4965       time  += time_count_steps * time_count_dir;
4966       score += time_count_steps * time_score;
4967
4968       // set final score to correct rounding differences after counting score
4969       if (time == time_final)
4970         score = score_final;
4971
4972       LevelSolved_DisplayFinalGameValues(time, score, health);
4973
4974       if (time == time_final)
4975         StopSound(SND_GAME_LEVELTIME_BONUS);
4976       else if (setup.sound_loops)
4977         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4978       else
4979         PlaySound(SND_GAME_LEVELTIME_BONUS);
4980
4981       return;
4982     }
4983
4984     if (health != health_final)
4985     {
4986       if (game_over_delay_2 > 0)
4987       {
4988         game_over_delay_2--;
4989
4990         return;
4991       }
4992
4993       int health_count_dir = (health < health_final ? +1 : -1);
4994
4995       health += health_count_dir;
4996       score  += time_score;
4997
4998       LevelSolved_DisplayFinalGameValues(time, score, health);
4999
5000       if (health == health_final)
5001         StopSound(SND_GAME_LEVELTIME_BONUS);
5002       else if (setup.sound_loops)
5003         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5004       else
5005         PlaySound(SND_GAME_LEVELTIME_BONUS);
5006
5007       return;
5008     }
5009   }
5010
5011   game.panel.active = FALSE;
5012
5013   if (game_over_delay_3 > 0)
5014   {
5015     game_over_delay_3--;
5016
5017     return;
5018   }
5019
5020   GameEnd();
5021 }
5022
5023 void GameEnd(void)
5024 {
5025   // used instead of "level_nr" (needed for network games)
5026   int last_level_nr = levelset.level_nr;
5027   boolean tape_saved = FALSE;
5028
5029   game.LevelSolved_GameEnd = TRUE;
5030
5031   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5032   {
5033     // make sure that request dialog to save tape does not open door again
5034     if (!global.use_envelope_request)
5035       CloseDoor(DOOR_CLOSE_1);
5036
5037     // ask to save tape
5038     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5039
5040     // set unique basename for score tape (also saved in high score table)
5041     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5042   }
5043
5044   // if no tape is to be saved, close both doors simultaneously
5045   CloseDoor(DOOR_CLOSE_ALL);
5046
5047   if (level_editor_test_game || score_info_tape_play)
5048   {
5049     SetGameStatus(GAME_MODE_MAIN);
5050
5051     DrawMainMenu();
5052
5053     return;
5054   }
5055
5056   if (!game.LevelSolved_SaveScore)
5057   {
5058     SetGameStatus(GAME_MODE_MAIN);
5059
5060     DrawMainMenu();
5061
5062     return;
5063   }
5064
5065   if (level_nr == leveldir_current->handicap_level)
5066   {
5067     leveldir_current->handicap_level++;
5068
5069     SaveLevelSetup_SeriesInfo();
5070   }
5071
5072   // save score and score tape before potentially erasing tape below
5073   NewHighScore(last_level_nr, tape_saved);
5074
5075   if (setup.increment_levels &&
5076       level_nr < leveldir_current->last_level &&
5077       !network_playing)
5078   {
5079     level_nr++;         // advance to next level
5080     TapeErase();        // start with empty tape
5081
5082     if (setup.auto_play_next_level)
5083     {
5084       scores.continue_playing = TRUE;
5085       scores.next_level_nr = level_nr;
5086
5087       LoadLevel(level_nr);
5088
5089       SaveLevelSetup_SeriesInfo();
5090     }
5091   }
5092
5093   if (scores.last_added >= 0 && setup.show_scores_after_game)
5094   {
5095     SetGameStatus(GAME_MODE_SCORES);
5096
5097     DrawHallOfFame(last_level_nr);
5098   }
5099   else if (scores.continue_playing)
5100   {
5101     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5102   }
5103   else
5104   {
5105     SetGameStatus(GAME_MODE_MAIN);
5106
5107     DrawMainMenu();
5108   }
5109 }
5110
5111 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5112                          boolean one_score_entry_per_name)
5113 {
5114   int i;
5115
5116   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5117     return -1;
5118
5119   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5120   {
5121     struct ScoreEntry *entry = &list->entry[i];
5122     boolean score_is_better = (new_entry->score >  entry->score);
5123     boolean score_is_equal  = (new_entry->score == entry->score);
5124     boolean time_is_better  = (new_entry->time  <  entry->time);
5125     boolean time_is_equal   = (new_entry->time  == entry->time);
5126     boolean better_by_score = (score_is_better ||
5127                                (score_is_equal && time_is_better));
5128     boolean better_by_time  = (time_is_better ||
5129                                (time_is_equal && score_is_better));
5130     boolean is_better = (level.rate_time_over_score ? better_by_time :
5131                          better_by_score);
5132     boolean entry_is_empty = (entry->score == 0 &&
5133                               entry->time == 0);
5134
5135     // prevent adding server score entries if also existing in local score file
5136     // (special case: historic score entries have an empty tape basename entry)
5137     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5138         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5139     {
5140       // add fields from server score entry not stored in local score entry
5141       // (currently, this means setting platform, version and country fields;
5142       // in rare cases, this may also correct an invalid score value, as
5143       // historic scores might have been truncated to 16-bit values locally)
5144       *entry = *new_entry;
5145
5146       return -1;
5147     }
5148
5149     if (is_better || entry_is_empty)
5150     {
5151       // player has made it to the hall of fame
5152
5153       if (i < MAX_SCORE_ENTRIES - 1)
5154       {
5155         int m = MAX_SCORE_ENTRIES - 1;
5156         int l;
5157
5158         if (one_score_entry_per_name)
5159         {
5160           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5161             if (strEqual(list->entry[l].name, new_entry->name))
5162               m = l;
5163
5164           if (m == i)   // player's new highscore overwrites his old one
5165             goto put_into_list;
5166         }
5167
5168         for (l = m; l > i; l--)
5169           list->entry[l] = list->entry[l - 1];
5170       }
5171
5172       put_into_list:
5173
5174       *entry = *new_entry;
5175
5176       return i;
5177     }
5178     else if (one_score_entry_per_name &&
5179              strEqual(entry->name, new_entry->name))
5180     {
5181       // player already in high score list with better score or time
5182
5183       return -1;
5184     }
5185   }
5186
5187   // special case: new score is beyond the last high score list position
5188   return MAX_SCORE_ENTRIES;
5189 }
5190
5191 void NewHighScore(int level_nr, boolean tape_saved)
5192 {
5193   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5194   boolean one_per_name = FALSE;
5195
5196   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5197   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5198
5199   new_entry.score = game.score_final;
5200   new_entry.time = game.score_time_final;
5201
5202   LoadScore(level_nr);
5203
5204   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5205
5206   if (scores.last_added >= MAX_SCORE_ENTRIES)
5207   {
5208     scores.last_added = MAX_SCORE_ENTRIES - 1;
5209     scores.force_last_added = TRUE;
5210
5211     scores.entry[scores.last_added] = new_entry;
5212
5213     // store last added local score entry (before merging server scores)
5214     scores.last_added_local = scores.last_added;
5215
5216     return;
5217   }
5218
5219   if (scores.last_added < 0)
5220     return;
5221
5222   SaveScore(level_nr);
5223
5224   // store last added local score entry (before merging server scores)
5225   scores.last_added_local = scores.last_added;
5226
5227   if (!game.LevelSolved_SaveTape)
5228     return;
5229
5230   SaveScoreTape(level_nr);
5231
5232   if (setup.ask_for_using_api_server)
5233   {
5234     setup.use_api_server =
5235       Request("Upload your score and tape to the high score server?", REQ_ASK);
5236
5237     if (!setup.use_api_server)
5238       Request("Not using high score server! Use setup menu to enable again!",
5239               REQ_CONFIRM);
5240
5241     runtime.use_api_server = setup.use_api_server;
5242
5243     // after asking for using API server once, do not ask again
5244     setup.ask_for_using_api_server = FALSE;
5245
5246     SaveSetup_ServerSetup();
5247   }
5248
5249   SaveServerScore(level_nr, tape_saved);
5250 }
5251
5252 void MergeServerScore(void)
5253 {
5254   struct ScoreEntry last_added_entry;
5255   boolean one_per_name = FALSE;
5256   int i;
5257
5258   if (scores.last_added >= 0)
5259     last_added_entry = scores.entry[scores.last_added];
5260
5261   for (i = 0; i < server_scores.num_entries; i++)
5262   {
5263     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5264
5265     if (pos >= 0 && pos <= scores.last_added)
5266       scores.last_added++;
5267   }
5268
5269   if (scores.last_added >= MAX_SCORE_ENTRIES)
5270   {
5271     scores.last_added = MAX_SCORE_ENTRIES - 1;
5272     scores.force_last_added = TRUE;
5273
5274     scores.entry[scores.last_added] = last_added_entry;
5275   }
5276 }
5277
5278 static int getElementMoveStepsizeExt(int x, int y, int direction)
5279 {
5280   int element = Tile[x][y];
5281   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5282   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5283   int horiz_move = (dx != 0);
5284   int sign = (horiz_move ? dx : dy);
5285   int step = sign * element_info[element].move_stepsize;
5286
5287   // special values for move stepsize for spring and things on conveyor belt
5288   if (horiz_move)
5289   {
5290     if (CAN_FALL(element) &&
5291         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5292       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5293     else if (element == EL_SPRING)
5294       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5295   }
5296
5297   return step;
5298 }
5299
5300 static int getElementMoveStepsize(int x, int y)
5301 {
5302   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5303 }
5304
5305 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5306 {
5307   if (player->GfxAction != action || player->GfxDir != dir)
5308   {
5309     player->GfxAction = action;
5310     player->GfxDir = dir;
5311     player->Frame = 0;
5312     player->StepFrame = 0;
5313   }
5314 }
5315
5316 static void ResetGfxFrame(int x, int y)
5317 {
5318   // profiling showed that "autotest" spends 10~20% of its time in this function
5319   if (DrawingDeactivatedField())
5320     return;
5321
5322   int element = Tile[x][y];
5323   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5324
5325   if (graphic_info[graphic].anim_global_sync)
5326     GfxFrame[x][y] = FrameCounter;
5327   else if (graphic_info[graphic].anim_global_anim_sync)
5328     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5329   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5330     GfxFrame[x][y] = CustomValue[x][y];
5331   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5332     GfxFrame[x][y] = element_info[element].collect_score;
5333   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5334     GfxFrame[x][y] = ChangeDelay[x][y];
5335 }
5336
5337 static void ResetGfxAnimation(int x, int y)
5338 {
5339   GfxAction[x][y] = ACTION_DEFAULT;
5340   GfxDir[x][y] = MovDir[x][y];
5341   GfxFrame[x][y] = 0;
5342
5343   ResetGfxFrame(x, y);
5344 }
5345
5346 static void ResetRandomAnimationValue(int x, int y)
5347 {
5348   GfxRandom[x][y] = INIT_GFX_RANDOM();
5349 }
5350
5351 static void InitMovingField(int x, int y, int direction)
5352 {
5353   int element = Tile[x][y];
5354   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5355   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5356   int newx = x + dx;
5357   int newy = y + dy;
5358   boolean is_moving_before, is_moving_after;
5359
5360   // check if element was/is moving or being moved before/after mode change
5361   is_moving_before = (WasJustMoving[x][y] != 0);
5362   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5363
5364   // reset animation only for moving elements which change direction of moving
5365   // or which just started or stopped moving
5366   // (else CEs with property "can move" / "not moving" are reset each frame)
5367   if (is_moving_before != is_moving_after ||
5368       direction != MovDir[x][y])
5369     ResetGfxAnimation(x, y);
5370
5371   MovDir[x][y] = direction;
5372   GfxDir[x][y] = direction;
5373
5374   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5375                      direction == MV_DOWN && CAN_FALL(element) ?
5376                      ACTION_FALLING : ACTION_MOVING);
5377
5378   // this is needed for CEs with property "can move" / "not moving"
5379
5380   if (is_moving_after)
5381   {
5382     if (Tile[newx][newy] == EL_EMPTY)
5383       Tile[newx][newy] = EL_BLOCKED;
5384
5385     MovDir[newx][newy] = MovDir[x][y];
5386
5387     CustomValue[newx][newy] = CustomValue[x][y];
5388
5389     GfxFrame[newx][newy] = GfxFrame[x][y];
5390     GfxRandom[newx][newy] = GfxRandom[x][y];
5391     GfxAction[newx][newy] = GfxAction[x][y];
5392     GfxDir[newx][newy] = GfxDir[x][y];
5393   }
5394 }
5395
5396 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5397 {
5398   int direction = MovDir[x][y];
5399   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5400   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5401
5402   *goes_to_x = newx;
5403   *goes_to_y = newy;
5404 }
5405
5406 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5407 {
5408   int oldx = x, oldy = y;
5409   int direction = MovDir[x][y];
5410
5411   if (direction == MV_LEFT)
5412     oldx++;
5413   else if (direction == MV_RIGHT)
5414     oldx--;
5415   else if (direction == MV_UP)
5416     oldy++;
5417   else if (direction == MV_DOWN)
5418     oldy--;
5419
5420   *comes_from_x = oldx;
5421   *comes_from_y = oldy;
5422 }
5423
5424 static int MovingOrBlocked2Element(int x, int y)
5425 {
5426   int element = Tile[x][y];
5427
5428   if (element == EL_BLOCKED)
5429   {
5430     int oldx, oldy;
5431
5432     Blocked2Moving(x, y, &oldx, &oldy);
5433
5434     return Tile[oldx][oldy];
5435   }
5436
5437   return element;
5438 }
5439
5440 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5441 {
5442   // like MovingOrBlocked2Element(), but if element is moving
5443   // and (x, y) is the field the moving element is just leaving,
5444   // return EL_BLOCKED instead of the element value
5445   int element = Tile[x][y];
5446
5447   if (IS_MOVING(x, y))
5448   {
5449     if (element == EL_BLOCKED)
5450     {
5451       int oldx, oldy;
5452
5453       Blocked2Moving(x, y, &oldx, &oldy);
5454       return Tile[oldx][oldy];
5455     }
5456     else
5457       return EL_BLOCKED;
5458   }
5459   else
5460     return element;
5461 }
5462
5463 static void RemoveField(int x, int y)
5464 {
5465   Tile[x][y] = EL_EMPTY;
5466
5467   MovPos[x][y] = 0;
5468   MovDir[x][y] = 0;
5469   MovDelay[x][y] = 0;
5470
5471   CustomValue[x][y] = 0;
5472
5473   AmoebaNr[x][y] = 0;
5474   ChangeDelay[x][y] = 0;
5475   ChangePage[x][y] = -1;
5476   Pushed[x][y] = FALSE;
5477
5478   GfxElement[x][y] = EL_UNDEFINED;
5479   GfxAction[x][y] = ACTION_DEFAULT;
5480   GfxDir[x][y] = MV_NONE;
5481 }
5482
5483 static void RemoveMovingField(int x, int y)
5484 {
5485   int oldx = x, oldy = y, newx = x, newy = y;
5486   int element = Tile[x][y];
5487   int next_element = EL_UNDEFINED;
5488
5489   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5490     return;
5491
5492   if (IS_MOVING(x, y))
5493   {
5494     Moving2Blocked(x, y, &newx, &newy);
5495
5496     if (Tile[newx][newy] != EL_BLOCKED)
5497     {
5498       // element is moving, but target field is not free (blocked), but
5499       // already occupied by something different (example: acid pool);
5500       // in this case, only remove the moving field, but not the target
5501
5502       RemoveField(oldx, oldy);
5503
5504       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5505
5506       TEST_DrawLevelField(oldx, oldy);
5507
5508       return;
5509     }
5510   }
5511   else if (element == EL_BLOCKED)
5512   {
5513     Blocked2Moving(x, y, &oldx, &oldy);
5514     if (!IS_MOVING(oldx, oldy))
5515       return;
5516   }
5517
5518   if (element == EL_BLOCKED &&
5519       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5520        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5521        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5522        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5523        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5524        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5525     next_element = get_next_element(Tile[oldx][oldy]);
5526
5527   RemoveField(oldx, oldy);
5528   RemoveField(newx, newy);
5529
5530   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5531
5532   if (next_element != EL_UNDEFINED)
5533     Tile[oldx][oldy] = next_element;
5534
5535   TEST_DrawLevelField(oldx, oldy);
5536   TEST_DrawLevelField(newx, newy);
5537 }
5538
5539 void DrawDynamite(int x, int y)
5540 {
5541   int sx = SCREENX(x), sy = SCREENY(y);
5542   int graphic = el2img(Tile[x][y]);
5543   int frame;
5544
5545   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5546     return;
5547
5548   if (IS_WALKABLE_INSIDE(Back[x][y]))
5549     return;
5550
5551   if (Back[x][y])
5552     DrawLevelElement(x, y, Back[x][y]);
5553   else if (Store[x][y])
5554     DrawLevelElement(x, y, Store[x][y]);
5555   else if (game.use_masked_elements)
5556     DrawLevelElement(x, y, EL_EMPTY);
5557
5558   frame = getGraphicAnimationFrameXY(graphic, x, y);
5559
5560   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5561     DrawGraphicThruMask(sx, sy, graphic, frame);
5562   else
5563     DrawGraphic(sx, sy, graphic, frame);
5564 }
5565
5566 static void CheckDynamite(int x, int y)
5567 {
5568   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5569   {
5570     MovDelay[x][y]--;
5571
5572     if (MovDelay[x][y] != 0)
5573     {
5574       DrawDynamite(x, y);
5575       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5576
5577       return;
5578     }
5579   }
5580
5581   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5582
5583   Bang(x, y);
5584 }
5585
5586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5587 {
5588   boolean num_checked_players = 0;
5589   int i;
5590
5591   for (i = 0; i < MAX_PLAYERS; i++)
5592   {
5593     if (stored_player[i].active)
5594     {
5595       int sx = stored_player[i].jx;
5596       int sy = stored_player[i].jy;
5597
5598       if (num_checked_players == 0)
5599       {
5600         *sx1 = *sx2 = sx;
5601         *sy1 = *sy2 = sy;
5602       }
5603       else
5604       {
5605         *sx1 = MIN(*sx1, sx);
5606         *sy1 = MIN(*sy1, sy);
5607         *sx2 = MAX(*sx2, sx);
5608         *sy2 = MAX(*sy2, sy);
5609       }
5610
5611       num_checked_players++;
5612     }
5613   }
5614 }
5615
5616 static boolean checkIfAllPlayersFitToScreen_RND(void)
5617 {
5618   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5619
5620   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5621
5622   return (sx2 - sx1 < SCR_FIELDX &&
5623           sy2 - sy1 < SCR_FIELDY);
5624 }
5625
5626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5627 {
5628   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5629
5630   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5631
5632   *sx = (sx1 + sx2) / 2;
5633   *sy = (sy1 + sy2) / 2;
5634 }
5635
5636 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5637                                boolean center_screen, boolean quick_relocation)
5638 {
5639   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5640   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5641   boolean no_delay = (tape.warp_forward);
5642   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5643   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5644   int new_scroll_x, new_scroll_y;
5645
5646   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5647   {
5648     // case 1: quick relocation inside visible screen (without scrolling)
5649
5650     RedrawPlayfield();
5651
5652     return;
5653   }
5654
5655   if (!level.shifted_relocation || center_screen)
5656   {
5657     // relocation _with_ centering of screen
5658
5659     new_scroll_x = SCROLL_POSITION_X(x);
5660     new_scroll_y = SCROLL_POSITION_Y(y);
5661   }
5662   else
5663   {
5664     // relocation _without_ centering of screen
5665
5666     int center_scroll_x = SCROLL_POSITION_X(old_x);
5667     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5668     int offset_x = x + (scroll_x - center_scroll_x);
5669     int offset_y = y + (scroll_y - center_scroll_y);
5670
5671     // for new screen position, apply previous offset to center position
5672     new_scroll_x = SCROLL_POSITION_X(offset_x);
5673     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5674   }
5675
5676   if (quick_relocation)
5677   {
5678     // case 2: quick relocation (redraw without visible scrolling)
5679
5680     scroll_x = new_scroll_x;
5681     scroll_y = new_scroll_y;
5682
5683     RedrawPlayfield();
5684
5685     return;
5686   }
5687
5688   // case 3: visible relocation (with scrolling to new position)
5689
5690   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5691
5692   SetVideoFrameDelay(wait_delay_value);
5693
5694   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5695   {
5696     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5697     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5698
5699     if (dx == 0 && dy == 0)             // no scrolling needed at all
5700       break;
5701
5702     scroll_x -= dx;
5703     scroll_y -= dy;
5704
5705     // set values for horizontal/vertical screen scrolling (half tile size)
5706     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5707     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5708     int pos_x = dx * TILEX / 2;
5709     int pos_y = dy * TILEY / 2;
5710     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5711     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5712
5713     ScrollLevel(dx, dy);
5714     DrawAllPlayers();
5715
5716     // scroll in two steps of half tile size to make things smoother
5717     BlitScreenToBitmapExt_RND(window, fx, fy);
5718
5719     // scroll second step to align at full tile size
5720     BlitScreenToBitmap(window);
5721   }
5722
5723   DrawAllPlayers();
5724   BackToFront();
5725
5726   SetVideoFrameDelay(frame_delay_value_old);
5727 }
5728
5729 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5730 {
5731   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5732   int player_nr = GET_PLAYER_NR(el_player);
5733   struct PlayerInfo *player = &stored_player[player_nr];
5734   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5735   boolean no_delay = (tape.warp_forward);
5736   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5737   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5738   int old_jx = player->jx;
5739   int old_jy = player->jy;
5740   int old_element = Tile[old_jx][old_jy];
5741   int element = Tile[jx][jy];
5742   boolean player_relocated = (old_jx != jx || old_jy != jy);
5743
5744   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5745   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5746   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5747   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5748   int leave_side_horiz = move_dir_horiz;
5749   int leave_side_vert  = move_dir_vert;
5750   int enter_side = enter_side_horiz | enter_side_vert;
5751   int leave_side = leave_side_horiz | leave_side_vert;
5752
5753   if (player->buried)           // do not reanimate dead player
5754     return;
5755
5756   if (!player_relocated)        // no need to relocate the player
5757     return;
5758
5759   if (IS_PLAYER(jx, jy))        // player already placed at new position
5760   {
5761     RemoveField(jx, jy);        // temporarily remove newly placed player
5762     DrawLevelField(jx, jy);
5763   }
5764
5765   if (player->present)
5766   {
5767     while (player->MovPos)
5768     {
5769       ScrollPlayer(player, SCROLL_GO_ON);
5770       ScrollScreen(NULL, SCROLL_GO_ON);
5771
5772       AdvanceFrameAndPlayerCounters(player->index_nr);
5773
5774       DrawPlayer(player);
5775
5776       BackToFront_WithFrameDelay(wait_delay_value);
5777     }
5778
5779     DrawPlayer(player);         // needed here only to cleanup last field
5780     DrawLevelField(player->jx, player->jy);     // remove player graphic
5781
5782     player->is_moving = FALSE;
5783   }
5784
5785   if (IS_CUSTOM_ELEMENT(old_element))
5786     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5787                                CE_LEFT_BY_PLAYER,
5788                                player->index_bit, leave_side);
5789
5790   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5791                                       CE_PLAYER_LEAVES_X,
5792                                       player->index_bit, leave_side);
5793
5794   Tile[jx][jy] = el_player;
5795   InitPlayerField(jx, jy, el_player, TRUE);
5796
5797   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5798      possible that the relocation target field did not contain a player element,
5799      but a walkable element, to which the new player was relocated -- in this
5800      case, restore that (already initialized!) element on the player field */
5801   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5802   {
5803     Tile[jx][jy] = element;     // restore previously existing element
5804   }
5805
5806   // only visually relocate centered player
5807   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5808                      FALSE, level.instant_relocation);
5809
5810   TestIfPlayerTouchesBadThing(jx, jy);
5811   TestIfPlayerTouchesCustomElement(jx, jy);
5812
5813   if (IS_CUSTOM_ELEMENT(element))
5814     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5815                                player->index_bit, enter_side);
5816
5817   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5818                                       player->index_bit, enter_side);
5819
5820   if (player->is_switching)
5821   {
5822     /* ensure that relocation while still switching an element does not cause
5823        a new element to be treated as also switched directly after relocation
5824        (this is important for teleporter switches that teleport the player to
5825        a place where another teleporter switch is in the same direction, which
5826        would then incorrectly be treated as immediately switched before the
5827        direction key that caused the switch was released) */
5828
5829     player->switch_x += jx - old_jx;
5830     player->switch_y += jy - old_jy;
5831   }
5832 }
5833
5834 static void Explode(int ex, int ey, int phase, int mode)
5835 {
5836   int x, y;
5837   int last_phase;
5838   int border_element;
5839
5840   if (game.explosions_delayed)
5841   {
5842     ExplodeField[ex][ey] = mode;
5843     return;
5844   }
5845
5846   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5847   {
5848     int center_element = Tile[ex][ey];
5849     int artwork_element, explosion_element;     // set these values later
5850
5851     // remove things displayed in background while burning dynamite
5852     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5853       Back[ex][ey] = 0;
5854
5855     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5856     {
5857       // put moving element to center field (and let it explode there)
5858       center_element = MovingOrBlocked2Element(ex, ey);
5859       RemoveMovingField(ex, ey);
5860       Tile[ex][ey] = center_element;
5861     }
5862
5863     // now "center_element" is finally determined -- set related values now
5864     artwork_element = center_element;           // for custom player artwork
5865     explosion_element = center_element;         // for custom player artwork
5866
5867     if (IS_PLAYER(ex, ey))
5868     {
5869       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5870
5871       artwork_element = stored_player[player_nr].artwork_element;
5872
5873       if (level.use_explosion_element[player_nr])
5874       {
5875         explosion_element = level.explosion_element[player_nr];
5876         artwork_element = explosion_element;
5877       }
5878     }
5879
5880     if (mode == EX_TYPE_NORMAL ||
5881         mode == EX_TYPE_CENTER ||
5882         mode == EX_TYPE_CROSS)
5883       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5884
5885     last_phase = element_info[explosion_element].explosion_delay + 1;
5886
5887     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5888     {
5889       int xx = x - ex + 1;
5890       int yy = y - ey + 1;
5891       int element;
5892
5893       if (!IN_LEV_FIELD(x, y) ||
5894           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5895           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5896         continue;
5897
5898       element = Tile[x][y];
5899
5900       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5901       {
5902         element = MovingOrBlocked2Element(x, y);
5903
5904         if (!IS_EXPLOSION_PROOF(element))
5905           RemoveMovingField(x, y);
5906       }
5907
5908       // indestructible elements can only explode in center (but not flames)
5909       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5910                                            mode == EX_TYPE_BORDER)) ||
5911           element == EL_FLAMES)
5912         continue;
5913
5914       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5915          behaviour, for example when touching a yamyam that explodes to rocks
5916          with active deadly shield, a rock is created under the player !!! */
5917       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5918 #if 0
5919       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5920           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5921            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5922 #else
5923       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5924 #endif
5925       {
5926         if (IS_ACTIVE_BOMB(element))
5927         {
5928           // re-activate things under the bomb like gate or penguin
5929           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5930           Back[x][y] = 0;
5931         }
5932
5933         continue;
5934       }
5935
5936       // save walkable background elements while explosion on same tile
5937       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5938           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5939         Back[x][y] = element;
5940
5941       // ignite explodable elements reached by other explosion
5942       if (element == EL_EXPLOSION)
5943         element = Store2[x][y];
5944
5945       if (AmoebaNr[x][y] &&
5946           (element == EL_AMOEBA_FULL ||
5947            element == EL_BD_AMOEBA ||
5948            element == EL_AMOEBA_GROWING))
5949       {
5950         AmoebaCnt[AmoebaNr[x][y]]--;
5951         AmoebaCnt2[AmoebaNr[x][y]]--;
5952       }
5953
5954       RemoveField(x, y);
5955
5956       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5957       {
5958         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5959
5960         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5961
5962         if (PLAYERINFO(ex, ey)->use_murphy)
5963           Store[x][y] = EL_EMPTY;
5964       }
5965
5966       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5967       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5968       else if (IS_PLAYER_ELEMENT(center_element))
5969         Store[x][y] = EL_EMPTY;
5970       else if (center_element == EL_YAMYAM)
5971         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5972       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5973         Store[x][y] = element_info[center_element].content.e[xx][yy];
5974 #if 1
5975       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5976       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5977       // otherwise) -- FIX THIS !!!
5978       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5979         Store[x][y] = element_info[element].content.e[1][1];
5980 #else
5981       else if (!CAN_EXPLODE(element))
5982         Store[x][y] = element_info[element].content.e[1][1];
5983 #endif
5984       else
5985         Store[x][y] = EL_EMPTY;
5986
5987       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5988           center_element == EL_AMOEBA_TO_DIAMOND)
5989         Store2[x][y] = element;
5990
5991       Tile[x][y] = EL_EXPLOSION;
5992       GfxElement[x][y] = artwork_element;
5993
5994       ExplodePhase[x][y] = 1;
5995       ExplodeDelay[x][y] = last_phase;
5996
5997       Stop[x][y] = TRUE;
5998     }
5999
6000     if (center_element == EL_YAMYAM)
6001       game.yamyam_content_nr =
6002         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6003
6004     return;
6005   }
6006
6007   if (Stop[ex][ey])
6008     return;
6009
6010   x = ex;
6011   y = ey;
6012
6013   if (phase == 1)
6014     GfxFrame[x][y] = 0;         // restart explosion animation
6015
6016   last_phase = ExplodeDelay[x][y];
6017
6018   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6019
6020   // this can happen if the player leaves an explosion just in time
6021   if (GfxElement[x][y] == EL_UNDEFINED)
6022     GfxElement[x][y] = EL_EMPTY;
6023
6024   border_element = Store2[x][y];
6025   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6026     border_element = StorePlayer[x][y];
6027
6028   if (phase == element_info[border_element].ignition_delay ||
6029       phase == last_phase)
6030   {
6031     boolean border_explosion = FALSE;
6032
6033     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6034         !PLAYER_EXPLOSION_PROTECTED(x, y))
6035     {
6036       KillPlayerUnlessExplosionProtected(x, y);
6037       border_explosion = TRUE;
6038     }
6039     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6040     {
6041       Tile[x][y] = Store2[x][y];
6042       Store2[x][y] = 0;
6043       Bang(x, y);
6044       border_explosion = TRUE;
6045     }
6046     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6047     {
6048       AmoebaToDiamond(x, y);
6049       Store2[x][y] = 0;
6050       border_explosion = TRUE;
6051     }
6052
6053     // if an element just explodes due to another explosion (chain-reaction),
6054     // do not immediately end the new explosion when it was the last frame of
6055     // the explosion (as it would be done in the following "if"-statement!)
6056     if (border_explosion && phase == last_phase)
6057       return;
6058   }
6059
6060   // this can happen if the player was just killed by an explosion
6061   if (GfxElement[x][y] == EL_UNDEFINED)
6062     GfxElement[x][y] = EL_EMPTY;
6063
6064   if (phase == last_phase)
6065   {
6066     int element;
6067
6068     element = Tile[x][y] = Store[x][y];
6069     Store[x][y] = Store2[x][y] = 0;
6070     GfxElement[x][y] = EL_UNDEFINED;
6071
6072     // player can escape from explosions and might therefore be still alive
6073     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6074         element <= EL_PLAYER_IS_EXPLODING_4)
6075     {
6076       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6077       int explosion_element = EL_PLAYER_1 + player_nr;
6078       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6079       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6080
6081       if (level.use_explosion_element[player_nr])
6082         explosion_element = level.explosion_element[player_nr];
6083
6084       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6085                     element_info[explosion_element].content.e[xx][yy]);
6086     }
6087
6088     // restore probably existing indestructible background element
6089     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6090       element = Tile[x][y] = Back[x][y];
6091     Back[x][y] = 0;
6092
6093     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6094     GfxDir[x][y] = MV_NONE;
6095     ChangeDelay[x][y] = 0;
6096     ChangePage[x][y] = -1;
6097
6098     CustomValue[x][y] = 0;
6099
6100     InitField_WithBug2(x, y, FALSE);
6101
6102     TEST_DrawLevelField(x, y);
6103
6104     TestIfElementTouchesCustomElement(x, y);
6105
6106     if (GFX_CRUMBLED(element))
6107       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108
6109     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6110       StorePlayer[x][y] = 0;
6111
6112     if (IS_PLAYER_ELEMENT(element))
6113       RelocatePlayer(x, y, element);
6114   }
6115   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6116   {
6117     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6118     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6119
6120     if (phase == 1)
6121       TEST_DrawLevelFieldCrumbled(x, y);
6122
6123     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6124     {
6125       DrawLevelElement(x, y, Back[x][y]);
6126       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6127     }
6128     else if (IS_WALKABLE_UNDER(Back[x][y]))
6129     {
6130       DrawLevelGraphic(x, y, graphic, frame);
6131       DrawLevelElementThruMask(x, y, Back[x][y]);
6132     }
6133     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6134       DrawLevelGraphic(x, y, graphic, frame);
6135   }
6136 }
6137
6138 static void DynaExplode(int ex, int ey)
6139 {
6140   int i, j;
6141   int dynabomb_element = Tile[ex][ey];
6142   int dynabomb_size = 1;
6143   boolean dynabomb_xl = FALSE;
6144   struct PlayerInfo *player;
6145   struct XY *xy = xy_topdown;
6146
6147   if (IS_ACTIVE_BOMB(dynabomb_element))
6148   {
6149     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6150     dynabomb_size = player->dynabomb_size;
6151     dynabomb_xl = player->dynabomb_xl;
6152     player->dynabombs_left++;
6153   }
6154
6155   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6156
6157   for (i = 0; i < NUM_DIRECTIONS; i++)
6158   {
6159     for (j = 1; j <= dynabomb_size; j++)
6160     {
6161       int x = ex + j * xy[i].x;
6162       int y = ey + j * xy[i].y;
6163       int element;
6164
6165       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6166         break;
6167
6168       element = Tile[x][y];
6169
6170       // do not restart explosions of fields with active bombs
6171       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6172         continue;
6173
6174       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6175
6176       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6177           !IS_DIGGABLE(element) && !dynabomb_xl)
6178         break;
6179     }
6180   }
6181 }
6182
6183 void Bang(int x, int y)
6184 {
6185   int element = MovingOrBlocked2Element(x, y);
6186   int explosion_type = EX_TYPE_NORMAL;
6187
6188   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6189   {
6190     struct PlayerInfo *player = PLAYERINFO(x, y);
6191
6192     element = Tile[x][y] = player->initial_element;
6193
6194     if (level.use_explosion_element[player->index_nr])
6195     {
6196       int explosion_element = level.explosion_element[player->index_nr];
6197
6198       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6199         explosion_type = EX_TYPE_CROSS;
6200       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6201         explosion_type = EX_TYPE_CENTER;
6202     }
6203   }
6204
6205   switch (element)
6206   {
6207     case EL_BUG:
6208     case EL_SPACESHIP:
6209     case EL_BD_BUTTERFLY:
6210     case EL_BD_FIREFLY:
6211     case EL_YAMYAM:
6212     case EL_DARK_YAMYAM:
6213     case EL_ROBOT:
6214     case EL_PACMAN:
6215     case EL_MOLE:
6216       RaiseScoreElement(element);
6217       break;
6218
6219     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6220     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6221     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6222     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6223     case EL_DYNABOMB_INCREASE_NUMBER:
6224     case EL_DYNABOMB_INCREASE_SIZE:
6225     case EL_DYNABOMB_INCREASE_POWER:
6226       explosion_type = EX_TYPE_DYNA;
6227       break;
6228
6229     case EL_DC_LANDMINE:
6230       explosion_type = EX_TYPE_CENTER;
6231       break;
6232
6233     case EL_PENGUIN:
6234     case EL_LAMP:
6235     case EL_LAMP_ACTIVE:
6236     case EL_AMOEBA_TO_DIAMOND:
6237       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6238         explosion_type = EX_TYPE_CENTER;
6239       break;
6240
6241     default:
6242       if (element_info[element].explosion_type == EXPLODES_CROSS)
6243         explosion_type = EX_TYPE_CROSS;
6244       else if (element_info[element].explosion_type == EXPLODES_1X1)
6245         explosion_type = EX_TYPE_CENTER;
6246       break;
6247   }
6248
6249   if (explosion_type == EX_TYPE_DYNA)
6250     DynaExplode(x, y);
6251   else
6252     Explode(x, y, EX_PHASE_START, explosion_type);
6253
6254   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6255 }
6256
6257 static void SplashAcid(int x, int y)
6258 {
6259   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6260       (!IN_LEV_FIELD(x - 1, y - 2) ||
6261        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6262     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6263
6264   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6265       (!IN_LEV_FIELD(x + 1, y - 2) ||
6266        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6267     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6268
6269   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6270 }
6271
6272 static void InitBeltMovement(void)
6273 {
6274   static int belt_base_element[4] =
6275   {
6276     EL_CONVEYOR_BELT_1_LEFT,
6277     EL_CONVEYOR_BELT_2_LEFT,
6278     EL_CONVEYOR_BELT_3_LEFT,
6279     EL_CONVEYOR_BELT_4_LEFT
6280   };
6281   static int belt_base_active_element[4] =
6282   {
6283     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6284     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6285     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6286     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6287   };
6288
6289   int x, y, i, j;
6290
6291   // set frame order for belt animation graphic according to belt direction
6292   for (i = 0; i < NUM_BELTS; i++)
6293   {
6294     int belt_nr = i;
6295
6296     for (j = 0; j < NUM_BELT_PARTS; j++)
6297     {
6298       int element = belt_base_active_element[belt_nr] + j;
6299       int graphic_1 = el2img(element);
6300       int graphic_2 = el2panelimg(element);
6301
6302       if (game.belt_dir[i] == MV_LEFT)
6303       {
6304         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6305         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6306       }
6307       else
6308       {
6309         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6310         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6311       }
6312     }
6313   }
6314
6315   SCAN_PLAYFIELD(x, y)
6316   {
6317     int element = Tile[x][y];
6318
6319     for (i = 0; i < NUM_BELTS; i++)
6320     {
6321       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6322       {
6323         int e_belt_nr = getBeltNrFromBeltElement(element);
6324         int belt_nr = i;
6325
6326         if (e_belt_nr == belt_nr)
6327         {
6328           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6329
6330           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6331         }
6332       }
6333     }
6334   }
6335 }
6336
6337 static void ToggleBeltSwitch(int x, int y)
6338 {
6339   static int belt_base_element[4] =
6340   {
6341     EL_CONVEYOR_BELT_1_LEFT,
6342     EL_CONVEYOR_BELT_2_LEFT,
6343     EL_CONVEYOR_BELT_3_LEFT,
6344     EL_CONVEYOR_BELT_4_LEFT
6345   };
6346   static int belt_base_active_element[4] =
6347   {
6348     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6349     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6350     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6351     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6352   };
6353   static int belt_base_switch_element[4] =
6354   {
6355     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6356     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6357     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6358     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6359   };
6360   static int belt_move_dir[4] =
6361   {
6362     MV_LEFT,
6363     MV_NONE,
6364     MV_RIGHT,
6365     MV_NONE,
6366   };
6367
6368   int element = Tile[x][y];
6369   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6370   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6371   int belt_dir = belt_move_dir[belt_dir_nr];
6372   int xx, yy, i;
6373
6374   if (!IS_BELT_SWITCH(element))
6375     return;
6376
6377   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6378   game.belt_dir[belt_nr] = belt_dir;
6379
6380   if (belt_dir_nr == 3)
6381     belt_dir_nr = 1;
6382
6383   // set frame order for belt animation graphic according to belt direction
6384   for (i = 0; i < NUM_BELT_PARTS; i++)
6385   {
6386     int element = belt_base_active_element[belt_nr] + i;
6387     int graphic_1 = el2img(element);
6388     int graphic_2 = el2panelimg(element);
6389
6390     if (belt_dir == MV_LEFT)
6391     {
6392       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6393       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6394     }
6395     else
6396     {
6397       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6398       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6399     }
6400   }
6401
6402   SCAN_PLAYFIELD(xx, yy)
6403   {
6404     int element = Tile[xx][yy];
6405
6406     if (IS_BELT_SWITCH(element))
6407     {
6408       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6409
6410       if (e_belt_nr == belt_nr)
6411       {
6412         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6413         TEST_DrawLevelField(xx, yy);
6414       }
6415     }
6416     else if (IS_BELT(element) && belt_dir != MV_NONE)
6417     {
6418       int e_belt_nr = getBeltNrFromBeltElement(element);
6419
6420       if (e_belt_nr == belt_nr)
6421       {
6422         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6423
6424         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6425         TEST_DrawLevelField(xx, yy);
6426       }
6427     }
6428     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6429     {
6430       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6431
6432       if (e_belt_nr == belt_nr)
6433       {
6434         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6435
6436         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6437         TEST_DrawLevelField(xx, yy);
6438       }
6439     }
6440   }
6441 }
6442
6443 static void ToggleSwitchgateSwitch(void)
6444 {
6445   int xx, yy;
6446
6447   game.switchgate_pos = !game.switchgate_pos;
6448
6449   SCAN_PLAYFIELD(xx, yy)
6450   {
6451     int element = Tile[xx][yy];
6452
6453     if (element == EL_SWITCHGATE_SWITCH_UP)
6454     {
6455       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6456       TEST_DrawLevelField(xx, yy);
6457     }
6458     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6459     {
6460       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6461       TEST_DrawLevelField(xx, yy);
6462     }
6463     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6464     {
6465       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6466       TEST_DrawLevelField(xx, yy);
6467     }
6468     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6469     {
6470       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6471       TEST_DrawLevelField(xx, yy);
6472     }
6473     else if (element == EL_SWITCHGATE_OPEN ||
6474              element == EL_SWITCHGATE_OPENING)
6475     {
6476       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6477
6478       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6479     }
6480     else if (element == EL_SWITCHGATE_CLOSED ||
6481              element == EL_SWITCHGATE_CLOSING)
6482     {
6483       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6484
6485       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6486     }
6487   }
6488 }
6489
6490 static int getInvisibleActiveFromInvisibleElement(int element)
6491 {
6492   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6493           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6494           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6495           element);
6496 }
6497
6498 static int getInvisibleFromInvisibleActiveElement(int element)
6499 {
6500   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6501           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6502           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6503           element);
6504 }
6505
6506 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6507 {
6508   int x, y;
6509
6510   SCAN_PLAYFIELD(x, y)
6511   {
6512     int element = Tile[x][y];
6513
6514     if (element == EL_LIGHT_SWITCH &&
6515         game.light_time_left > 0)
6516     {
6517       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6518       TEST_DrawLevelField(x, y);
6519     }
6520     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6521              game.light_time_left == 0)
6522     {
6523       Tile[x][y] = EL_LIGHT_SWITCH;
6524       TEST_DrawLevelField(x, y);
6525     }
6526     else if (element == EL_EMC_DRIPPER &&
6527              game.light_time_left > 0)
6528     {
6529       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533              game.light_time_left == 0)
6534     {
6535       Tile[x][y] = EL_EMC_DRIPPER;
6536       TEST_DrawLevelField(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL ||
6539              element == EL_INVISIBLE_WALL ||
6540              element == EL_INVISIBLE_SAND)
6541     {
6542       if (game.light_time_left > 0)
6543         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // uncrumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552              element == EL_INVISIBLE_WALL_ACTIVE ||
6553              element == EL_INVISIBLE_SAND_ACTIVE)
6554     {
6555       if (game.light_time_left == 0)
6556         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6557
6558       TEST_DrawLevelField(x, y);
6559
6560       // re-crumble neighbour fields, if needed
6561       if (element == EL_INVISIBLE_SAND)
6562         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6563     }
6564   }
6565 }
6566
6567 static void RedrawAllInvisibleElementsForLenses(void)
6568 {
6569   int x, y;
6570
6571   SCAN_PLAYFIELD(x, y)
6572   {
6573     int element = Tile[x][y];
6574
6575     if (element == EL_EMC_DRIPPER &&
6576         game.lenses_time_left > 0)
6577     {
6578       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582              game.lenses_time_left == 0)
6583     {
6584       Tile[x][y] = EL_EMC_DRIPPER;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL ||
6588              element == EL_INVISIBLE_WALL ||
6589              element == EL_INVISIBLE_SAND)
6590     {
6591       if (game.lenses_time_left > 0)
6592         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       // uncrumble neighbour fields, if needed
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6599     }
6600     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601              element == EL_INVISIBLE_WALL_ACTIVE ||
6602              element == EL_INVISIBLE_SAND_ACTIVE)
6603     {
6604       if (game.lenses_time_left == 0)
6605         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6606
6607       TEST_DrawLevelField(x, y);
6608
6609       // re-crumble neighbour fields, if needed
6610       if (element == EL_INVISIBLE_SAND)
6611         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6612     }
6613   }
6614 }
6615
6616 static void RedrawAllInvisibleElementsForMagnifier(void)
6617 {
6618   int x, y;
6619
6620   SCAN_PLAYFIELD(x, y)
6621   {
6622     int element = Tile[x][y];
6623
6624     if (element == EL_EMC_FAKE_GRASS &&
6625         game.magnify_time_left > 0)
6626     {
6627       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6631              game.magnify_time_left == 0)
6632     {
6633       Tile[x][y] = EL_EMC_FAKE_GRASS;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (IS_GATE_GRAY(element) &&
6637              game.magnify_time_left > 0)
6638     {
6639       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6640                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6641                     IS_EM_GATE_GRAY(element) ?
6642                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6643                     IS_EMC_GATE_GRAY(element) ?
6644                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6645                     IS_DC_GATE_GRAY(element) ?
6646                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6647                     element);
6648       TEST_DrawLevelField(x, y);
6649     }
6650     else if (IS_GATE_GRAY_ACTIVE(element) &&
6651              game.magnify_time_left == 0)
6652     {
6653       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6654                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6655                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6656                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6657                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6658                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6659                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6660                     EL_DC_GATE_WHITE_GRAY :
6661                     element);
6662       TEST_DrawLevelField(x, y);
6663     }
6664   }
6665 }
6666
6667 static void ToggleLightSwitch(int x, int y)
6668 {
6669   int element = Tile[x][y];
6670
6671   game.light_time_left =
6672     (element == EL_LIGHT_SWITCH ?
6673      level.time_light * FRAMES_PER_SECOND : 0);
6674
6675   RedrawAllLightSwitchesAndInvisibleElements();
6676 }
6677
6678 static void ActivateTimegateSwitch(int x, int y)
6679 {
6680   int xx, yy;
6681
6682   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6683
6684   SCAN_PLAYFIELD(xx, yy)
6685   {
6686     int element = Tile[xx][yy];
6687
6688     if (element == EL_TIMEGATE_CLOSED ||
6689         element == EL_TIMEGATE_CLOSING)
6690     {
6691       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6692       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6693     }
6694
6695     /*
6696     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6697     {
6698       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6699       TEST_DrawLevelField(xx, yy);
6700     }
6701     */
6702
6703   }
6704
6705   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6706                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6707 }
6708
6709 static void Impact(int x, int y)
6710 {
6711   boolean last_line = (y == lev_fieldy - 1);
6712   boolean object_hit = FALSE;
6713   boolean impact = (last_line || object_hit);
6714   int element = Tile[x][y];
6715   int smashed = EL_STEELWALL;
6716
6717   if (!last_line)       // check if element below was hit
6718   {
6719     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6720       return;
6721
6722     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6723                                          MovDir[x][y + 1] != MV_DOWN ||
6724                                          MovPos[x][y + 1] <= TILEY / 2));
6725
6726     // do not smash moving elements that left the smashed field in time
6727     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6728         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6729       object_hit = FALSE;
6730
6731 #if USE_QUICKSAND_IMPACT_BUGFIX
6732     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6733     {
6734       RemoveMovingField(x, y + 1);
6735       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6736       Tile[x][y + 2] = EL_ROCK;
6737       TEST_DrawLevelField(x, y + 2);
6738
6739       object_hit = TRUE;
6740     }
6741
6742     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6743     {
6744       RemoveMovingField(x, y + 1);
6745       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6746       Tile[x][y + 2] = EL_ROCK;
6747       TEST_DrawLevelField(x, y + 2);
6748
6749       object_hit = TRUE;
6750     }
6751 #endif
6752
6753     if (object_hit)
6754       smashed = MovingOrBlocked2Element(x, y + 1);
6755
6756     impact = (last_line || object_hit);
6757   }
6758
6759   if (!last_line && smashed == EL_ACID) // element falls into acid
6760   {
6761     SplashAcid(x, y + 1);
6762     return;
6763   }
6764
6765   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6766   // only reset graphic animation if graphic really changes after impact
6767   if (impact &&
6768       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6769   {
6770     ResetGfxAnimation(x, y);
6771     TEST_DrawLevelField(x, y);
6772   }
6773
6774   if (impact && CAN_EXPLODE_IMPACT(element))
6775   {
6776     Bang(x, y);
6777     return;
6778   }
6779   else if (impact && element == EL_PEARL &&
6780            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6781   {
6782     ResetGfxAnimation(x, y);
6783
6784     Tile[x][y] = EL_PEARL_BREAKING;
6785     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6786     return;
6787   }
6788   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6789   {
6790     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6791
6792     return;
6793   }
6794
6795   if (impact && element == EL_AMOEBA_DROP)
6796   {
6797     if (object_hit && IS_PLAYER(x, y + 1))
6798       KillPlayerUnlessEnemyProtected(x, y + 1);
6799     else if (object_hit && smashed == EL_PENGUIN)
6800       Bang(x, y + 1);
6801     else
6802     {
6803       Tile[x][y] = EL_AMOEBA_GROWING;
6804       Store[x][y] = EL_AMOEBA_WET;
6805
6806       ResetRandomAnimationValue(x, y);
6807     }
6808     return;
6809   }
6810
6811   if (object_hit)               // check which object was hit
6812   {
6813     if ((CAN_PASS_MAGIC_WALL(element) && 
6814          (smashed == EL_MAGIC_WALL ||
6815           smashed == EL_BD_MAGIC_WALL)) ||
6816         (CAN_PASS_DC_MAGIC_WALL(element) &&
6817          smashed == EL_DC_MAGIC_WALL))
6818     {
6819       int xx, yy;
6820       int activated_magic_wall =
6821         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6822          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6823          EL_DC_MAGIC_WALL_ACTIVE);
6824
6825       // activate magic wall / mill
6826       SCAN_PLAYFIELD(xx, yy)
6827       {
6828         if (Tile[xx][yy] == smashed)
6829           Tile[xx][yy] = activated_magic_wall;
6830       }
6831
6832       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6833       game.magic_wall_active = TRUE;
6834
6835       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6836                             SND_MAGIC_WALL_ACTIVATING :
6837                             smashed == EL_BD_MAGIC_WALL ?
6838                             SND_BD_MAGIC_WALL_ACTIVATING :
6839                             SND_DC_MAGIC_WALL_ACTIVATING));
6840     }
6841
6842     if (IS_PLAYER(x, y + 1))
6843     {
6844       if (CAN_SMASH_PLAYER(element))
6845       {
6846         KillPlayerUnlessEnemyProtected(x, y + 1);
6847         return;
6848       }
6849     }
6850     else if (smashed == EL_PENGUIN)
6851     {
6852       if (CAN_SMASH_PLAYER(element))
6853       {
6854         Bang(x, y + 1);
6855         return;
6856       }
6857     }
6858     else if (element == EL_BD_DIAMOND)
6859     {
6860       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6861       {
6862         Bang(x, y + 1);
6863         return;
6864       }
6865     }
6866     else if (((element == EL_SP_INFOTRON ||
6867                element == EL_SP_ZONK) &&
6868               (smashed == EL_SP_SNIKSNAK ||
6869                smashed == EL_SP_ELECTRON ||
6870                smashed == EL_SP_DISK_ORANGE)) ||
6871              (element == EL_SP_INFOTRON &&
6872               smashed == EL_SP_DISK_YELLOW))
6873     {
6874       Bang(x, y + 1);
6875       return;
6876     }
6877     else if (CAN_SMASH_EVERYTHING(element))
6878     {
6879       if (IS_CLASSIC_ENEMY(smashed) ||
6880           CAN_EXPLODE_SMASHED(smashed))
6881       {
6882         Bang(x, y + 1);
6883         return;
6884       }
6885       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6886       {
6887         if (smashed == EL_LAMP ||
6888             smashed == EL_LAMP_ACTIVE)
6889         {
6890           Bang(x, y + 1);
6891           return;
6892         }
6893         else if (smashed == EL_NUT)
6894         {
6895           Tile[x][y + 1] = EL_NUT_BREAKING;
6896           PlayLevelSound(x, y, SND_NUT_BREAKING);
6897           RaiseScoreElement(EL_NUT);
6898           return;
6899         }
6900         else if (smashed == EL_PEARL)
6901         {
6902           ResetGfxAnimation(x, y);
6903
6904           Tile[x][y + 1] = EL_PEARL_BREAKING;
6905           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6906           return;
6907         }
6908         else if (smashed == EL_DIAMOND)
6909         {
6910           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6911           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6912           return;
6913         }
6914         else if (IS_BELT_SWITCH(smashed))
6915         {
6916           ToggleBeltSwitch(x, y + 1);
6917         }
6918         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6919                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6920                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6921                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6922         {
6923           ToggleSwitchgateSwitch();
6924         }
6925         else if (smashed == EL_LIGHT_SWITCH ||
6926                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6927         {
6928           ToggleLightSwitch(x, y + 1);
6929         }
6930         else
6931         {
6932           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6933
6934           CheckElementChangeBySide(x, y + 1, smashed, element,
6935                                    CE_SWITCHED, CH_SIDE_TOP);
6936           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6937                                             CH_SIDE_TOP);
6938         }
6939       }
6940       else
6941       {
6942         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6943       }
6944     }
6945   }
6946
6947   // play sound of magic wall / mill
6948   if (!last_line &&
6949       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6950        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6951        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6952   {
6953     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6954       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6955     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6956       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6957     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6958       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6959
6960     return;
6961   }
6962
6963   // play sound of object that hits the ground
6964   if (last_line || object_hit)
6965     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6966 }
6967
6968 static void TurnRoundExt(int x, int y)
6969 {
6970   static struct
6971   {
6972     int dx, dy;
6973   } move_xy[] =
6974   {
6975     {  0,  0 },
6976     { -1,  0 },
6977     { +1,  0 },
6978     {  0,  0 },
6979     {  0, -1 },
6980     {  0,  0 }, { 0, 0 }, { 0, 0 },
6981     {  0, +1 }
6982   };
6983   static struct
6984   {
6985     int left, right, back;
6986   } turn[] =
6987   {
6988     { 0,        0,              0        },
6989     { MV_DOWN,  MV_UP,          MV_RIGHT },
6990     { MV_UP,    MV_DOWN,        MV_LEFT  },
6991     { 0,        0,              0        },
6992     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6993     { 0,        0,              0        },
6994     { 0,        0,              0        },
6995     { 0,        0,              0        },
6996     { MV_RIGHT, MV_LEFT,        MV_UP    }
6997   };
6998
6999   int element = Tile[x][y];
7000   int move_pattern = element_info[element].move_pattern;
7001
7002   int old_move_dir = MovDir[x][y];
7003   int left_dir  = turn[old_move_dir].left;
7004   int right_dir = turn[old_move_dir].right;
7005   int back_dir  = turn[old_move_dir].back;
7006
7007   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7008   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7009   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7010   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7011
7012   int left_x  = x + left_dx,  left_y  = y + left_dy;
7013   int right_x = x + right_dx, right_y = y + right_dy;
7014   int move_x  = x + move_dx,  move_y  = y + move_dy;
7015
7016   int xx, yy;
7017
7018   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7019   {
7020     TestIfBadThingTouchesOtherBadThing(x, y);
7021
7022     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7023       MovDir[x][y] = right_dir;
7024     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7025       MovDir[x][y] = left_dir;
7026
7027     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7028       MovDelay[x][y] = 9;
7029     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7030       MovDelay[x][y] = 1;
7031   }
7032   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7033   {
7034     TestIfBadThingTouchesOtherBadThing(x, y);
7035
7036     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7037       MovDir[x][y] = left_dir;
7038     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7039       MovDir[x][y] = right_dir;
7040
7041     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7042       MovDelay[x][y] = 9;
7043     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7044       MovDelay[x][y] = 1;
7045   }
7046   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7047   {
7048     TestIfBadThingTouchesOtherBadThing(x, y);
7049
7050     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7051       MovDir[x][y] = left_dir;
7052     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7053       MovDir[x][y] = right_dir;
7054
7055     if (MovDir[x][y] != old_move_dir)
7056       MovDelay[x][y] = 9;
7057   }
7058   else if (element == EL_YAMYAM)
7059   {
7060     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7061     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7062
7063     if (can_turn_left && can_turn_right)
7064       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7065     else if (can_turn_left)
7066       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7067     else if (can_turn_right)
7068       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7069     else
7070       MovDir[x][y] = back_dir;
7071
7072     MovDelay[x][y] = 16 + 16 * RND(3);
7073   }
7074   else if (element == EL_DARK_YAMYAM)
7075   {
7076     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7077                                                          left_x, left_y);
7078     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7079                                                          right_x, right_y);
7080
7081     if (can_turn_left && can_turn_right)
7082       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7083     else if (can_turn_left)
7084       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7085     else if (can_turn_right)
7086       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7087     else
7088       MovDir[x][y] = back_dir;
7089
7090     MovDelay[x][y] = 16 + 16 * RND(3);
7091   }
7092   else if (element == EL_PACMAN)
7093   {
7094     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7095     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7096
7097     if (can_turn_left && can_turn_right)
7098       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7099     else if (can_turn_left)
7100       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7101     else if (can_turn_right)
7102       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7103     else
7104       MovDir[x][y] = back_dir;
7105
7106     MovDelay[x][y] = 6 + RND(40);
7107   }
7108   else if (element == EL_PIG)
7109   {
7110     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7111     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7112     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7113     boolean should_turn_left, should_turn_right, should_move_on;
7114     int rnd_value = 24;
7115     int rnd = RND(rnd_value);
7116
7117     should_turn_left = (can_turn_left &&
7118                         (!can_move_on ||
7119                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7120                                                    y + back_dy + left_dy)));
7121     should_turn_right = (can_turn_right &&
7122                          (!can_move_on ||
7123                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7124                                                     y + back_dy + right_dy)));
7125     should_move_on = (can_move_on &&
7126                       (!can_turn_left ||
7127                        !can_turn_right ||
7128                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7129                                                  y + move_dy + left_dy) ||
7130                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7131                                                  y + move_dy + right_dy)));
7132
7133     if (should_turn_left || should_turn_right || should_move_on)
7134     {
7135       if (should_turn_left && should_turn_right && should_move_on)
7136         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7137                         rnd < 2 * rnd_value / 3 ? right_dir :
7138                         old_move_dir);
7139       else if (should_turn_left && should_turn_right)
7140         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7141       else if (should_turn_left && should_move_on)
7142         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7143       else if (should_turn_right && should_move_on)
7144         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7145       else if (should_turn_left)
7146         MovDir[x][y] = left_dir;
7147       else if (should_turn_right)
7148         MovDir[x][y] = right_dir;
7149       else if (should_move_on)
7150         MovDir[x][y] = old_move_dir;
7151     }
7152     else if (can_move_on && rnd > rnd_value / 8)
7153       MovDir[x][y] = old_move_dir;
7154     else if (can_turn_left && can_turn_right)
7155       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7156     else if (can_turn_left && rnd > rnd_value / 8)
7157       MovDir[x][y] = left_dir;
7158     else if (can_turn_right && rnd > rnd_value/8)
7159       MovDir[x][y] = right_dir;
7160     else
7161       MovDir[x][y] = back_dir;
7162
7163     xx = x + move_xy[MovDir[x][y]].dx;
7164     yy = y + move_xy[MovDir[x][y]].dy;
7165
7166     if (!IN_LEV_FIELD(xx, yy) ||
7167         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7168       MovDir[x][y] = old_move_dir;
7169
7170     MovDelay[x][y] = 0;
7171   }
7172   else if (element == EL_DRAGON)
7173   {
7174     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7175     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7176     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7177     int rnd_value = 24;
7178     int rnd = RND(rnd_value);
7179
7180     if (can_move_on && rnd > rnd_value / 8)
7181       MovDir[x][y] = old_move_dir;
7182     else if (can_turn_left && can_turn_right)
7183       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7184     else if (can_turn_left && rnd > rnd_value / 8)
7185       MovDir[x][y] = left_dir;
7186     else if (can_turn_right && rnd > rnd_value / 8)
7187       MovDir[x][y] = right_dir;
7188     else
7189       MovDir[x][y] = back_dir;
7190
7191     xx = x + move_xy[MovDir[x][y]].dx;
7192     yy = y + move_xy[MovDir[x][y]].dy;
7193
7194     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7195       MovDir[x][y] = old_move_dir;
7196
7197     MovDelay[x][y] = 0;
7198   }
7199   else if (element == EL_MOLE)
7200   {
7201     boolean can_move_on =
7202       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7203                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7204                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7205     if (!can_move_on)
7206     {
7207       boolean can_turn_left =
7208         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7209                               IS_AMOEBOID(Tile[left_x][left_y])));
7210
7211       boolean can_turn_right =
7212         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7213                               IS_AMOEBOID(Tile[right_x][right_y])));
7214
7215       if (can_turn_left && can_turn_right)
7216         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7217       else if (can_turn_left)
7218         MovDir[x][y] = left_dir;
7219       else
7220         MovDir[x][y] = right_dir;
7221     }
7222
7223     if (MovDir[x][y] != old_move_dir)
7224       MovDelay[x][y] = 9;
7225   }
7226   else if (element == EL_BALLOON)
7227   {
7228     MovDir[x][y] = game.wind_direction;
7229     MovDelay[x][y] = 0;
7230   }
7231   else if (element == EL_SPRING)
7232   {
7233     if (MovDir[x][y] & MV_HORIZONTAL)
7234     {
7235       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7236           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7237       {
7238         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7239         ResetGfxAnimation(move_x, move_y);
7240         TEST_DrawLevelField(move_x, move_y);
7241
7242         MovDir[x][y] = back_dir;
7243       }
7244       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7245                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7246         MovDir[x][y] = MV_NONE;
7247     }
7248
7249     MovDelay[x][y] = 0;
7250   }
7251   else if (element == EL_ROBOT ||
7252            element == EL_SATELLITE ||
7253            element == EL_PENGUIN ||
7254            element == EL_EMC_ANDROID)
7255   {
7256     int attr_x = -1, attr_y = -1;
7257
7258     if (game.all_players_gone)
7259     {
7260       attr_x = game.exit_x;
7261       attr_y = game.exit_y;
7262     }
7263     else
7264     {
7265       int i;
7266
7267       for (i = 0; i < MAX_PLAYERS; i++)
7268       {
7269         struct PlayerInfo *player = &stored_player[i];
7270         int jx = player->jx, jy = player->jy;
7271
7272         if (!player->active)
7273           continue;
7274
7275         if (attr_x == -1 ||
7276             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7277         {
7278           attr_x = jx;
7279           attr_y = jy;
7280         }
7281       }
7282     }
7283
7284     if (element == EL_ROBOT &&
7285         game.robot_wheel_x >= 0 &&
7286         game.robot_wheel_y >= 0 &&
7287         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7288          game.engine_version < VERSION_IDENT(3,1,0,0)))
7289     {
7290       attr_x = game.robot_wheel_x;
7291       attr_y = game.robot_wheel_y;
7292     }
7293
7294     if (element == EL_PENGUIN)
7295     {
7296       int i;
7297       struct XY *xy = xy_topdown;
7298
7299       for (i = 0; i < NUM_DIRECTIONS; i++)
7300       {
7301         int ex = x + xy[i].x;
7302         int ey = y + xy[i].y;
7303
7304         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7305                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7306                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7307                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7308         {
7309           attr_x = ex;
7310           attr_y = ey;
7311           break;
7312         }
7313       }
7314     }
7315
7316     MovDir[x][y] = MV_NONE;
7317     if (attr_x < x)
7318       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7319     else if (attr_x > x)
7320       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7321     if (attr_y < y)
7322       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7323     else if (attr_y > y)
7324       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7325
7326     if (element == EL_ROBOT)
7327     {
7328       int newx, newy;
7329
7330       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7331         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7332       Moving2Blocked(x, y, &newx, &newy);
7333
7334       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7335         MovDelay[x][y] = 8 + 8 * !RND(3);
7336       else
7337         MovDelay[x][y] = 16;
7338     }
7339     else if (element == EL_PENGUIN)
7340     {
7341       int newx, newy;
7342
7343       MovDelay[x][y] = 1;
7344
7345       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7346       {
7347         boolean first_horiz = RND(2);
7348         int new_move_dir = MovDir[x][y];
7349
7350         MovDir[x][y] =
7351           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7352         Moving2Blocked(x, y, &newx, &newy);
7353
7354         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7355           return;
7356
7357         MovDir[x][y] =
7358           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7359         Moving2Blocked(x, y, &newx, &newy);
7360
7361         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7362           return;
7363
7364         MovDir[x][y] = old_move_dir;
7365         return;
7366       }
7367     }
7368     else if (element == EL_SATELLITE)
7369     {
7370       int newx, newy;
7371
7372       MovDelay[x][y] = 1;
7373
7374       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7375       {
7376         boolean first_horiz = RND(2);
7377         int new_move_dir = MovDir[x][y];
7378
7379         MovDir[x][y] =
7380           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7381         Moving2Blocked(x, y, &newx, &newy);
7382
7383         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7384           return;
7385
7386         MovDir[x][y] =
7387           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7388         Moving2Blocked(x, y, &newx, &newy);
7389
7390         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7391           return;
7392
7393         MovDir[x][y] = old_move_dir;
7394         return;
7395       }
7396     }
7397     else if (element == EL_EMC_ANDROID)
7398     {
7399       static int check_pos[16] =
7400       {
7401         -1,             //  0 => (invalid)
7402         7,              //  1 => MV_LEFT
7403         3,              //  2 => MV_RIGHT
7404         -1,             //  3 => (invalid)
7405         1,              //  4 =>            MV_UP
7406         0,              //  5 => MV_LEFT  | MV_UP
7407         2,              //  6 => MV_RIGHT | MV_UP
7408         -1,             //  7 => (invalid)
7409         5,              //  8 =>            MV_DOWN
7410         6,              //  9 => MV_LEFT  | MV_DOWN
7411         4,              // 10 => MV_RIGHT | MV_DOWN
7412         -1,             // 11 => (invalid)
7413         -1,             // 12 => (invalid)
7414         -1,             // 13 => (invalid)
7415         -1,             // 14 => (invalid)
7416         -1,             // 15 => (invalid)
7417       };
7418       static struct
7419       {
7420         int dx, dy;
7421         int dir;
7422       } check_xy[8] =
7423       {
7424         { -1, -1,       MV_LEFT  | MV_UP   },
7425         {  0, -1,                  MV_UP   },
7426         { +1, -1,       MV_RIGHT | MV_UP   },
7427         { +1,  0,       MV_RIGHT           },
7428         { +1, +1,       MV_RIGHT | MV_DOWN },
7429         {  0, +1,                  MV_DOWN },
7430         { -1, +1,       MV_LEFT  | MV_DOWN },
7431         { -1,  0,       MV_LEFT            },
7432       };
7433       int start_pos, check_order;
7434       boolean can_clone = FALSE;
7435       int i;
7436
7437       // check if there is any free field around current position
7438       for (i = 0; i < 8; i++)
7439       {
7440         int newx = x + check_xy[i].dx;
7441         int newy = y + check_xy[i].dy;
7442
7443         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7444         {
7445           can_clone = TRUE;
7446
7447           break;
7448         }
7449       }
7450
7451       if (can_clone)            // randomly find an element to clone
7452       {
7453         can_clone = FALSE;
7454
7455         start_pos = check_pos[RND(8)];
7456         check_order = (RND(2) ? -1 : +1);
7457
7458         for (i = 0; i < 8; i++)
7459         {
7460           int pos_raw = start_pos + i * check_order;
7461           int pos = (pos_raw + 8) % 8;
7462           int newx = x + check_xy[pos].dx;
7463           int newy = y + check_xy[pos].dy;
7464
7465           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7466           {
7467             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7468             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7469
7470             Store[x][y] = Tile[newx][newy];
7471
7472             can_clone = TRUE;
7473
7474             break;
7475           }
7476         }
7477       }
7478
7479       if (can_clone)            // randomly find a direction to move
7480       {
7481         can_clone = FALSE;
7482
7483         start_pos = check_pos[RND(8)];
7484         check_order = (RND(2) ? -1 : +1);
7485
7486         for (i = 0; i < 8; i++)
7487         {
7488           int pos_raw = start_pos + i * check_order;
7489           int pos = (pos_raw + 8) % 8;
7490           int newx = x + check_xy[pos].dx;
7491           int newy = y + check_xy[pos].dy;
7492           int new_move_dir = check_xy[pos].dir;
7493
7494           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7495           {
7496             MovDir[x][y] = new_move_dir;
7497             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7498
7499             can_clone = TRUE;
7500
7501             break;
7502           }
7503         }
7504       }
7505
7506       if (can_clone)            // cloning and moving successful
7507         return;
7508
7509       // cannot clone -- try to move towards player
7510
7511       start_pos = check_pos[MovDir[x][y] & 0x0f];
7512       check_order = (RND(2) ? -1 : +1);
7513
7514       for (i = 0; i < 3; i++)
7515       {
7516         // first check start_pos, then previous/next or (next/previous) pos
7517         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7518         int pos = (pos_raw + 8) % 8;
7519         int newx = x + check_xy[pos].dx;
7520         int newy = y + check_xy[pos].dy;
7521         int new_move_dir = check_xy[pos].dir;
7522
7523         if (IS_PLAYER(newx, newy))
7524           break;
7525
7526         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7527         {
7528           MovDir[x][y] = new_move_dir;
7529           MovDelay[x][y] = level.android_move_time * 8 + 1;
7530
7531           break;
7532         }
7533       }
7534     }
7535   }
7536   else if (move_pattern == MV_TURNING_LEFT ||
7537            move_pattern == MV_TURNING_RIGHT ||
7538            move_pattern == MV_TURNING_LEFT_RIGHT ||
7539            move_pattern == MV_TURNING_RIGHT_LEFT ||
7540            move_pattern == MV_TURNING_RANDOM ||
7541            move_pattern == MV_ALL_DIRECTIONS)
7542   {
7543     boolean can_turn_left =
7544       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7545     boolean can_turn_right =
7546       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7547
7548     if (element_info[element].move_stepsize == 0)       // "not moving"
7549       return;
7550
7551     if (move_pattern == MV_TURNING_LEFT)
7552       MovDir[x][y] = left_dir;
7553     else if (move_pattern == MV_TURNING_RIGHT)
7554       MovDir[x][y] = right_dir;
7555     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7556       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7557     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7558       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7559     else if (move_pattern == MV_TURNING_RANDOM)
7560       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7561                       can_turn_right && !can_turn_left ? right_dir :
7562                       RND(2) ? left_dir : right_dir);
7563     else if (can_turn_left && can_turn_right)
7564       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7565     else if (can_turn_left)
7566       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7567     else if (can_turn_right)
7568       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7569     else
7570       MovDir[x][y] = back_dir;
7571
7572     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7573   }
7574   else if (move_pattern == MV_HORIZONTAL ||
7575            move_pattern == MV_VERTICAL)
7576   {
7577     if (move_pattern & old_move_dir)
7578       MovDir[x][y] = back_dir;
7579     else if (move_pattern == MV_HORIZONTAL)
7580       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7581     else if (move_pattern == MV_VERTICAL)
7582       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7583
7584     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7585   }
7586   else if (move_pattern & MV_ANY_DIRECTION)
7587   {
7588     MovDir[x][y] = move_pattern;
7589     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7590   }
7591   else if (move_pattern & MV_WIND_DIRECTION)
7592   {
7593     MovDir[x][y] = game.wind_direction;
7594     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7595   }
7596   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7597   {
7598     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7599       MovDir[x][y] = left_dir;
7600     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7601       MovDir[x][y] = right_dir;
7602
7603     if (MovDir[x][y] != old_move_dir)
7604       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7605   }
7606   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7607   {
7608     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7609       MovDir[x][y] = right_dir;
7610     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7611       MovDir[x][y] = left_dir;
7612
7613     if (MovDir[x][y] != old_move_dir)
7614       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7615   }
7616   else if (move_pattern == MV_TOWARDS_PLAYER ||
7617            move_pattern == MV_AWAY_FROM_PLAYER)
7618   {
7619     int attr_x = -1, attr_y = -1;
7620     int newx, newy;
7621     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7622
7623     if (game.all_players_gone)
7624     {
7625       attr_x = game.exit_x;
7626       attr_y = game.exit_y;
7627     }
7628     else
7629     {
7630       int i;
7631
7632       for (i = 0; i < MAX_PLAYERS; i++)
7633       {
7634         struct PlayerInfo *player = &stored_player[i];
7635         int jx = player->jx, jy = player->jy;
7636
7637         if (!player->active)
7638           continue;
7639
7640         if (attr_x == -1 ||
7641             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7642         {
7643           attr_x = jx;
7644           attr_y = jy;
7645         }
7646       }
7647     }
7648
7649     MovDir[x][y] = MV_NONE;
7650     if (attr_x < x)
7651       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7652     else if (attr_x > x)
7653       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7654     if (attr_y < y)
7655       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7656     else if (attr_y > y)
7657       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7658
7659     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7660
7661     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7662     {
7663       boolean first_horiz = RND(2);
7664       int new_move_dir = MovDir[x][y];
7665
7666       if (element_info[element].move_stepsize == 0)     // "not moving"
7667       {
7668         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7669         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7670
7671         return;
7672       }
7673
7674       MovDir[x][y] =
7675         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7676       Moving2Blocked(x, y, &newx, &newy);
7677
7678       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7679         return;
7680
7681       MovDir[x][y] =
7682         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7683       Moving2Blocked(x, y, &newx, &newy);
7684
7685       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7686         return;
7687
7688       MovDir[x][y] = old_move_dir;
7689     }
7690   }
7691   else if (move_pattern == MV_WHEN_PUSHED ||
7692            move_pattern == MV_WHEN_DROPPED)
7693   {
7694     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7695       MovDir[x][y] = MV_NONE;
7696
7697     MovDelay[x][y] = 0;
7698   }
7699   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7700   {
7701     struct XY *test_xy = xy_topdown;
7702     static int test_dir[4] =
7703     {
7704       MV_UP,
7705       MV_LEFT,
7706       MV_RIGHT,
7707       MV_DOWN
7708     };
7709     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7710     int move_preference = -1000000;     // start with very low preference
7711     int new_move_dir = MV_NONE;
7712     int start_test = RND(4);
7713     int i;
7714
7715     for (i = 0; i < NUM_DIRECTIONS; i++)
7716     {
7717       int j = (start_test + i) % 4;
7718       int move_dir = test_dir[j];
7719       int move_dir_preference;
7720
7721       xx = x + test_xy[j].x;
7722       yy = y + test_xy[j].y;
7723
7724       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7725           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7726       {
7727         new_move_dir = move_dir;
7728
7729         break;
7730       }
7731
7732       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7733         continue;
7734
7735       move_dir_preference = -1 * RunnerVisit[xx][yy];
7736       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7737         move_dir_preference = PlayerVisit[xx][yy];
7738
7739       if (move_dir_preference > move_preference)
7740       {
7741         // prefer field that has not been visited for the longest time
7742         move_preference = move_dir_preference;
7743         new_move_dir = move_dir;
7744       }
7745       else if (move_dir_preference == move_preference &&
7746                move_dir == old_move_dir)
7747       {
7748         // prefer last direction when all directions are preferred equally
7749         move_preference = move_dir_preference;
7750         new_move_dir = move_dir;
7751       }
7752     }
7753
7754     MovDir[x][y] = new_move_dir;
7755     if (old_move_dir != new_move_dir)
7756       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7757   }
7758 }
7759
7760 static void TurnRound(int x, int y)
7761 {
7762   int direction = MovDir[x][y];
7763
7764   TurnRoundExt(x, y);
7765
7766   GfxDir[x][y] = MovDir[x][y];
7767
7768   if (direction != MovDir[x][y])
7769     GfxFrame[x][y] = 0;
7770
7771   if (MovDelay[x][y])
7772     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7773
7774   ResetGfxFrame(x, y);
7775 }
7776
7777 static boolean JustBeingPushed(int x, int y)
7778 {
7779   int i;
7780
7781   for (i = 0; i < MAX_PLAYERS; i++)
7782   {
7783     struct PlayerInfo *player = &stored_player[i];
7784
7785     if (player->active && player->is_pushing && player->MovPos)
7786     {
7787       int next_jx = player->jx + (player->jx - player->last_jx);
7788       int next_jy = player->jy + (player->jy - player->last_jy);
7789
7790       if (x == next_jx && y == next_jy)
7791         return TRUE;
7792     }
7793   }
7794
7795   return FALSE;
7796 }
7797
7798 static void StartMoving(int x, int y)
7799 {
7800   boolean started_moving = FALSE;       // some elements can fall _and_ move
7801   int element = Tile[x][y];
7802
7803   if (Stop[x][y])
7804     return;
7805
7806   if (MovDelay[x][y] == 0)
7807     GfxAction[x][y] = ACTION_DEFAULT;
7808
7809   if (CAN_FALL(element) && y < lev_fieldy - 1)
7810   {
7811     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7812         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7813       if (JustBeingPushed(x, y))
7814         return;
7815
7816     if (element == EL_QUICKSAND_FULL)
7817     {
7818       if (IS_FREE(x, y + 1))
7819       {
7820         InitMovingField(x, y, MV_DOWN);
7821         started_moving = TRUE;
7822
7823         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7824 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7825         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7826           Store[x][y] = EL_ROCK;
7827 #else
7828         Store[x][y] = EL_ROCK;
7829 #endif
7830
7831         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7832       }
7833       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7834       {
7835         if (!MovDelay[x][y])
7836         {
7837           MovDelay[x][y] = TILEY + 1;
7838
7839           ResetGfxAnimation(x, y);
7840           ResetGfxAnimation(x, y + 1);
7841         }
7842
7843         if (MovDelay[x][y])
7844         {
7845           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7846           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7847
7848           MovDelay[x][y]--;
7849           if (MovDelay[x][y])
7850             return;
7851         }
7852
7853         Tile[x][y] = EL_QUICKSAND_EMPTY;
7854         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7855         Store[x][y + 1] = Store[x][y];
7856         Store[x][y] = 0;
7857
7858         PlayLevelSoundAction(x, y, ACTION_FILLING);
7859       }
7860       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7861       {
7862         if (!MovDelay[x][y])
7863         {
7864           MovDelay[x][y] = TILEY + 1;
7865
7866           ResetGfxAnimation(x, y);
7867           ResetGfxAnimation(x, y + 1);
7868         }
7869
7870         if (MovDelay[x][y])
7871         {
7872           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7873           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7874
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Tile[x][y] = EL_QUICKSAND_EMPTY;
7881         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7882         Store[x][y + 1] = Store[x][y];
7883         Store[x][y] = 0;
7884
7885         PlayLevelSoundAction(x, y, ACTION_FILLING);
7886       }
7887     }
7888     else if (element == EL_QUICKSAND_FAST_FULL)
7889     {
7890       if (IS_FREE(x, y + 1))
7891       {
7892         InitMovingField(x, y, MV_DOWN);
7893         started_moving = TRUE;
7894
7895         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7896 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7897         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7898           Store[x][y] = EL_ROCK;
7899 #else
7900         Store[x][y] = EL_ROCK;
7901 #endif
7902
7903         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7904       }
7905       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7906       {
7907         if (!MovDelay[x][y])
7908         {
7909           MovDelay[x][y] = TILEY + 1;
7910
7911           ResetGfxAnimation(x, y);
7912           ResetGfxAnimation(x, y + 1);
7913         }
7914
7915         if (MovDelay[x][y])
7916         {
7917           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7918           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7919
7920           MovDelay[x][y]--;
7921           if (MovDelay[x][y])
7922             return;
7923         }
7924
7925         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7926         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7927         Store[x][y + 1] = Store[x][y];
7928         Store[x][y] = 0;
7929
7930         PlayLevelSoundAction(x, y, ACTION_FILLING);
7931       }
7932       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7933       {
7934         if (!MovDelay[x][y])
7935         {
7936           MovDelay[x][y] = TILEY + 1;
7937
7938           ResetGfxAnimation(x, y);
7939           ResetGfxAnimation(x, y + 1);
7940         }
7941
7942         if (MovDelay[x][y])
7943         {
7944           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7945           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7946
7947           MovDelay[x][y]--;
7948           if (MovDelay[x][y])
7949             return;
7950         }
7951
7952         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7953         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7954         Store[x][y + 1] = Store[x][y];
7955         Store[x][y] = 0;
7956
7957         PlayLevelSoundAction(x, y, ACTION_FILLING);
7958       }
7959     }
7960     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7961              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7962     {
7963       InitMovingField(x, y, MV_DOWN);
7964       started_moving = TRUE;
7965
7966       Tile[x][y] = EL_QUICKSAND_FILLING;
7967       Store[x][y] = element;
7968
7969       PlayLevelSoundAction(x, y, ACTION_FILLING);
7970     }
7971     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7972              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7973     {
7974       InitMovingField(x, y, MV_DOWN);
7975       started_moving = TRUE;
7976
7977       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7978       Store[x][y] = element;
7979
7980       PlayLevelSoundAction(x, y, ACTION_FILLING);
7981     }
7982     else if (element == EL_MAGIC_WALL_FULL)
7983     {
7984       if (IS_FREE(x, y + 1))
7985       {
7986         InitMovingField(x, y, MV_DOWN);
7987         started_moving = TRUE;
7988
7989         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7990         Store[x][y] = EL_CHANGED(Store[x][y]);
7991       }
7992       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7993       {
7994         if (!MovDelay[x][y])
7995           MovDelay[x][y] = TILEY / 4 + 1;
7996
7997         if (MovDelay[x][y])
7998         {
7999           MovDelay[x][y]--;
8000           if (MovDelay[x][y])
8001             return;
8002         }
8003
8004         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8005         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8006         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8007         Store[x][y] = 0;
8008       }
8009     }
8010     else if (element == EL_BD_MAGIC_WALL_FULL)
8011     {
8012       if (IS_FREE(x, y + 1))
8013       {
8014         InitMovingField(x, y, MV_DOWN);
8015         started_moving = TRUE;
8016
8017         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8018         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8019       }
8020       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8021       {
8022         if (!MovDelay[x][y])
8023           MovDelay[x][y] = TILEY / 4 + 1;
8024
8025         if (MovDelay[x][y])
8026         {
8027           MovDelay[x][y]--;
8028           if (MovDelay[x][y])
8029             return;
8030         }
8031
8032         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8033         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8034         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8035         Store[x][y] = 0;
8036       }
8037     }
8038     else if (element == EL_DC_MAGIC_WALL_FULL)
8039     {
8040       if (IS_FREE(x, y + 1))
8041       {
8042         InitMovingField(x, y, MV_DOWN);
8043         started_moving = TRUE;
8044
8045         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8046         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8047       }
8048       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8049       {
8050         if (!MovDelay[x][y])
8051           MovDelay[x][y] = TILEY / 4 + 1;
8052
8053         if (MovDelay[x][y])
8054         {
8055           MovDelay[x][y]--;
8056           if (MovDelay[x][y])
8057             return;
8058         }
8059
8060         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8061         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8062         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8063         Store[x][y] = 0;
8064       }
8065     }
8066     else if ((CAN_PASS_MAGIC_WALL(element) &&
8067               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8068                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8069              (CAN_PASS_DC_MAGIC_WALL(element) &&
8070               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8071
8072     {
8073       InitMovingField(x, y, MV_DOWN);
8074       started_moving = TRUE;
8075
8076       Tile[x][y] =
8077         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8078          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8079          EL_DC_MAGIC_WALL_FILLING);
8080       Store[x][y] = element;
8081     }
8082     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8083     {
8084       SplashAcid(x, y + 1);
8085
8086       InitMovingField(x, y, MV_DOWN);
8087       started_moving = TRUE;
8088
8089       Store[x][y] = EL_ACID;
8090     }
8091     else if (
8092              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8093               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8094              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8095               CAN_FALL(element) && WasJustFalling[x][y] &&
8096               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8097
8098              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8099               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8100               (Tile[x][y + 1] == EL_BLOCKED)))
8101     {
8102       /* this is needed for a special case not covered by calling "Impact()"
8103          from "ContinueMoving()": if an element moves to a tile directly below
8104          another element which was just falling on that tile (which was empty
8105          in the previous frame), the falling element above would just stop
8106          instead of smashing the element below (in previous version, the above
8107          element was just checked for "moving" instead of "falling", resulting
8108          in incorrect smashes caused by horizontal movement of the above
8109          element; also, the case of the player being the element to smash was
8110          simply not covered here... :-/ ) */
8111
8112       CheckCollision[x][y] = 0;
8113       CheckImpact[x][y] = 0;
8114
8115       Impact(x, y);
8116     }
8117     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8118     {
8119       if (MovDir[x][y] == MV_NONE)
8120       {
8121         InitMovingField(x, y, MV_DOWN);
8122         started_moving = TRUE;
8123       }
8124     }
8125     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8126     {
8127       if (WasJustFalling[x][y]) // prevent animation from being restarted
8128         MovDir[x][y] = MV_DOWN;
8129
8130       InitMovingField(x, y, MV_DOWN);
8131       started_moving = TRUE;
8132     }
8133     else if (element == EL_AMOEBA_DROP)
8134     {
8135       Tile[x][y] = EL_AMOEBA_GROWING;
8136       Store[x][y] = EL_AMOEBA_WET;
8137     }
8138     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8139               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8140              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8141              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8142     {
8143       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8144                                 (IS_FREE(x - 1, y + 1) ||
8145                                  Tile[x - 1][y + 1] == EL_ACID));
8146       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8147                                 (IS_FREE(x + 1, y + 1) ||
8148                                  Tile[x + 1][y + 1] == EL_ACID));
8149       boolean can_fall_any  = (can_fall_left || can_fall_right);
8150       boolean can_fall_both = (can_fall_left && can_fall_right);
8151       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8152
8153       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8154       {
8155         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8156           can_fall_right = FALSE;
8157         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8158           can_fall_left = FALSE;
8159         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8160           can_fall_right = FALSE;
8161         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8162           can_fall_left = FALSE;
8163
8164         can_fall_any  = (can_fall_left || can_fall_right);
8165         can_fall_both = FALSE;
8166       }
8167
8168       if (can_fall_both)
8169       {
8170         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8171           can_fall_right = FALSE;       // slip down on left side
8172         else
8173           can_fall_left = !(can_fall_right = RND(2));
8174
8175         can_fall_both = FALSE;
8176       }
8177
8178       if (can_fall_any)
8179       {
8180         // if not determined otherwise, prefer left side for slipping down
8181         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8182         started_moving = TRUE;
8183       }
8184     }
8185     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8186     {
8187       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8188       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8189       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8190       int belt_dir = game.belt_dir[belt_nr];
8191
8192       if ((belt_dir == MV_LEFT  && left_is_free) ||
8193           (belt_dir == MV_RIGHT && right_is_free))
8194       {
8195         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8196
8197         InitMovingField(x, y, belt_dir);
8198         started_moving = TRUE;
8199
8200         Pushed[x][y] = TRUE;
8201         Pushed[nextx][y] = TRUE;
8202
8203         GfxAction[x][y] = ACTION_DEFAULT;
8204       }
8205       else
8206       {
8207         MovDir[x][y] = 0;       // if element was moving, stop it
8208       }
8209     }
8210   }
8211
8212   // not "else if" because of elements that can fall and move (EL_SPRING)
8213   if (CAN_MOVE(element) && !started_moving)
8214   {
8215     int move_pattern = element_info[element].move_pattern;
8216     int newx, newy;
8217
8218     Moving2Blocked(x, y, &newx, &newy);
8219
8220     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8221       return;
8222
8223     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8224         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8225     {
8226       WasJustMoving[x][y] = 0;
8227       CheckCollision[x][y] = 0;
8228
8229       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8230
8231       if (Tile[x][y] != element)        // element has changed
8232         return;
8233     }
8234
8235     if (!MovDelay[x][y])        // start new movement phase
8236     {
8237       // all objects that can change their move direction after each step
8238       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8239
8240       if (element != EL_YAMYAM &&
8241           element != EL_DARK_YAMYAM &&
8242           element != EL_PACMAN &&
8243           !(move_pattern & MV_ANY_DIRECTION) &&
8244           move_pattern != MV_TURNING_LEFT &&
8245           move_pattern != MV_TURNING_RIGHT &&
8246           move_pattern != MV_TURNING_LEFT_RIGHT &&
8247           move_pattern != MV_TURNING_RIGHT_LEFT &&
8248           move_pattern != MV_TURNING_RANDOM)
8249       {
8250         TurnRound(x, y);
8251
8252         if (MovDelay[x][y] && (element == EL_BUG ||
8253                                element == EL_SPACESHIP ||
8254                                element == EL_SP_SNIKSNAK ||
8255                                element == EL_SP_ELECTRON ||
8256                                element == EL_MOLE))
8257           TEST_DrawLevelField(x, y);
8258       }
8259     }
8260
8261     if (MovDelay[x][y])         // wait some time before next movement
8262     {
8263       MovDelay[x][y]--;
8264
8265       if (element == EL_ROBOT ||
8266           element == EL_YAMYAM ||
8267           element == EL_DARK_YAMYAM)
8268       {
8269         DrawLevelElementAnimationIfNeeded(x, y, element);
8270         PlayLevelSoundAction(x, y, ACTION_WAITING);
8271       }
8272       else if (element == EL_SP_ELECTRON)
8273         DrawLevelElementAnimationIfNeeded(x, y, element);
8274       else if (element == EL_DRAGON)
8275       {
8276         int i;
8277         int dir = MovDir[x][y];
8278         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8279         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8280         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8281                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8282                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8283                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8284         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8285
8286         GfxAction[x][y] = ACTION_ATTACKING;
8287
8288         if (IS_PLAYER(x, y))
8289           DrawPlayerField(x, y);
8290         else
8291           TEST_DrawLevelField(x, y);
8292
8293         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8294
8295         for (i = 1; i <= 3; i++)
8296         {
8297           int xx = x + i * dx;
8298           int yy = y + i * dy;
8299           int sx = SCREENX(xx);
8300           int sy = SCREENY(yy);
8301           int flame_graphic = graphic + (i - 1);
8302
8303           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8304             break;
8305
8306           if (MovDelay[x][y])
8307           {
8308             int flamed = MovingOrBlocked2Element(xx, yy);
8309
8310             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8311               Bang(xx, yy);
8312             else
8313               RemoveMovingField(xx, yy);
8314
8315             ChangeDelay[xx][yy] = 0;
8316
8317             Tile[xx][yy] = EL_FLAMES;
8318
8319             if (IN_SCR_FIELD(sx, sy))
8320             {
8321               TEST_DrawLevelFieldCrumbled(xx, yy);
8322               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8323             }
8324           }
8325           else
8326           {
8327             if (Tile[xx][yy] == EL_FLAMES)
8328               Tile[xx][yy] = EL_EMPTY;
8329             TEST_DrawLevelField(xx, yy);
8330           }
8331         }
8332       }
8333
8334       if (MovDelay[x][y])       // element still has to wait some time
8335       {
8336         PlayLevelSoundAction(x, y, ACTION_WAITING);
8337
8338         return;
8339       }
8340     }
8341
8342     // now make next step
8343
8344     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8345
8346     if (DONT_COLLIDE_WITH(element) &&
8347         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8348         !PLAYER_ENEMY_PROTECTED(newx, newy))
8349     {
8350       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8351
8352       return;
8353     }
8354
8355     else if (CAN_MOVE_INTO_ACID(element) &&
8356              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8357              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8358              (MovDir[x][y] == MV_DOWN ||
8359               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8360     {
8361       SplashAcid(newx, newy);
8362       Store[x][y] = EL_ACID;
8363     }
8364     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8365     {
8366       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8367           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8368           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8369           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8370       {
8371         RemoveField(x, y);
8372         TEST_DrawLevelField(x, y);
8373
8374         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8375         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8376           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8377
8378         game.friends_still_needed--;
8379         if (!game.friends_still_needed &&
8380             !game.GameOver &&
8381             game.all_players_gone)
8382           LevelSolved();
8383
8384         return;
8385       }
8386       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8387       {
8388         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8389           TEST_DrawLevelField(newx, newy);
8390         else
8391           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8392       }
8393       else if (!IS_FREE(newx, newy))
8394       {
8395         GfxAction[x][y] = ACTION_WAITING;
8396
8397         if (IS_PLAYER(x, y))
8398           DrawPlayerField(x, y);
8399         else
8400           TEST_DrawLevelField(x, y);
8401
8402         return;
8403       }
8404     }
8405     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8406     {
8407       if (IS_FOOD_PIG(Tile[newx][newy]))
8408       {
8409         if (IS_MOVING(newx, newy))
8410           RemoveMovingField(newx, newy);
8411         else
8412         {
8413           Tile[newx][newy] = EL_EMPTY;
8414           TEST_DrawLevelField(newx, newy);
8415         }
8416
8417         PlayLevelSound(x, y, SND_PIG_DIGGING);
8418       }
8419       else if (!IS_FREE(newx, newy))
8420       {
8421         if (IS_PLAYER(x, y))
8422           DrawPlayerField(x, y);
8423         else
8424           TEST_DrawLevelField(x, y);
8425
8426         return;
8427       }
8428     }
8429     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8430     {
8431       if (Store[x][y] != EL_EMPTY)
8432       {
8433         boolean can_clone = FALSE;
8434         int xx, yy;
8435
8436         // check if element to clone is still there
8437         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8438         {
8439           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8440           {
8441             can_clone = TRUE;
8442
8443             break;
8444           }
8445         }
8446
8447         // cannot clone or target field not free anymore -- do not clone
8448         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8449           Store[x][y] = EL_EMPTY;
8450       }
8451
8452       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8453       {
8454         if (IS_MV_DIAGONAL(MovDir[x][y]))
8455         {
8456           int diagonal_move_dir = MovDir[x][y];
8457           int stored = Store[x][y];
8458           int change_delay = 8;
8459           int graphic;
8460
8461           // android is moving diagonally
8462
8463           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8464
8465           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8466           GfxElement[x][y] = EL_EMC_ANDROID;
8467           GfxAction[x][y] = ACTION_SHRINKING;
8468           GfxDir[x][y] = diagonal_move_dir;
8469           ChangeDelay[x][y] = change_delay;
8470
8471           if (Store[x][y] == EL_EMPTY)
8472             Store[x][y] = GfxElementEmpty[x][y];
8473
8474           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8475                                    GfxDir[x][y]);
8476
8477           DrawLevelGraphicAnimation(x, y, graphic);
8478           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8479
8480           if (Tile[newx][newy] == EL_ACID)
8481           {
8482             SplashAcid(newx, newy);
8483
8484             return;
8485           }
8486
8487           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8488
8489           Store[newx][newy] = EL_EMC_ANDROID;
8490           GfxElement[newx][newy] = EL_EMC_ANDROID;
8491           GfxAction[newx][newy] = ACTION_GROWING;
8492           GfxDir[newx][newy] = diagonal_move_dir;
8493           ChangeDelay[newx][newy] = change_delay;
8494
8495           graphic = el_act_dir2img(GfxElement[newx][newy],
8496                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8497
8498           DrawLevelGraphicAnimation(newx, newy, graphic);
8499           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8500
8501           return;
8502         }
8503         else
8504         {
8505           Tile[newx][newy] = EL_EMPTY;
8506           TEST_DrawLevelField(newx, newy);
8507
8508           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8509         }
8510       }
8511       else if (!IS_FREE(newx, newy))
8512       {
8513         return;
8514       }
8515     }
8516     else if (IS_CUSTOM_ELEMENT(element) &&
8517              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8518     {
8519       if (!DigFieldByCE(newx, newy, element))
8520         return;
8521
8522       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8523       {
8524         RunnerVisit[x][y] = FrameCounter;
8525         PlayerVisit[x][y] /= 8;         // expire player visit path
8526       }
8527     }
8528     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8529     {
8530       if (!IS_FREE(newx, newy))
8531       {
8532         if (IS_PLAYER(x, y))
8533           DrawPlayerField(x, y);
8534         else
8535           TEST_DrawLevelField(x, y);
8536
8537         return;
8538       }
8539       else
8540       {
8541         boolean wanna_flame = !RND(10);
8542         int dx = newx - x, dy = newy - y;
8543         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8544         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8545         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8546                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8547         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8548                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8549
8550         if ((wanna_flame ||
8551              IS_CLASSIC_ENEMY(element1) ||
8552              IS_CLASSIC_ENEMY(element2)) &&
8553             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8554             element1 != EL_FLAMES && element2 != EL_FLAMES)
8555         {
8556           ResetGfxAnimation(x, y);
8557           GfxAction[x][y] = ACTION_ATTACKING;
8558
8559           if (IS_PLAYER(x, y))
8560             DrawPlayerField(x, y);
8561           else
8562             TEST_DrawLevelField(x, y);
8563
8564           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8565
8566           MovDelay[x][y] = 50;
8567
8568           Tile[newx][newy] = EL_FLAMES;
8569           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8570             Tile[newx1][newy1] = EL_FLAMES;
8571           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8572             Tile[newx2][newy2] = EL_FLAMES;
8573
8574           return;
8575         }
8576       }
8577     }
8578     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8579              Tile[newx][newy] == EL_DIAMOND)
8580     {
8581       if (IS_MOVING(newx, newy))
8582         RemoveMovingField(newx, newy);
8583       else
8584       {
8585         Tile[newx][newy] = EL_EMPTY;
8586         TEST_DrawLevelField(newx, newy);
8587       }
8588
8589       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8590     }
8591     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8592              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8593     {
8594       if (AmoebaNr[newx][newy])
8595       {
8596         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8597         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8598             Tile[newx][newy] == EL_BD_AMOEBA)
8599           AmoebaCnt[AmoebaNr[newx][newy]]--;
8600       }
8601
8602       if (IS_MOVING(newx, newy))
8603       {
8604         RemoveMovingField(newx, newy);
8605       }
8606       else
8607       {
8608         Tile[newx][newy] = EL_EMPTY;
8609         TEST_DrawLevelField(newx, newy);
8610       }
8611
8612       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8613     }
8614     else if ((element == EL_PACMAN || element == EL_MOLE)
8615              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8616     {
8617       if (AmoebaNr[newx][newy])
8618       {
8619         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8620         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8621             Tile[newx][newy] == EL_BD_AMOEBA)
8622           AmoebaCnt[AmoebaNr[newx][newy]]--;
8623       }
8624
8625       if (element == EL_MOLE)
8626       {
8627         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8628         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8629
8630         ResetGfxAnimation(x, y);
8631         GfxAction[x][y] = ACTION_DIGGING;
8632         TEST_DrawLevelField(x, y);
8633
8634         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8635
8636         return;                         // wait for shrinking amoeba
8637       }
8638       else      // element == EL_PACMAN
8639       {
8640         Tile[newx][newy] = EL_EMPTY;
8641         TEST_DrawLevelField(newx, newy);
8642         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8643       }
8644     }
8645     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8646              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8647               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8648     {
8649       // wait for shrinking amoeba to completely disappear
8650       return;
8651     }
8652     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8653     {
8654       // object was running against a wall
8655
8656       TurnRound(x, y);
8657
8658       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8659         DrawLevelElementAnimation(x, y, element);
8660
8661       if (DONT_TOUCH(element))
8662         TestIfBadThingTouchesPlayer(x, y);
8663
8664       return;
8665     }
8666
8667     InitMovingField(x, y, MovDir[x][y]);
8668
8669     PlayLevelSoundAction(x, y, ACTION_MOVING);
8670   }
8671
8672   if (MovDir[x][y])
8673     ContinueMoving(x, y);
8674 }
8675
8676 void ContinueMoving(int x, int y)
8677 {
8678   int element = Tile[x][y];
8679   struct ElementInfo *ei = &element_info[element];
8680   int direction = MovDir[x][y];
8681   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8682   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8683   int newx = x + dx, newy = y + dy;
8684   int stored = Store[x][y];
8685   int stored_new = Store[newx][newy];
8686   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8687   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8688   boolean last_line = (newy == lev_fieldy - 1);
8689   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8690
8691   if (pushed_by_player)         // special case: moving object pushed by player
8692   {
8693     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8694   }
8695   else if (use_step_delay)      // special case: moving object has step delay
8696   {
8697     if (!MovDelay[x][y])
8698       MovPos[x][y] += getElementMoveStepsize(x, y);
8699
8700     if (MovDelay[x][y])
8701       MovDelay[x][y]--;
8702     else
8703       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8704
8705     if (MovDelay[x][y])
8706     {
8707       TEST_DrawLevelField(x, y);
8708
8709       return;   // element is still waiting
8710     }
8711   }
8712   else                          // normal case: generically moving object
8713   {
8714     MovPos[x][y] += getElementMoveStepsize(x, y);
8715   }
8716
8717   if (ABS(MovPos[x][y]) < TILEX)
8718   {
8719     TEST_DrawLevelField(x, y);
8720
8721     return;     // element is still moving
8722   }
8723
8724   // element reached destination field
8725
8726   Tile[x][y] = EL_EMPTY;
8727   Tile[newx][newy] = element;
8728   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8729
8730   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8731   {
8732     element = Tile[newx][newy] = EL_ACID;
8733   }
8734   else if (element == EL_MOLE)
8735   {
8736     Tile[x][y] = EL_SAND;
8737
8738     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8739   }
8740   else if (element == EL_QUICKSAND_FILLING)
8741   {
8742     element = Tile[newx][newy] = get_next_element(element);
8743     Store[newx][newy] = Store[x][y];
8744   }
8745   else if (element == EL_QUICKSAND_EMPTYING)
8746   {
8747     Tile[x][y] = get_next_element(element);
8748     element = Tile[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_QUICKSAND_FAST_FILLING)
8751   {
8752     element = Tile[newx][newy] = get_next_element(element);
8753     Store[newx][newy] = Store[x][y];
8754   }
8755   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8756   {
8757     Tile[x][y] = get_next_element(element);
8758     element = Tile[newx][newy] = Store[x][y];
8759   }
8760   else if (element == EL_MAGIC_WALL_FILLING)
8761   {
8762     element = Tile[newx][newy] = get_next_element(element);
8763     if (!game.magic_wall_active)
8764       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8765     Store[newx][newy] = Store[x][y];
8766   }
8767   else if (element == EL_MAGIC_WALL_EMPTYING)
8768   {
8769     Tile[x][y] = get_next_element(element);
8770     if (!game.magic_wall_active)
8771       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8772     element = Tile[newx][newy] = Store[x][y];
8773
8774     InitField(newx, newy, FALSE);
8775   }
8776   else if (element == EL_BD_MAGIC_WALL_FILLING)
8777   {
8778     element = Tile[newx][newy] = get_next_element(element);
8779     if (!game.magic_wall_active)
8780       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8781     Store[newx][newy] = Store[x][y];
8782   }
8783   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8784   {
8785     Tile[x][y] = get_next_element(element);
8786     if (!game.magic_wall_active)
8787       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8788     element = Tile[newx][newy] = Store[x][y];
8789
8790     InitField(newx, newy, FALSE);
8791   }
8792   else if (element == EL_DC_MAGIC_WALL_FILLING)
8793   {
8794     element = Tile[newx][newy] = get_next_element(element);
8795     if (!game.magic_wall_active)
8796       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8797     Store[newx][newy] = Store[x][y];
8798   }
8799   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8800   {
8801     Tile[x][y] = get_next_element(element);
8802     if (!game.magic_wall_active)
8803       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8804     element = Tile[newx][newy] = Store[x][y];
8805
8806     InitField(newx, newy, FALSE);
8807   }
8808   else if (element == EL_AMOEBA_DROPPING)
8809   {
8810     Tile[x][y] = get_next_element(element);
8811     element = Tile[newx][newy] = Store[x][y];
8812   }
8813   else if (element == EL_SOKOBAN_OBJECT)
8814   {
8815     if (Back[x][y])
8816       Tile[x][y] = Back[x][y];
8817
8818     if (Back[newx][newy])
8819       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8820
8821     Back[x][y] = Back[newx][newy] = 0;
8822   }
8823
8824   Store[x][y] = EL_EMPTY;
8825   MovPos[x][y] = 0;
8826   MovDir[x][y] = 0;
8827   MovDelay[x][y] = 0;
8828
8829   MovDelay[newx][newy] = 0;
8830
8831   if (CAN_CHANGE_OR_HAS_ACTION(element))
8832   {
8833     // copy element change control values to new field
8834     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8835     ChangePage[newx][newy]  = ChangePage[x][y];
8836     ChangeCount[newx][newy] = ChangeCount[x][y];
8837     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8838   }
8839
8840   CustomValue[newx][newy] = CustomValue[x][y];
8841
8842   ChangeDelay[x][y] = 0;
8843   ChangePage[x][y] = -1;
8844   ChangeCount[x][y] = 0;
8845   ChangeEvent[x][y] = -1;
8846
8847   CustomValue[x][y] = 0;
8848
8849   // copy animation control values to new field
8850   GfxFrame[newx][newy]  = GfxFrame[x][y];
8851   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8852   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8853   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8854
8855   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8856
8857   // some elements can leave other elements behind after moving
8858   if (ei->move_leave_element != EL_EMPTY &&
8859       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8860       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8861   {
8862     int move_leave_element = ei->move_leave_element;
8863
8864     // this makes it possible to leave the removed element again
8865     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8866       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8867
8868     Tile[x][y] = move_leave_element;
8869
8870     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8871       MovDir[x][y] = direction;
8872
8873     InitField(x, y, FALSE);
8874
8875     if (GFX_CRUMBLED(Tile[x][y]))
8876       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8877
8878     if (IS_PLAYER_ELEMENT(move_leave_element))
8879       RelocatePlayer(x, y, move_leave_element);
8880   }
8881
8882   // do this after checking for left-behind element
8883   ResetGfxAnimation(x, y);      // reset animation values for old field
8884
8885   if (!CAN_MOVE(element) ||
8886       (CAN_FALL(element) && direction == MV_DOWN &&
8887        (element == EL_SPRING ||
8888         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8889         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8890     GfxDir[x][y] = MovDir[newx][newy] = 0;
8891
8892   TEST_DrawLevelField(x, y);
8893   TEST_DrawLevelField(newx, newy);
8894
8895   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8896
8897   // prevent pushed element from moving on in pushed direction
8898   if (pushed_by_player && CAN_MOVE(element) &&
8899       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8900       !(element_info[element].move_pattern & direction))
8901     TurnRound(newx, newy);
8902
8903   // prevent elements on conveyor belt from moving on in last direction
8904   if (pushed_by_conveyor && CAN_FALL(element) &&
8905       direction & MV_HORIZONTAL)
8906     MovDir[newx][newy] = 0;
8907
8908   if (!pushed_by_player)
8909   {
8910     int nextx = newx + dx, nexty = newy + dy;
8911     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8912
8913     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8914
8915     if (CAN_FALL(element) && direction == MV_DOWN)
8916       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8917
8918     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8919       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8920
8921     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8922       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8923   }
8924
8925   if (DONT_TOUCH(element))      // object may be nasty to player or others
8926   {
8927     TestIfBadThingTouchesPlayer(newx, newy);
8928     TestIfBadThingTouchesFriend(newx, newy);
8929
8930     if (!IS_CUSTOM_ELEMENT(element))
8931       TestIfBadThingTouchesOtherBadThing(newx, newy);
8932   }
8933   else if (element == EL_PENGUIN)
8934     TestIfFriendTouchesBadThing(newx, newy);
8935
8936   if (DONT_GET_HIT_BY(element))
8937   {
8938     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8939   }
8940
8941   // give the player one last chance (one more frame) to move away
8942   if (CAN_FALL(element) && direction == MV_DOWN &&
8943       (last_line || (!IS_FREE(x, newy + 1) &&
8944                      (!IS_PLAYER(x, newy + 1) ||
8945                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8946     Impact(x, newy);
8947
8948   if (pushed_by_player && !game.use_change_when_pushing_bug)
8949   {
8950     int push_side = MV_DIR_OPPOSITE(direction);
8951     struct PlayerInfo *player = PLAYERINFO(x, y);
8952
8953     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8954                                player->index_bit, push_side);
8955     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8956                                         player->index_bit, push_side);
8957   }
8958
8959   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8960     MovDelay[newx][newy] = 1;
8961
8962   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8963
8964   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8965   TestIfElementHitsCustomElement(newx, newy, direction);
8966   TestIfPlayerTouchesCustomElement(newx, newy);
8967   TestIfElementTouchesCustomElement(newx, newy);
8968
8969   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8970       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8971     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8972                              MV_DIR_OPPOSITE(direction));
8973 }
8974
8975 int AmoebaNeighbourNr(int ax, int ay)
8976 {
8977   int i;
8978   int element = Tile[ax][ay];
8979   int group_nr = 0;
8980   struct XY *xy = xy_topdown;
8981
8982   for (i = 0; i < NUM_DIRECTIONS; i++)
8983   {
8984     int x = ax + xy[i].x;
8985     int y = ay + xy[i].y;
8986
8987     if (!IN_LEV_FIELD(x, y))
8988       continue;
8989
8990     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8991       group_nr = AmoebaNr[x][y];
8992   }
8993
8994   return group_nr;
8995 }
8996
8997 static void AmoebaMerge(int ax, int ay)
8998 {
8999   int i, x, y, xx, yy;
9000   int new_group_nr = AmoebaNr[ax][ay];
9001   struct XY *xy = xy_topdown;
9002
9003   if (new_group_nr == 0)
9004     return;
9005
9006   for (i = 0; i < NUM_DIRECTIONS; i++)
9007   {
9008     x = ax + xy[i].x;
9009     y = ay + xy[i].y;
9010
9011     if (!IN_LEV_FIELD(x, y))
9012       continue;
9013
9014     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9015          Tile[x][y] == EL_BD_AMOEBA ||
9016          Tile[x][y] == EL_AMOEBA_DEAD) &&
9017         AmoebaNr[x][y] != new_group_nr)
9018     {
9019       int old_group_nr = AmoebaNr[x][y];
9020
9021       if (old_group_nr == 0)
9022         return;
9023
9024       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9025       AmoebaCnt[old_group_nr] = 0;
9026       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9027       AmoebaCnt2[old_group_nr] = 0;
9028
9029       SCAN_PLAYFIELD(xx, yy)
9030       {
9031         if (AmoebaNr[xx][yy] == old_group_nr)
9032           AmoebaNr[xx][yy] = new_group_nr;
9033       }
9034     }
9035   }
9036 }
9037
9038 void AmoebaToDiamond(int ax, int ay)
9039 {
9040   int i, x, y;
9041
9042   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9043   {
9044     int group_nr = AmoebaNr[ax][ay];
9045
9046 #ifdef DEBUG
9047     if (group_nr == 0)
9048     {
9049       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9050       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9051
9052       return;
9053     }
9054 #endif
9055
9056     SCAN_PLAYFIELD(x, y)
9057     {
9058       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9059       {
9060         AmoebaNr[x][y] = 0;
9061         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9062       }
9063     }
9064
9065     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9066                             SND_AMOEBA_TURNING_TO_GEM :
9067                             SND_AMOEBA_TURNING_TO_ROCK));
9068     Bang(ax, ay);
9069   }
9070   else
9071   {
9072     struct XY *xy = xy_topdown;
9073
9074     for (i = 0; i < NUM_DIRECTIONS; i++)
9075     {
9076       x = ax + xy[i].x;
9077       y = ay + xy[i].y;
9078
9079       if (!IN_LEV_FIELD(x, y))
9080         continue;
9081
9082       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9083       {
9084         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9085                               SND_AMOEBA_TURNING_TO_GEM :
9086                               SND_AMOEBA_TURNING_TO_ROCK));
9087         Bang(x, y);
9088       }
9089     }
9090   }
9091 }
9092
9093 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9094 {
9095   int x, y;
9096   int group_nr = AmoebaNr[ax][ay];
9097   boolean done = FALSE;
9098
9099 #ifdef DEBUG
9100   if (group_nr == 0)
9101   {
9102     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9103     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9104
9105     return;
9106   }
9107 #endif
9108
9109   SCAN_PLAYFIELD(x, y)
9110   {
9111     if (AmoebaNr[x][y] == group_nr &&
9112         (Tile[x][y] == EL_AMOEBA_DEAD ||
9113          Tile[x][y] == EL_BD_AMOEBA ||
9114          Tile[x][y] == EL_AMOEBA_GROWING))
9115     {
9116       AmoebaNr[x][y] = 0;
9117       Tile[x][y] = new_element;
9118       InitField(x, y, FALSE);
9119       TEST_DrawLevelField(x, y);
9120       done = TRUE;
9121     }
9122   }
9123
9124   if (done)
9125     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9126                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9127                             SND_BD_AMOEBA_TURNING_TO_GEM));
9128 }
9129
9130 static void AmoebaGrowing(int x, int y)
9131 {
9132   static DelayCounter sound_delay = { 0 };
9133
9134   if (!MovDelay[x][y])          // start new growing cycle
9135   {
9136     MovDelay[x][y] = 7;
9137
9138     if (DelayReached(&sound_delay))
9139     {
9140       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9141       sound_delay.value = 30;
9142     }
9143   }
9144
9145   if (MovDelay[x][y])           // wait some time before growing bigger
9146   {
9147     MovDelay[x][y]--;
9148     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9149     {
9150       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9151                                            6 - MovDelay[x][y]);
9152
9153       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9154     }
9155
9156     if (!MovDelay[x][y])
9157     {
9158       Tile[x][y] = Store[x][y];
9159       Store[x][y] = 0;
9160       TEST_DrawLevelField(x, y);
9161     }
9162   }
9163 }
9164
9165 static void AmoebaShrinking(int x, int y)
9166 {
9167   static DelayCounter sound_delay = { 0 };
9168
9169   if (!MovDelay[x][y])          // start new shrinking cycle
9170   {
9171     MovDelay[x][y] = 7;
9172
9173     if (DelayReached(&sound_delay))
9174       sound_delay.value = 30;
9175   }
9176
9177   if (MovDelay[x][y])           // wait some time before shrinking
9178   {
9179     MovDelay[x][y]--;
9180     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9181     {
9182       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9183                                            6 - MovDelay[x][y]);
9184
9185       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9186     }
9187
9188     if (!MovDelay[x][y])
9189     {
9190       Tile[x][y] = EL_EMPTY;
9191       TEST_DrawLevelField(x, y);
9192
9193       // don't let mole enter this field in this cycle;
9194       // (give priority to objects falling to this field from above)
9195       Stop[x][y] = TRUE;
9196     }
9197   }
9198 }
9199
9200 static void AmoebaReproduce(int ax, int ay)
9201 {
9202   int i;
9203   int element = Tile[ax][ay];
9204   int graphic = el2img(element);
9205   int newax = ax, neway = ay;
9206   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9207   struct XY *xy = xy_topdown;
9208
9209   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9210   {
9211     Tile[ax][ay] = EL_AMOEBA_DEAD;
9212     TEST_DrawLevelField(ax, ay);
9213     return;
9214   }
9215
9216   if (IS_ANIMATED(graphic))
9217     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9218
9219   if (!MovDelay[ax][ay])        // start making new amoeba field
9220     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9221
9222   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9223   {
9224     MovDelay[ax][ay]--;
9225     if (MovDelay[ax][ay])
9226       return;
9227   }
9228
9229   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9230   {
9231     int start = RND(4);
9232     int x = ax + xy[start].x;
9233     int y = ay + xy[start].y;
9234
9235     if (!IN_LEV_FIELD(x, y))
9236       return;
9237
9238     if (IS_FREE(x, y) ||
9239         CAN_GROW_INTO(Tile[x][y]) ||
9240         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9241         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9242     {
9243       newax = x;
9244       neway = y;
9245     }
9246
9247     if (newax == ax && neway == ay)
9248       return;
9249   }
9250   else                          // normal or "filled" (BD style) amoeba
9251   {
9252     int start = RND(4);
9253     boolean waiting_for_player = FALSE;
9254
9255     for (i = 0; i < NUM_DIRECTIONS; i++)
9256     {
9257       int j = (start + i) % 4;
9258       int x = ax + xy[j].x;
9259       int y = ay + xy[j].y;
9260
9261       if (!IN_LEV_FIELD(x, y))
9262         continue;
9263
9264       if (IS_FREE(x, y) ||
9265           CAN_GROW_INTO(Tile[x][y]) ||
9266           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9267           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9268       {
9269         newax = x;
9270         neway = y;
9271         break;
9272       }
9273       else if (IS_PLAYER(x, y))
9274         waiting_for_player = TRUE;
9275     }
9276
9277     if (newax == ax && neway == ay)             // amoeba cannot grow
9278     {
9279       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9280       {
9281         Tile[ax][ay] = EL_AMOEBA_DEAD;
9282         TEST_DrawLevelField(ax, ay);
9283         AmoebaCnt[AmoebaNr[ax][ay]]--;
9284
9285         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9286         {
9287           if (element == EL_AMOEBA_FULL)
9288             AmoebaToDiamond(ax, ay);
9289           else if (element == EL_BD_AMOEBA)
9290             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9291         }
9292       }
9293       return;
9294     }
9295     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9296     {
9297       // amoeba gets larger by growing in some direction
9298
9299       int new_group_nr = AmoebaNr[ax][ay];
9300
9301 #ifdef DEBUG
9302   if (new_group_nr == 0)
9303   {
9304     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9305           newax, neway);
9306     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9307
9308     return;
9309   }
9310 #endif
9311
9312       AmoebaNr[newax][neway] = new_group_nr;
9313       AmoebaCnt[new_group_nr]++;
9314       AmoebaCnt2[new_group_nr]++;
9315
9316       // if amoeba touches other amoeba(s) after growing, unify them
9317       AmoebaMerge(newax, neway);
9318
9319       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9320       {
9321         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9322         return;
9323       }
9324     }
9325   }
9326
9327   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9328       (neway == lev_fieldy - 1 && newax != ax))
9329   {
9330     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9331     Store[newax][neway] = element;
9332   }
9333   else if (neway == ay || element == EL_EMC_DRIPPER)
9334   {
9335     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9336
9337     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9338   }
9339   else
9340   {
9341     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9342     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9343     Store[ax][ay] = EL_AMOEBA_DROP;
9344     ContinueMoving(ax, ay);
9345     return;
9346   }
9347
9348   TEST_DrawLevelField(newax, neway);
9349 }
9350
9351 static void Life(int ax, int ay)
9352 {
9353   int x1, y1, x2, y2;
9354   int life_time = 40;
9355   int element = Tile[ax][ay];
9356   int graphic = el2img(element);
9357   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9358                          level.biomaze);
9359   boolean changed = FALSE;
9360
9361   if (IS_ANIMATED(graphic))
9362     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9363
9364   if (Stop[ax][ay])
9365     return;
9366
9367   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9368     MovDelay[ax][ay] = life_time;
9369
9370   if (MovDelay[ax][ay])         // wait some time before next cycle
9371   {
9372     MovDelay[ax][ay]--;
9373     if (MovDelay[ax][ay])
9374       return;
9375   }
9376
9377   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9378   {
9379     int xx = ax+x1, yy = ay+y1;
9380     int old_element = Tile[xx][yy];
9381     int num_neighbours = 0;
9382
9383     if (!IN_LEV_FIELD(xx, yy))
9384       continue;
9385
9386     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9387     {
9388       int x = xx+x2, y = yy+y2;
9389
9390       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9391         continue;
9392
9393       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9394       boolean is_neighbour = FALSE;
9395
9396       if (level.use_life_bugs)
9397         is_neighbour =
9398           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9399            (IS_FREE(x, y)                             &&  Stop[x][y]));
9400       else
9401         is_neighbour =
9402           (Last[x][y] == element || is_player_cell);
9403
9404       if (is_neighbour)
9405         num_neighbours++;
9406     }
9407
9408     boolean is_free = FALSE;
9409
9410     if (level.use_life_bugs)
9411       is_free = (IS_FREE(xx, yy));
9412     else
9413       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9414
9415     if (xx == ax && yy == ay)           // field in the middle
9416     {
9417       if (num_neighbours < life_parameter[0] ||
9418           num_neighbours > life_parameter[1])
9419       {
9420         Tile[xx][yy] = EL_EMPTY;
9421         if (Tile[xx][yy] != old_element)
9422           TEST_DrawLevelField(xx, yy);
9423         Stop[xx][yy] = TRUE;
9424         changed = TRUE;
9425       }
9426     }
9427     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9428     {                                   // free border field
9429       if (num_neighbours >= life_parameter[2] &&
9430           num_neighbours <= life_parameter[3])
9431       {
9432         Tile[xx][yy] = element;
9433         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9434         if (Tile[xx][yy] != old_element)
9435           TEST_DrawLevelField(xx, yy);
9436         Stop[xx][yy] = TRUE;
9437         changed = TRUE;
9438       }
9439     }
9440   }
9441
9442   if (changed)
9443     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9444                    SND_GAME_OF_LIFE_GROWING);
9445 }
9446
9447 static void InitRobotWheel(int x, int y)
9448 {
9449   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9450 }
9451
9452 static void RunRobotWheel(int x, int y)
9453 {
9454   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9455 }
9456
9457 static void StopRobotWheel(int x, int y)
9458 {
9459   if (game.robot_wheel_x == x &&
9460       game.robot_wheel_y == y)
9461   {
9462     game.robot_wheel_x = -1;
9463     game.robot_wheel_y = -1;
9464     game.robot_wheel_active = FALSE;
9465   }
9466 }
9467
9468 static void InitTimegateWheel(int x, int y)
9469 {
9470   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9471 }
9472
9473 static void RunTimegateWheel(int x, int y)
9474 {
9475   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9476 }
9477
9478 static void InitMagicBallDelay(int x, int y)
9479 {
9480   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9481 }
9482
9483 static void ActivateMagicBall(int bx, int by)
9484 {
9485   int x, y;
9486
9487   if (level.ball_random)
9488   {
9489     int pos_border = RND(8);    // select one of the eight border elements
9490     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9491     int xx = pos_content % 3;
9492     int yy = pos_content / 3;
9493
9494     x = bx - 1 + xx;
9495     y = by - 1 + yy;
9496
9497     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9498       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9499   }
9500   else
9501   {
9502     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9503     {
9504       int xx = x - bx + 1;
9505       int yy = y - by + 1;
9506
9507       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9508         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9509     }
9510   }
9511
9512   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9513 }
9514
9515 static void CheckExit(int x, int y)
9516 {
9517   if (game.gems_still_needed > 0 ||
9518       game.sokoban_fields_still_needed > 0 ||
9519       game.sokoban_objects_still_needed > 0 ||
9520       game.lights_still_needed > 0)
9521   {
9522     int element = Tile[x][y];
9523     int graphic = el2img(element);
9524
9525     if (IS_ANIMATED(graphic))
9526       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9527
9528     return;
9529   }
9530
9531   // do not re-open exit door closed after last player
9532   if (game.all_players_gone)
9533     return;
9534
9535   Tile[x][y] = EL_EXIT_OPENING;
9536
9537   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9538 }
9539
9540 static void CheckExitEM(int x, int y)
9541 {
9542   if (game.gems_still_needed > 0 ||
9543       game.sokoban_fields_still_needed > 0 ||
9544       game.sokoban_objects_still_needed > 0 ||
9545       game.lights_still_needed > 0)
9546   {
9547     int element = Tile[x][y];
9548     int graphic = el2img(element);
9549
9550     if (IS_ANIMATED(graphic))
9551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9552
9553     return;
9554   }
9555
9556   // do not re-open exit door closed after last player
9557   if (game.all_players_gone)
9558     return;
9559
9560   Tile[x][y] = EL_EM_EXIT_OPENING;
9561
9562   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9563 }
9564
9565 static void CheckExitSteel(int x, int y)
9566 {
9567   if (game.gems_still_needed > 0 ||
9568       game.sokoban_fields_still_needed > 0 ||
9569       game.sokoban_objects_still_needed > 0 ||
9570       game.lights_still_needed > 0)
9571   {
9572     int element = Tile[x][y];
9573     int graphic = el2img(element);
9574
9575     if (IS_ANIMATED(graphic))
9576       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9577
9578     return;
9579   }
9580
9581   // do not re-open exit door closed after last player
9582   if (game.all_players_gone)
9583     return;
9584
9585   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9586
9587   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9588 }
9589
9590 static void CheckExitSteelEM(int x, int y)
9591 {
9592   if (game.gems_still_needed > 0 ||
9593       game.sokoban_fields_still_needed > 0 ||
9594       game.sokoban_objects_still_needed > 0 ||
9595       game.lights_still_needed > 0)
9596   {
9597     int element = Tile[x][y];
9598     int graphic = el2img(element);
9599
9600     if (IS_ANIMATED(graphic))
9601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9602
9603     return;
9604   }
9605
9606   // do not re-open exit door closed after last player
9607   if (game.all_players_gone)
9608     return;
9609
9610   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9611
9612   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9613 }
9614
9615 static void CheckExitSP(int x, int y)
9616 {
9617   if (game.gems_still_needed > 0)
9618   {
9619     int element = Tile[x][y];
9620     int graphic = el2img(element);
9621
9622     if (IS_ANIMATED(graphic))
9623       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9624
9625     return;
9626   }
9627
9628   // do not re-open exit door closed after last player
9629   if (game.all_players_gone)
9630     return;
9631
9632   Tile[x][y] = EL_SP_EXIT_OPENING;
9633
9634   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9635 }
9636
9637 static void CloseAllOpenTimegates(void)
9638 {
9639   int x, y;
9640
9641   SCAN_PLAYFIELD(x, y)
9642   {
9643     int element = Tile[x][y];
9644
9645     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9646     {
9647       Tile[x][y] = EL_TIMEGATE_CLOSING;
9648
9649       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9650     }
9651   }
9652 }
9653
9654 static void DrawTwinkleOnField(int x, int y)
9655 {
9656   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9657     return;
9658
9659   if (Tile[x][y] == EL_BD_DIAMOND)
9660     return;
9661
9662   if (MovDelay[x][y] == 0)      // next animation frame
9663     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9664
9665   if (MovDelay[x][y] != 0)      // wait some time before next frame
9666   {
9667     MovDelay[x][y]--;
9668
9669     DrawLevelElementAnimation(x, y, Tile[x][y]);
9670
9671     if (MovDelay[x][y] != 0)
9672     {
9673       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9674                                            10 - MovDelay[x][y]);
9675
9676       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9677     }
9678   }
9679 }
9680
9681 static void WallGrowing(int x, int y)
9682 {
9683   int delay = 6;
9684
9685   if (!MovDelay[x][y])          // next animation frame
9686     MovDelay[x][y] = 3 * delay;
9687
9688   if (MovDelay[x][y])           // wait some time before next frame
9689   {
9690     MovDelay[x][y]--;
9691
9692     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9693     {
9694       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9695       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9696
9697       DrawLevelGraphic(x, y, graphic, frame);
9698     }
9699
9700     if (!MovDelay[x][y])
9701     {
9702       if (MovDir[x][y] == MV_LEFT)
9703       {
9704         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9705           TEST_DrawLevelField(x - 1, y);
9706       }
9707       else if (MovDir[x][y] == MV_RIGHT)
9708       {
9709         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9710           TEST_DrawLevelField(x + 1, y);
9711       }
9712       else if (MovDir[x][y] == MV_UP)
9713       {
9714         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9715           TEST_DrawLevelField(x, y - 1);
9716       }
9717       else
9718       {
9719         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9720           TEST_DrawLevelField(x, y + 1);
9721       }
9722
9723       Tile[x][y] = Store[x][y];
9724       Store[x][y] = 0;
9725       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9726       TEST_DrawLevelField(x, y);
9727     }
9728   }
9729 }
9730
9731 static void CheckWallGrowing(int ax, int ay)
9732 {
9733   int element = Tile[ax][ay];
9734   int graphic = el2img(element);
9735   boolean free_top    = FALSE;
9736   boolean free_bottom = FALSE;
9737   boolean free_left   = FALSE;
9738   boolean free_right  = FALSE;
9739   boolean stop_top    = FALSE;
9740   boolean stop_bottom = FALSE;
9741   boolean stop_left   = FALSE;
9742   boolean stop_right  = FALSE;
9743   boolean new_wall    = FALSE;
9744
9745   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9746                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9747                            element == EL_EXPANDABLE_STEELWALL_ANY);
9748
9749   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9750                              element == EL_EXPANDABLE_WALL_ANY ||
9751                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9752                              element == EL_EXPANDABLE_STEELWALL_ANY);
9753
9754   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9755                              element == EL_EXPANDABLE_WALL_ANY ||
9756                              element == EL_EXPANDABLE_WALL ||
9757                              element == EL_BD_EXPANDABLE_WALL ||
9758                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9759                              element == EL_EXPANDABLE_STEELWALL_ANY);
9760
9761   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9762                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9763
9764   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9765                              element == EL_EXPANDABLE_WALL ||
9766                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9767
9768   int wall_growing = (is_steelwall ?
9769                       EL_EXPANDABLE_STEELWALL_GROWING :
9770                       EL_EXPANDABLE_WALL_GROWING);
9771
9772   int gfx_wall_growing_up    = (is_steelwall ?
9773                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9774                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9775   int gfx_wall_growing_down  = (is_steelwall ?
9776                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9777                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9778   int gfx_wall_growing_left  = (is_steelwall ?
9779                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9780                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9781   int gfx_wall_growing_right = (is_steelwall ?
9782                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9783                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9784
9785   if (IS_ANIMATED(graphic))
9786     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9787
9788   if (!MovDelay[ax][ay])        // start building new wall
9789     MovDelay[ax][ay] = 6;
9790
9791   if (MovDelay[ax][ay])         // wait some time before building new wall
9792   {
9793     MovDelay[ax][ay]--;
9794     if (MovDelay[ax][ay])
9795       return;
9796   }
9797
9798   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9799     free_top = TRUE;
9800   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9801     free_bottom = TRUE;
9802   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9803     free_left = TRUE;
9804   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9805     free_right = TRUE;
9806
9807   if (grow_vertical)
9808   {
9809     if (free_top)
9810     {
9811       Tile[ax][ay - 1] = wall_growing;
9812       Store[ax][ay - 1] = element;
9813       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9814
9815       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9816         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9817
9818       new_wall = TRUE;
9819     }
9820
9821     if (free_bottom)
9822     {
9823       Tile[ax][ay + 1] = wall_growing;
9824       Store[ax][ay + 1] = element;
9825       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9826
9827       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9828         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9829
9830       new_wall = TRUE;
9831     }
9832   }
9833
9834   if (grow_horizontal)
9835   {
9836     if (free_left)
9837     {
9838       Tile[ax - 1][ay] = wall_growing;
9839       Store[ax - 1][ay] = element;
9840       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9841
9842       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9843         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9844
9845       new_wall = TRUE;
9846     }
9847
9848     if (free_right)
9849     {
9850       Tile[ax + 1][ay] = wall_growing;
9851       Store[ax + 1][ay] = element;
9852       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9853
9854       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9855         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9856
9857       new_wall = TRUE;
9858     }
9859   }
9860
9861   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9862     TEST_DrawLevelField(ax, ay);
9863
9864   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9865     stop_top = TRUE;
9866   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9867     stop_bottom = TRUE;
9868   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9869     stop_left = TRUE;
9870   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9871     stop_right = TRUE;
9872
9873   if (((stop_top && stop_bottom) || stop_horizontal) &&
9874       ((stop_left && stop_right) || stop_vertical))
9875     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9876
9877   if (new_wall)
9878     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9879 }
9880
9881 static void CheckForDragon(int x, int y)
9882 {
9883   int i, j;
9884   boolean dragon_found = FALSE;
9885   struct XY *xy = xy_topdown;
9886
9887   for (i = 0; i < NUM_DIRECTIONS; i++)
9888   {
9889     for (j = 0; j < 4; j++)
9890     {
9891       int xx = x + j * xy[i].x;
9892       int yy = y + j * xy[i].y;
9893
9894       if (IN_LEV_FIELD(xx, yy) &&
9895           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9896       {
9897         if (Tile[xx][yy] == EL_DRAGON)
9898           dragon_found = TRUE;
9899       }
9900       else
9901         break;
9902     }
9903   }
9904
9905   if (!dragon_found)
9906   {
9907     for (i = 0; i < NUM_DIRECTIONS; i++)
9908     {
9909       for (j = 0; j < 3; j++)
9910       {
9911         int xx = x + j * xy[i].x;
9912         int yy = y + j * xy[i].y;
9913
9914         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9915         {
9916           Tile[xx][yy] = EL_EMPTY;
9917           TEST_DrawLevelField(xx, yy);
9918         }
9919         else
9920           break;
9921       }
9922     }
9923   }
9924 }
9925
9926 static void InitBuggyBase(int x, int y)
9927 {
9928   int element = Tile[x][y];
9929   int activating_delay = FRAMES_PER_SECOND / 4;
9930
9931   ChangeDelay[x][y] =
9932     (element == EL_SP_BUGGY_BASE ?
9933      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9934      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9935      activating_delay :
9936      element == EL_SP_BUGGY_BASE_ACTIVE ?
9937      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9938 }
9939
9940 static void WarnBuggyBase(int x, int y)
9941 {
9942   int i;
9943   struct XY *xy = xy_topdown;
9944
9945   for (i = 0; i < NUM_DIRECTIONS; i++)
9946   {
9947     int xx = x + xy[i].x;
9948     int yy = y + xy[i].y;
9949
9950     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9951     {
9952       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9953
9954       break;
9955     }
9956   }
9957 }
9958
9959 static void InitTrap(int x, int y)
9960 {
9961   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9962 }
9963
9964 static void ActivateTrap(int x, int y)
9965 {
9966   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9967 }
9968
9969 static void ChangeActiveTrap(int x, int y)
9970 {
9971   int graphic = IMG_TRAP_ACTIVE;
9972
9973   // if new animation frame was drawn, correct crumbled sand border
9974   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9975     TEST_DrawLevelFieldCrumbled(x, y);
9976 }
9977
9978 static int getSpecialActionElement(int element, int number, int base_element)
9979 {
9980   return (element != EL_EMPTY ? element :
9981           number != -1 ? base_element + number - 1 :
9982           EL_EMPTY);
9983 }
9984
9985 static int getModifiedActionNumber(int value_old, int operator, int operand,
9986                                    int value_min, int value_max)
9987 {
9988   int value_new = (operator == CA_MODE_SET      ? operand :
9989                    operator == CA_MODE_ADD      ? value_old + operand :
9990                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9991                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9992                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9993                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9994                    value_old);
9995
9996   return (value_new < value_min ? value_min :
9997           value_new > value_max ? value_max :
9998           value_new);
9999 }
10000
10001 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10002 {
10003   struct ElementInfo *ei = &element_info[element];
10004   struct ElementChangeInfo *change = &ei->change_page[page];
10005   int target_element = change->target_element;
10006   int action_type = change->action_type;
10007   int action_mode = change->action_mode;
10008   int action_arg = change->action_arg;
10009   int action_element = change->action_element;
10010   int i;
10011
10012   if (!change->has_action)
10013     return;
10014
10015   // ---------- determine action paramater values -----------------------------
10016
10017   int level_time_value =
10018     (level.time > 0 ? TimeLeft :
10019      TimePlayed);
10020
10021   int action_arg_element_raw =
10022     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10023      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10024      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10025      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10026      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10027      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10028      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10029      EL_EMPTY);
10030   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10031
10032   int action_arg_direction =
10033     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10034      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10035      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10036      change->actual_trigger_side :
10037      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10038      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10039      MV_NONE);
10040
10041   int action_arg_number_min =
10042     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10043      CA_ARG_MIN);
10044
10045   int action_arg_number_max =
10046     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10047      action_type == CA_SET_LEVEL_GEMS ? 999 :
10048      action_type == CA_SET_LEVEL_TIME ? 9999 :
10049      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10050      action_type == CA_SET_CE_VALUE ? 9999 :
10051      action_type == CA_SET_CE_SCORE ? 9999 :
10052      CA_ARG_MAX);
10053
10054   int action_arg_number_reset =
10055     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10056      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10057      action_type == CA_SET_LEVEL_TIME ? level.time :
10058      action_type == CA_SET_LEVEL_SCORE ? 0 :
10059      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10060      action_type == CA_SET_CE_SCORE ? 0 :
10061      0);
10062
10063   int action_arg_number =
10064     (action_arg <= CA_ARG_MAX ? action_arg :
10065      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10066      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10067      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10068      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10069      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10070      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10071      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10072      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10073      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10074      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10075      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10076      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10077      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10078      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10079      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10080      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10081      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10082      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10083      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10084      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10085      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10086      -1);
10087
10088   int action_arg_number_old =
10089     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10090      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10091      action_type == CA_SET_LEVEL_SCORE ? game.score :
10092      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10093      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10094      0);
10095
10096   int action_arg_number_new =
10097     getModifiedActionNumber(action_arg_number_old,
10098                             action_mode, action_arg_number,
10099                             action_arg_number_min, action_arg_number_max);
10100
10101   int trigger_player_bits =
10102     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10103      change->actual_trigger_player_bits : change->trigger_player);
10104
10105   int action_arg_player_bits =
10106     (action_arg >= CA_ARG_PLAYER_1 &&
10107      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10108      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10109      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10110      PLAYER_BITS_ANY);
10111
10112   // ---------- execute action  -----------------------------------------------
10113
10114   switch (action_type)
10115   {
10116     case CA_NO_ACTION:
10117     {
10118       return;
10119     }
10120
10121     // ---------- level actions  ----------------------------------------------
10122
10123     case CA_RESTART_LEVEL:
10124     {
10125       game.restart_level = TRUE;
10126
10127       break;
10128     }
10129
10130     case CA_SHOW_ENVELOPE:
10131     {
10132       int element = getSpecialActionElement(action_arg_element,
10133                                             action_arg_number, EL_ENVELOPE_1);
10134
10135       if (IS_ENVELOPE(element))
10136         local_player->show_envelope = element;
10137
10138       break;
10139     }
10140
10141     case CA_SET_LEVEL_TIME:
10142     {
10143       if (level.time > 0)       // only modify limited time value
10144       {
10145         TimeLeft = action_arg_number_new;
10146
10147         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10148
10149         DisplayGameControlValues();
10150
10151         if (!TimeLeft && game.time_limit)
10152           for (i = 0; i < MAX_PLAYERS; i++)
10153             KillPlayer(&stored_player[i]);
10154       }
10155
10156       break;
10157     }
10158
10159     case CA_SET_LEVEL_SCORE:
10160     {
10161       game.score = action_arg_number_new;
10162
10163       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10164
10165       DisplayGameControlValues();
10166
10167       break;
10168     }
10169
10170     case CA_SET_LEVEL_GEMS:
10171     {
10172       game.gems_still_needed = action_arg_number_new;
10173
10174       game.snapshot.collected_item = TRUE;
10175
10176       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10177
10178       DisplayGameControlValues();
10179
10180       break;
10181     }
10182
10183     case CA_SET_LEVEL_WIND:
10184     {
10185       game.wind_direction = action_arg_direction;
10186
10187       break;
10188     }
10189
10190     case CA_SET_LEVEL_RANDOM_SEED:
10191     {
10192       // ensure that setting a new random seed while playing is predictable
10193       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10194
10195       break;
10196     }
10197
10198     // ---------- player actions  ---------------------------------------------
10199
10200     case CA_MOVE_PLAYER:
10201     case CA_MOVE_PLAYER_NEW:
10202     {
10203       // automatically move to the next field in specified direction
10204       for (i = 0; i < MAX_PLAYERS; i++)
10205         if (trigger_player_bits & (1 << i))
10206           if (action_type == CA_MOVE_PLAYER ||
10207               stored_player[i].MovPos == 0)
10208             stored_player[i].programmed_action = action_arg_direction;
10209
10210       break;
10211     }
10212
10213     case CA_EXIT_PLAYER:
10214     {
10215       for (i = 0; i < MAX_PLAYERS; i++)
10216         if (action_arg_player_bits & (1 << i))
10217           ExitPlayer(&stored_player[i]);
10218
10219       if (game.players_still_needed == 0)
10220         LevelSolved();
10221
10222       break;
10223     }
10224
10225     case CA_KILL_PLAYER:
10226     {
10227       for (i = 0; i < MAX_PLAYERS; i++)
10228         if (action_arg_player_bits & (1 << i))
10229           KillPlayer(&stored_player[i]);
10230
10231       break;
10232     }
10233
10234     case CA_SET_PLAYER_KEYS:
10235     {
10236       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10237       int element = getSpecialActionElement(action_arg_element,
10238                                             action_arg_number, EL_KEY_1);
10239
10240       if (IS_KEY(element))
10241       {
10242         for (i = 0; i < MAX_PLAYERS; i++)
10243         {
10244           if (trigger_player_bits & (1 << i))
10245           {
10246             stored_player[i].key[KEY_NR(element)] = key_state;
10247
10248             DrawGameDoorValues();
10249           }
10250         }
10251       }
10252
10253       break;
10254     }
10255
10256     case CA_SET_PLAYER_SPEED:
10257     {
10258       for (i = 0; i < MAX_PLAYERS; i++)
10259       {
10260         if (trigger_player_bits & (1 << i))
10261         {
10262           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10263
10264           if (action_arg == CA_ARG_SPEED_FASTER &&
10265               stored_player[i].cannot_move)
10266           {
10267             action_arg_number = STEPSIZE_VERY_SLOW;
10268           }
10269           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10270                    action_arg == CA_ARG_SPEED_FASTER)
10271           {
10272             action_arg_number = 2;
10273             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10274                            CA_MODE_MULTIPLY);
10275           }
10276           else if (action_arg == CA_ARG_NUMBER_RESET)
10277           {
10278             action_arg_number = level.initial_player_stepsize[i];
10279           }
10280
10281           move_stepsize =
10282             getModifiedActionNumber(move_stepsize,
10283                                     action_mode,
10284                                     action_arg_number,
10285                                     action_arg_number_min,
10286                                     action_arg_number_max);
10287
10288           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10289         }
10290       }
10291
10292       break;
10293     }
10294
10295     case CA_SET_PLAYER_SHIELD:
10296     {
10297       for (i = 0; i < MAX_PLAYERS; i++)
10298       {
10299         if (trigger_player_bits & (1 << i))
10300         {
10301           if (action_arg == CA_ARG_SHIELD_OFF)
10302           {
10303             stored_player[i].shield_normal_time_left = 0;
10304             stored_player[i].shield_deadly_time_left = 0;
10305           }
10306           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10307           {
10308             stored_player[i].shield_normal_time_left = 999999;
10309           }
10310           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10311           {
10312             stored_player[i].shield_normal_time_left = 999999;
10313             stored_player[i].shield_deadly_time_left = 999999;
10314           }
10315         }
10316       }
10317
10318       break;
10319     }
10320
10321     case CA_SET_PLAYER_GRAVITY:
10322     {
10323       for (i = 0; i < MAX_PLAYERS; i++)
10324       {
10325         if (trigger_player_bits & (1 << i))
10326         {
10327           stored_player[i].gravity =
10328             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10329              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10330              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10331              stored_player[i].gravity);
10332         }
10333       }
10334
10335       break;
10336     }
10337
10338     case CA_SET_PLAYER_ARTWORK:
10339     {
10340       for (i = 0; i < MAX_PLAYERS; i++)
10341       {
10342         if (trigger_player_bits & (1 << i))
10343         {
10344           int artwork_element = action_arg_element;
10345
10346           if (action_arg == CA_ARG_ELEMENT_RESET)
10347             artwork_element =
10348               (level.use_artwork_element[i] ? level.artwork_element[i] :
10349                stored_player[i].element_nr);
10350
10351           if (stored_player[i].artwork_element != artwork_element)
10352             stored_player[i].Frame = 0;
10353
10354           stored_player[i].artwork_element = artwork_element;
10355
10356           SetPlayerWaiting(&stored_player[i], FALSE);
10357
10358           // set number of special actions for bored and sleeping animation
10359           stored_player[i].num_special_action_bored =
10360             get_num_special_action(artwork_element,
10361                                    ACTION_BORING_1, ACTION_BORING_LAST);
10362           stored_player[i].num_special_action_sleeping =
10363             get_num_special_action(artwork_element,
10364                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10365         }
10366       }
10367
10368       break;
10369     }
10370
10371     case CA_SET_PLAYER_INVENTORY:
10372     {
10373       for (i = 0; i < MAX_PLAYERS; i++)
10374       {
10375         struct PlayerInfo *player = &stored_player[i];
10376         int j, k;
10377
10378         if (trigger_player_bits & (1 << i))
10379         {
10380           int inventory_element = action_arg_element;
10381
10382           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10383               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10384               action_arg == CA_ARG_ELEMENT_ACTION)
10385           {
10386             int element = inventory_element;
10387             int collect_count = element_info[element].collect_count_initial;
10388
10389             if (!IS_CUSTOM_ELEMENT(element))
10390               collect_count = 1;
10391
10392             if (collect_count == 0)
10393               player->inventory_infinite_element = element;
10394             else
10395               for (k = 0; k < collect_count; k++)
10396                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10397                   player->inventory_element[player->inventory_size++] =
10398                     element;
10399           }
10400           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10401                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10402                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10403           {
10404             if (player->inventory_infinite_element != EL_UNDEFINED &&
10405                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10406                                      action_arg_element_raw))
10407               player->inventory_infinite_element = EL_UNDEFINED;
10408
10409             for (k = 0, j = 0; j < player->inventory_size; j++)
10410             {
10411               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10412                                         action_arg_element_raw))
10413                 player->inventory_element[k++] = player->inventory_element[j];
10414             }
10415
10416             player->inventory_size = k;
10417           }
10418           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10419           {
10420             if (player->inventory_size > 0)
10421             {
10422               for (j = 0; j < player->inventory_size - 1; j++)
10423                 player->inventory_element[j] = player->inventory_element[j + 1];
10424
10425               player->inventory_size--;
10426             }
10427           }
10428           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10429           {
10430             if (player->inventory_size > 0)
10431               player->inventory_size--;
10432           }
10433           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10434           {
10435             player->inventory_infinite_element = EL_UNDEFINED;
10436             player->inventory_size = 0;
10437           }
10438           else if (action_arg == CA_ARG_INVENTORY_RESET)
10439           {
10440             player->inventory_infinite_element = EL_UNDEFINED;
10441             player->inventory_size = 0;
10442
10443             if (level.use_initial_inventory[i])
10444             {
10445               for (j = 0; j < level.initial_inventory_size[i]; j++)
10446               {
10447                 int element = level.initial_inventory_content[i][j];
10448                 int collect_count = element_info[element].collect_count_initial;
10449
10450                 if (!IS_CUSTOM_ELEMENT(element))
10451                   collect_count = 1;
10452
10453                 if (collect_count == 0)
10454                   player->inventory_infinite_element = element;
10455                 else
10456                   for (k = 0; k < collect_count; k++)
10457                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10458                       player->inventory_element[player->inventory_size++] =
10459                         element;
10460               }
10461             }
10462           }
10463         }
10464       }
10465
10466       break;
10467     }
10468
10469     // ---------- CE actions  -------------------------------------------------
10470
10471     case CA_SET_CE_VALUE:
10472     {
10473       int last_ce_value = CustomValue[x][y];
10474
10475       CustomValue[x][y] = action_arg_number_new;
10476
10477       if (CustomValue[x][y] != last_ce_value)
10478       {
10479         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10480         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10481
10482         if (CustomValue[x][y] == 0)
10483         {
10484           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10485           ChangeCount[x][y] = 0;        // allow at least one more change
10486
10487           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10488           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10489         }
10490       }
10491
10492       break;
10493     }
10494
10495     case CA_SET_CE_SCORE:
10496     {
10497       int last_ce_score = ei->collect_score;
10498
10499       ei->collect_score = action_arg_number_new;
10500
10501       if (ei->collect_score != last_ce_score)
10502       {
10503         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10504         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10505
10506         if (ei->collect_score == 0)
10507         {
10508           int xx, yy;
10509
10510           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10511           ChangeCount[x][y] = 0;        // allow at least one more change
10512
10513           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10514           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10515
10516           /*
10517             This is a very special case that seems to be a mixture between
10518             CheckElementChange() and CheckTriggeredElementChange(): while
10519             the first one only affects single elements that are triggered
10520             directly, the second one affects multiple elements in the playfield
10521             that are triggered indirectly by another element. This is a third
10522             case: Changing the CE score always affects multiple identical CEs,
10523             so every affected CE must be checked, not only the single CE for
10524             which the CE score was changed in the first place (as every instance
10525             of that CE shares the same CE score, and therefore also can change)!
10526           */
10527           SCAN_PLAYFIELD(xx, yy)
10528           {
10529             if (Tile[xx][yy] == element)
10530               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10531                                  CE_SCORE_GETS_ZERO);
10532           }
10533         }
10534       }
10535
10536       break;
10537     }
10538
10539     case CA_SET_CE_ARTWORK:
10540     {
10541       int artwork_element = action_arg_element;
10542       boolean reset_frame = FALSE;
10543       int xx, yy;
10544
10545       if (action_arg == CA_ARG_ELEMENT_RESET)
10546         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10547                            element);
10548
10549       if (ei->gfx_element != artwork_element)
10550         reset_frame = TRUE;
10551
10552       ei->gfx_element = artwork_element;
10553
10554       SCAN_PLAYFIELD(xx, yy)
10555       {
10556         if (Tile[xx][yy] == element)
10557         {
10558           if (reset_frame)
10559           {
10560             ResetGfxAnimation(xx, yy);
10561             ResetRandomAnimationValue(xx, yy);
10562           }
10563
10564           TEST_DrawLevelField(xx, yy);
10565         }
10566       }
10567
10568       break;
10569     }
10570
10571     // ---------- engine actions  ---------------------------------------------
10572
10573     case CA_SET_ENGINE_SCAN_MODE:
10574     {
10575       InitPlayfieldScanMode(action_arg);
10576
10577       break;
10578     }
10579
10580     default:
10581       break;
10582   }
10583 }
10584
10585 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10586 {
10587   int old_element = Tile[x][y];
10588   int new_element = GetElementFromGroupElement(element);
10589   int previous_move_direction = MovDir[x][y];
10590   int last_ce_value = CustomValue[x][y];
10591   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10592   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10593   boolean add_player_onto_element = (new_element_is_player &&
10594                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10595                                      IS_WALKABLE(old_element));
10596
10597   if (!add_player_onto_element)
10598   {
10599     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10600       RemoveMovingField(x, y);
10601     else
10602       RemoveField(x, y);
10603
10604     Tile[x][y] = new_element;
10605
10606     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10607       MovDir[x][y] = previous_move_direction;
10608
10609     if (element_info[new_element].use_last_ce_value)
10610       CustomValue[x][y] = last_ce_value;
10611
10612     InitField_WithBug1(x, y, FALSE);
10613
10614     new_element = Tile[x][y];   // element may have changed
10615
10616     ResetGfxAnimation(x, y);
10617     ResetRandomAnimationValue(x, y);
10618
10619     TEST_DrawLevelField(x, y);
10620
10621     if (GFX_CRUMBLED(new_element))
10622       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10623   }
10624
10625   // check if element under the player changes from accessible to unaccessible
10626   // (needed for special case of dropping element which then changes)
10627   // (must be checked after creating new element for walkable group elements)
10628   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10629       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10630   {
10631     Bang(x, y);
10632
10633     return;
10634   }
10635
10636   // "ChangeCount" not set yet to allow "entered by player" change one time
10637   if (new_element_is_player)
10638     RelocatePlayer(x, y, new_element);
10639
10640   if (is_change)
10641     ChangeCount[x][y]++;        // count number of changes in the same frame
10642
10643   TestIfBadThingTouchesPlayer(x, y);
10644   TestIfPlayerTouchesCustomElement(x, y);
10645   TestIfElementTouchesCustomElement(x, y);
10646 }
10647
10648 static void CreateField(int x, int y, int element)
10649 {
10650   CreateFieldExt(x, y, element, FALSE);
10651 }
10652
10653 static void CreateElementFromChange(int x, int y, int element)
10654 {
10655   element = GET_VALID_RUNTIME_ELEMENT(element);
10656
10657   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10658   {
10659     int old_element = Tile[x][y];
10660
10661     // prevent changed element from moving in same engine frame
10662     // unless both old and new element can either fall or move
10663     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10664         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10665       Stop[x][y] = TRUE;
10666   }
10667
10668   CreateFieldExt(x, y, element, TRUE);
10669 }
10670
10671 static boolean ChangeElement(int x, int y, int element, int page)
10672 {
10673   struct ElementInfo *ei = &element_info[element];
10674   struct ElementChangeInfo *change = &ei->change_page[page];
10675   int ce_value = CustomValue[x][y];
10676   int ce_score = ei->collect_score;
10677   int target_element;
10678   int old_element = Tile[x][y];
10679
10680   // always use default change event to prevent running into a loop
10681   if (ChangeEvent[x][y] == -1)
10682     ChangeEvent[x][y] = CE_DELAY;
10683
10684   if (ChangeEvent[x][y] == CE_DELAY)
10685   {
10686     // reset actual trigger element, trigger player and action element
10687     change->actual_trigger_element = EL_EMPTY;
10688     change->actual_trigger_player = EL_EMPTY;
10689     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10690     change->actual_trigger_side = CH_SIDE_NONE;
10691     change->actual_trigger_ce_value = 0;
10692     change->actual_trigger_ce_score = 0;
10693   }
10694
10695   // do not change elements more than a specified maximum number of changes
10696   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10697     return FALSE;
10698
10699   ChangeCount[x][y]++;          // count number of changes in the same frame
10700
10701   if (change->explode)
10702   {
10703     Bang(x, y);
10704
10705     return TRUE;
10706   }
10707
10708   if (change->use_target_content)
10709   {
10710     boolean complete_replace = TRUE;
10711     boolean can_replace[3][3];
10712     int xx, yy;
10713
10714     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10715     {
10716       boolean is_empty;
10717       boolean is_walkable;
10718       boolean is_diggable;
10719       boolean is_collectible;
10720       boolean is_removable;
10721       boolean is_destructible;
10722       int ex = x + xx - 1;
10723       int ey = y + yy - 1;
10724       int content_element = change->target_content.e[xx][yy];
10725       int e;
10726
10727       can_replace[xx][yy] = TRUE;
10728
10729       if (ex == x && ey == y)   // do not check changing element itself
10730         continue;
10731
10732       if (content_element == EL_EMPTY_SPACE)
10733       {
10734         can_replace[xx][yy] = FALSE;    // do not replace border with space
10735
10736         continue;
10737       }
10738
10739       if (!IN_LEV_FIELD(ex, ey))
10740       {
10741         can_replace[xx][yy] = FALSE;
10742         complete_replace = FALSE;
10743
10744         continue;
10745       }
10746
10747       e = Tile[ex][ey];
10748
10749       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10750         e = MovingOrBlocked2Element(ex, ey);
10751
10752       is_empty = (IS_FREE(ex, ey) ||
10753                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10754
10755       is_walkable     = (is_empty || IS_WALKABLE(e));
10756       is_diggable     = (is_empty || IS_DIGGABLE(e));
10757       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10758       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10759       is_removable    = (is_diggable || is_collectible);
10760
10761       can_replace[xx][yy] =
10762         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10763           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10764           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10765           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10766           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10767           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10768          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10769
10770       if (!can_replace[xx][yy])
10771         complete_replace = FALSE;
10772     }
10773
10774     if (!change->only_if_complete || complete_replace)
10775     {
10776       boolean something_has_changed = FALSE;
10777
10778       if (change->only_if_complete && change->use_random_replace &&
10779           RND(100) < change->random_percentage)
10780         return FALSE;
10781
10782       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10783       {
10784         int ex = x + xx - 1;
10785         int ey = y + yy - 1;
10786         int content_element;
10787
10788         if (can_replace[xx][yy] && (!change->use_random_replace ||
10789                                     RND(100) < change->random_percentage))
10790         {
10791           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10792             RemoveMovingField(ex, ey);
10793
10794           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10795
10796           content_element = change->target_content.e[xx][yy];
10797           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10798                                               ce_value, ce_score);
10799
10800           CreateElementFromChange(ex, ey, target_element);
10801
10802           something_has_changed = TRUE;
10803
10804           // for symmetry reasons, freeze newly created border elements
10805           if (ex != x || ey != y)
10806             Stop[ex][ey] = TRUE;        // no more moving in this frame
10807         }
10808       }
10809
10810       if (something_has_changed)
10811       {
10812         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10813         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10814       }
10815     }
10816   }
10817   else
10818   {
10819     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10820                                         ce_value, ce_score);
10821
10822     if (element == EL_DIAGONAL_GROWING ||
10823         element == EL_DIAGONAL_SHRINKING)
10824     {
10825       target_element = Store[x][y];
10826
10827       Store[x][y] = EL_EMPTY;
10828     }
10829
10830     // special case: element changes to player (and may be kept if walkable)
10831     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10832       CreateElementFromChange(x, y, EL_EMPTY);
10833
10834     CreateElementFromChange(x, y, target_element);
10835
10836     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10837     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10838   }
10839
10840   // this uses direct change before indirect change
10841   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10842
10843   return TRUE;
10844 }
10845
10846 static void HandleElementChange(int x, int y, int page)
10847 {
10848   int element = MovingOrBlocked2Element(x, y);
10849   struct ElementInfo *ei = &element_info[element];
10850   struct ElementChangeInfo *change = &ei->change_page[page];
10851   boolean handle_action_before_change = FALSE;
10852
10853 #ifdef DEBUG
10854   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10855       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10856   {
10857     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10858           x, y, element, element_info[element].token_name);
10859     Debug("game:playing:HandleElementChange", "This should never happen!");
10860   }
10861 #endif
10862
10863   // this can happen with classic bombs on walkable, changing elements
10864   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10865   {
10866     return;
10867   }
10868
10869   if (ChangeDelay[x][y] == 0)           // initialize element change
10870   {
10871     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10872
10873     if (change->can_change)
10874     {
10875       // !!! not clear why graphic animation should be reset at all here !!!
10876       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10877       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10878
10879       /*
10880         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10881
10882         When using an animation frame delay of 1 (this only happens with
10883         "sp_zonk.moving.left/right" in the classic graphics), the default
10884         (non-moving) animation shows wrong animation frames (while the
10885         moving animation, like "sp_zonk.moving.left/right", is correct,
10886         so this graphical bug never shows up with the classic graphics).
10887         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10888         be drawn instead of the correct frames 0,1,2,3. This is caused by
10889         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10890         an element change: First when the change delay ("ChangeDelay[][]")
10891         counter has reached zero after decrementing, then a second time in
10892         the next frame (after "GfxFrame[][]" was already incremented) when
10893         "ChangeDelay[][]" is reset to the initial delay value again.
10894
10895         This causes frame 0 to be drawn twice, while the last frame won't
10896         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10897
10898         As some animations may already be cleverly designed around this bug
10899         (at least the "Snake Bite" snake tail animation does this), it cannot
10900         simply be fixed here without breaking such existing animations.
10901         Unfortunately, it cannot easily be detected if a graphics set was
10902         designed "before" or "after" the bug was fixed. As a workaround,
10903         a new graphics set option "game.graphics_engine_version" was added
10904         to be able to specify the game's major release version for which the
10905         graphics set was designed, which can then be used to decide if the
10906         bugfix should be used (version 4 and above) or not (version 3 or
10907         below, or if no version was specified at all, as with old sets).
10908
10909         (The wrong/fixed animation frames can be tested with the test level set
10910         "test_gfxframe" and level "000", which contains a specially prepared
10911         custom element at level position (x/y) == (11/9) which uses the zonk
10912         animation mentioned above. Using "game.graphics_engine_version: 4"
10913         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10914         This can also be seen from the debug output for this test element.)
10915       */
10916
10917       // when a custom element is about to change (for example by change delay),
10918       // do not reset graphic animation when the custom element is moving
10919       if (game.graphics_engine_version < 4 &&
10920           !IS_MOVING(x, y))
10921       {
10922         ResetGfxAnimation(x, y);
10923         ResetRandomAnimationValue(x, y);
10924       }
10925
10926       if (change->pre_change_function)
10927         change->pre_change_function(x, y);
10928     }
10929   }
10930
10931   ChangeDelay[x][y]--;
10932
10933   if (ChangeDelay[x][y] != 0)           // continue element change
10934   {
10935     if (change->can_change)
10936     {
10937       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10938
10939       if (IS_ANIMATED(graphic))
10940         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10941
10942       if (change->change_function)
10943         change->change_function(x, y);
10944     }
10945   }
10946   else                                  // finish element change
10947   {
10948     if (ChangePage[x][y] != -1)         // remember page from delayed change
10949     {
10950       page = ChangePage[x][y];
10951       ChangePage[x][y] = -1;
10952
10953       change = &ei->change_page[page];
10954     }
10955
10956     if (IS_MOVING(x, y))                // never change a running system ;-)
10957     {
10958       ChangeDelay[x][y] = 1;            // try change after next move step
10959       ChangePage[x][y] = page;          // remember page to use for change
10960
10961       return;
10962     }
10963
10964     // special case: set new level random seed before changing element
10965     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10966       handle_action_before_change = TRUE;
10967
10968     if (change->has_action && handle_action_before_change)
10969       ExecuteCustomElementAction(x, y, element, page);
10970
10971     if (change->can_change)
10972     {
10973       if (ChangeElement(x, y, element, page))
10974       {
10975         if (change->post_change_function)
10976           change->post_change_function(x, y);
10977       }
10978     }
10979
10980     if (change->has_action && !handle_action_before_change)
10981       ExecuteCustomElementAction(x, y, element, page);
10982   }
10983 }
10984
10985 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10986                                               int trigger_element,
10987                                               int trigger_event,
10988                                               int trigger_player,
10989                                               int trigger_side,
10990                                               int trigger_page)
10991 {
10992   boolean change_done_any = FALSE;
10993   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10994   int i;
10995
10996   if (!(trigger_events[trigger_element][trigger_event]))
10997     return FALSE;
10998
10999   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11000
11001   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11002   {
11003     int element = EL_CUSTOM_START + i;
11004     boolean change_done = FALSE;
11005     int p;
11006
11007     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11008         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11009       continue;
11010
11011     for (p = 0; p < element_info[element].num_change_pages; p++)
11012     {
11013       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11014
11015       if (change->can_change_or_has_action &&
11016           change->has_event[trigger_event] &&
11017           change->trigger_side & trigger_side &&
11018           change->trigger_player & trigger_player &&
11019           change->trigger_page & trigger_page_bits &&
11020           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11021       {
11022         change->actual_trigger_element = trigger_element;
11023         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11024         change->actual_trigger_player_bits = trigger_player;
11025         change->actual_trigger_side = trigger_side;
11026         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11027         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11028
11029         if ((change->can_change && !change_done) || change->has_action)
11030         {
11031           int x, y;
11032
11033           SCAN_PLAYFIELD(x, y)
11034           {
11035             if (Tile[x][y] == element)
11036             {
11037               if (change->can_change && !change_done)
11038               {
11039                 // if element already changed in this frame, not only prevent
11040                 // another element change (checked in ChangeElement()), but
11041                 // also prevent additional element actions for this element
11042
11043                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11044                     !level.use_action_after_change_bug)
11045                   continue;
11046
11047                 ChangeDelay[x][y] = 1;
11048                 ChangeEvent[x][y] = trigger_event;
11049
11050                 HandleElementChange(x, y, p);
11051               }
11052               else if (change->has_action)
11053               {
11054                 // if element already changed in this frame, not only prevent
11055                 // another element change (checked in ChangeElement()), but
11056                 // also prevent additional element actions for this element
11057
11058                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11059                     !level.use_action_after_change_bug)
11060                   continue;
11061
11062                 ExecuteCustomElementAction(x, y, element, p);
11063                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11064               }
11065             }
11066           }
11067
11068           if (change->can_change)
11069           {
11070             change_done = TRUE;
11071             change_done_any = TRUE;
11072           }
11073         }
11074       }
11075     }
11076   }
11077
11078   RECURSION_LOOP_DETECTION_END();
11079
11080   return change_done_any;
11081 }
11082
11083 static boolean CheckElementChangeExt(int x, int y,
11084                                      int element,
11085                                      int trigger_element,
11086                                      int trigger_event,
11087                                      int trigger_player,
11088                                      int trigger_side)
11089 {
11090   boolean change_done = FALSE;
11091   int p;
11092
11093   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11094       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11095     return FALSE;
11096
11097   if (Tile[x][y] == EL_BLOCKED)
11098   {
11099     Blocked2Moving(x, y, &x, &y);
11100     element = Tile[x][y];
11101   }
11102
11103   // check if element has already changed or is about to change after moving
11104   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11105        Tile[x][y] != element) ||
11106
11107       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11108        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11109         ChangePage[x][y] != -1)))
11110     return FALSE;
11111
11112   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11113
11114   for (p = 0; p < element_info[element].num_change_pages; p++)
11115   {
11116     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11117
11118     /* check trigger element for all events where the element that is checked
11119        for changing interacts with a directly adjacent element -- this is
11120        different to element changes that affect other elements to change on the
11121        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11122     boolean check_trigger_element =
11123       (trigger_event == CE_NEXT_TO_X ||
11124        trigger_event == CE_TOUCHING_X ||
11125        trigger_event == CE_HITTING_X ||
11126        trigger_event == CE_HIT_BY_X ||
11127        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11128
11129     if (change->can_change_or_has_action &&
11130         change->has_event[trigger_event] &&
11131         change->trigger_side & trigger_side &&
11132         change->trigger_player & trigger_player &&
11133         (!check_trigger_element ||
11134          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11135     {
11136       change->actual_trigger_element = trigger_element;
11137       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11138       change->actual_trigger_player_bits = trigger_player;
11139       change->actual_trigger_side = trigger_side;
11140       change->actual_trigger_ce_value = CustomValue[x][y];
11141       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11142
11143       // special case: trigger element not at (x,y) position for some events
11144       if (check_trigger_element)
11145       {
11146         static struct
11147         {
11148           int dx, dy;
11149         } move_xy[] =
11150           {
11151             {  0,  0 },
11152             { -1,  0 },
11153             { +1,  0 },
11154             {  0,  0 },
11155             {  0, -1 },
11156             {  0,  0 }, { 0, 0 }, { 0, 0 },
11157             {  0, +1 }
11158           };
11159
11160         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11161         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11162
11163         change->actual_trigger_ce_value = CustomValue[xx][yy];
11164         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11165       }
11166
11167       if (change->can_change && !change_done)
11168       {
11169         ChangeDelay[x][y] = 1;
11170         ChangeEvent[x][y] = trigger_event;
11171
11172         HandleElementChange(x, y, p);
11173
11174         change_done = TRUE;
11175       }
11176       else if (change->has_action)
11177       {
11178         ExecuteCustomElementAction(x, y, element, p);
11179         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11180       }
11181     }
11182   }
11183
11184   RECURSION_LOOP_DETECTION_END();
11185
11186   return change_done;
11187 }
11188
11189 static void PlayPlayerSound(struct PlayerInfo *player)
11190 {
11191   int jx = player->jx, jy = player->jy;
11192   int sound_element = player->artwork_element;
11193   int last_action = player->last_action_waiting;
11194   int action = player->action_waiting;
11195
11196   if (player->is_waiting)
11197   {
11198     if (action != last_action)
11199       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11200     else
11201       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11202   }
11203   else
11204   {
11205     if (action != last_action)
11206       StopSound(element_info[sound_element].sound[last_action]);
11207
11208     if (last_action == ACTION_SLEEPING)
11209       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11210   }
11211 }
11212
11213 static void PlayAllPlayersSound(void)
11214 {
11215   int i;
11216
11217   for (i = 0; i < MAX_PLAYERS; i++)
11218     if (stored_player[i].active)
11219       PlayPlayerSound(&stored_player[i]);
11220 }
11221
11222 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11223 {
11224   boolean last_waiting = player->is_waiting;
11225   int move_dir = player->MovDir;
11226
11227   player->dir_waiting = move_dir;
11228   player->last_action_waiting = player->action_waiting;
11229
11230   if (is_waiting)
11231   {
11232     if (!last_waiting)          // not waiting -> waiting
11233     {
11234       player->is_waiting = TRUE;
11235
11236       player->frame_counter_bored =
11237         FrameCounter +
11238         game.player_boring_delay_fixed +
11239         GetSimpleRandom(game.player_boring_delay_random);
11240       player->frame_counter_sleeping =
11241         FrameCounter +
11242         game.player_sleeping_delay_fixed +
11243         GetSimpleRandom(game.player_sleeping_delay_random);
11244
11245       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11246     }
11247
11248     if (game.player_sleeping_delay_fixed +
11249         game.player_sleeping_delay_random > 0 &&
11250         player->anim_delay_counter == 0 &&
11251         player->post_delay_counter == 0 &&
11252         FrameCounter >= player->frame_counter_sleeping)
11253       player->is_sleeping = TRUE;
11254     else if (game.player_boring_delay_fixed +
11255              game.player_boring_delay_random > 0 &&
11256              FrameCounter >= player->frame_counter_bored)
11257       player->is_bored = TRUE;
11258
11259     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11260                               player->is_bored ? ACTION_BORING :
11261                               ACTION_WAITING);
11262
11263     if (player->is_sleeping && player->use_murphy)
11264     {
11265       // special case for sleeping Murphy when leaning against non-free tile
11266
11267       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11268           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11269            !IS_MOVING(player->jx - 1, player->jy)))
11270         move_dir = MV_LEFT;
11271       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11272                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11273                 !IS_MOVING(player->jx + 1, player->jy)))
11274         move_dir = MV_RIGHT;
11275       else
11276         player->is_sleeping = FALSE;
11277
11278       player->dir_waiting = move_dir;
11279     }
11280
11281     if (player->is_sleeping)
11282     {
11283       if (player->num_special_action_sleeping > 0)
11284       {
11285         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11286         {
11287           int last_special_action = player->special_action_sleeping;
11288           int num_special_action = player->num_special_action_sleeping;
11289           int special_action =
11290             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11291              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11292              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11293              last_special_action + 1 : ACTION_SLEEPING);
11294           int special_graphic =
11295             el_act_dir2img(player->artwork_element, special_action, move_dir);
11296
11297           player->anim_delay_counter =
11298             graphic_info[special_graphic].anim_delay_fixed +
11299             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11300           player->post_delay_counter =
11301             graphic_info[special_graphic].post_delay_fixed +
11302             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11303
11304           player->special_action_sleeping = special_action;
11305         }
11306
11307         if (player->anim_delay_counter > 0)
11308         {
11309           player->action_waiting = player->special_action_sleeping;
11310           player->anim_delay_counter--;
11311         }
11312         else if (player->post_delay_counter > 0)
11313         {
11314           player->post_delay_counter--;
11315         }
11316       }
11317     }
11318     else if (player->is_bored)
11319     {
11320       if (player->num_special_action_bored > 0)
11321       {
11322         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11323         {
11324           int special_action =
11325             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11326           int special_graphic =
11327             el_act_dir2img(player->artwork_element, special_action, move_dir);
11328
11329           player->anim_delay_counter =
11330             graphic_info[special_graphic].anim_delay_fixed +
11331             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11332           player->post_delay_counter =
11333             graphic_info[special_graphic].post_delay_fixed +
11334             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11335
11336           player->special_action_bored = special_action;
11337         }
11338
11339         if (player->anim_delay_counter > 0)
11340         {
11341           player->action_waiting = player->special_action_bored;
11342           player->anim_delay_counter--;
11343         }
11344         else if (player->post_delay_counter > 0)
11345         {
11346           player->post_delay_counter--;
11347         }
11348       }
11349     }
11350   }
11351   else if (last_waiting)        // waiting -> not waiting
11352   {
11353     player->is_waiting = FALSE;
11354     player->is_bored = FALSE;
11355     player->is_sleeping = FALSE;
11356
11357     player->frame_counter_bored = -1;
11358     player->frame_counter_sleeping = -1;
11359
11360     player->anim_delay_counter = 0;
11361     player->post_delay_counter = 0;
11362
11363     player->dir_waiting = player->MovDir;
11364     player->action_waiting = ACTION_DEFAULT;
11365
11366     player->special_action_bored = ACTION_DEFAULT;
11367     player->special_action_sleeping = ACTION_DEFAULT;
11368   }
11369 }
11370
11371 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11372 {
11373   if ((!player->is_moving  && player->was_moving) ||
11374       (player->MovPos == 0 && player->was_moving) ||
11375       (player->is_snapping && !player->was_snapping) ||
11376       (player->is_dropping && !player->was_dropping))
11377   {
11378     if (!CheckSaveEngineSnapshotToList())
11379       return;
11380
11381     player->was_moving = FALSE;
11382     player->was_snapping = TRUE;
11383     player->was_dropping = TRUE;
11384   }
11385   else
11386   {
11387     if (player->is_moving)
11388       player->was_moving = TRUE;
11389
11390     if (!player->is_snapping)
11391       player->was_snapping = FALSE;
11392
11393     if (!player->is_dropping)
11394       player->was_dropping = FALSE;
11395   }
11396
11397   static struct MouseActionInfo mouse_action_last = { 0 };
11398   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11399   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11400
11401   if (new_released)
11402     CheckSaveEngineSnapshotToList();
11403
11404   mouse_action_last = mouse_action;
11405 }
11406
11407 static void CheckSingleStepMode(struct PlayerInfo *player)
11408 {
11409   if (tape.single_step && tape.recording && !tape.pausing)
11410   {
11411     // as it is called "single step mode", just return to pause mode when the
11412     // player stopped moving after one tile (or never starts moving at all)
11413     // (reverse logic needed here in case single step mode used in team mode)
11414     if (player->is_moving ||
11415         player->is_pushing ||
11416         player->is_dropping_pressed ||
11417         player->effective_mouse_action.button)
11418       game.enter_single_step_mode = FALSE;
11419   }
11420
11421   CheckSaveEngineSnapshot(player);
11422 }
11423
11424 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11425 {
11426   int left      = player_action & JOY_LEFT;
11427   int right     = player_action & JOY_RIGHT;
11428   int up        = player_action & JOY_UP;
11429   int down      = player_action & JOY_DOWN;
11430   int button1   = player_action & JOY_BUTTON_1;
11431   int button2   = player_action & JOY_BUTTON_2;
11432   int dx        = (left ? -1 : right ? 1 : 0);
11433   int dy        = (up   ? -1 : down  ? 1 : 0);
11434
11435   if (!player->active || tape.pausing)
11436     return 0;
11437
11438   if (player_action)
11439   {
11440     if (button1)
11441       SnapField(player, dx, dy);
11442     else
11443     {
11444       if (button2)
11445         DropElement(player);
11446
11447       MovePlayer(player, dx, dy);
11448     }
11449
11450     CheckSingleStepMode(player);
11451
11452     SetPlayerWaiting(player, FALSE);
11453
11454     return player_action;
11455   }
11456   else
11457   {
11458     // no actions for this player (no input at player's configured device)
11459
11460     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11461     SnapField(player, 0, 0);
11462     CheckGravityMovementWhenNotMoving(player);
11463
11464     if (player->MovPos == 0)
11465       SetPlayerWaiting(player, TRUE);
11466
11467     if (player->MovPos == 0)    // needed for tape.playing
11468       player->is_moving = FALSE;
11469
11470     player->is_dropping = FALSE;
11471     player->is_dropping_pressed = FALSE;
11472     player->drop_pressed_delay = 0;
11473
11474     CheckSingleStepMode(player);
11475
11476     return 0;
11477   }
11478 }
11479
11480 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11481                                          byte *tape_action)
11482 {
11483   if (!tape.use_mouse_actions)
11484     return;
11485
11486   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11487   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11488   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11489 }
11490
11491 static void SetTapeActionFromMouseAction(byte *tape_action,
11492                                          struct MouseActionInfo *mouse_action)
11493 {
11494   if (!tape.use_mouse_actions)
11495     return;
11496
11497   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11498   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11499   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11500 }
11501
11502 static void CheckLevelSolved(void)
11503 {
11504   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11505   {
11506     if (game_em.level_solved &&
11507         !game_em.game_over)                             // game won
11508     {
11509       LevelSolved();
11510
11511       game_em.game_over = TRUE;
11512
11513       game.all_players_gone = TRUE;
11514     }
11515
11516     if (game_em.game_over)                              // game lost
11517       game.all_players_gone = TRUE;
11518   }
11519   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11520   {
11521     if (game_sp.level_solved &&
11522         !game_sp.game_over)                             // game won
11523     {
11524       LevelSolved();
11525
11526       game_sp.game_over = TRUE;
11527
11528       game.all_players_gone = TRUE;
11529     }
11530
11531     if (game_sp.game_over)                              // game lost
11532       game.all_players_gone = TRUE;
11533   }
11534   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11535   {
11536     if (game_mm.level_solved &&
11537         !game_mm.game_over)                             // game won
11538     {
11539       LevelSolved();
11540
11541       game_mm.game_over = TRUE;
11542
11543       game.all_players_gone = TRUE;
11544     }
11545
11546     if (game_mm.game_over)                              // game lost
11547       game.all_players_gone = TRUE;
11548   }
11549 }
11550
11551 static void CheckLevelTime_StepCounter(void)
11552 {
11553   int i;
11554
11555   TimePlayed++;
11556
11557   if (TimeLeft > 0)
11558   {
11559     TimeLeft--;
11560
11561     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11562       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11563
11564     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11565
11566     DisplayGameControlValues();
11567
11568     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11569       for (i = 0; i < MAX_PLAYERS; i++)
11570         KillPlayer(&stored_player[i]);
11571   }
11572   else if (game.no_level_time_limit && !game.all_players_gone)
11573   {
11574     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11575
11576     DisplayGameControlValues();
11577   }
11578 }
11579
11580 static void CheckLevelTime(void)
11581 {
11582   int i;
11583
11584   if (TimeFrames >= FRAMES_PER_SECOND)
11585   {
11586     TimeFrames = 0;
11587     TapeTime++;
11588
11589     for (i = 0; i < MAX_PLAYERS; i++)
11590     {
11591       struct PlayerInfo *player = &stored_player[i];
11592
11593       if (SHIELD_ON(player))
11594       {
11595         player->shield_normal_time_left--;
11596
11597         if (player->shield_deadly_time_left > 0)
11598           player->shield_deadly_time_left--;
11599       }
11600     }
11601
11602     if (!game.LevelSolved && !level.use_step_counter)
11603     {
11604       TimePlayed++;
11605
11606       if (TimeLeft > 0)
11607       {
11608         TimeLeft--;
11609
11610         if (TimeLeft <= 10 && game.time_limit)
11611           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11612
11613         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11614            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11615
11616         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11617
11618         if (!TimeLeft && game.time_limit)
11619         {
11620           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11621             game_em.lev->killed_out_of_time = TRUE;
11622           else
11623             for (i = 0; i < MAX_PLAYERS; i++)
11624               KillPlayer(&stored_player[i]);
11625         }
11626       }
11627       else if (game.no_level_time_limit && !game.all_players_gone)
11628       {
11629         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11630       }
11631
11632       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11633     }
11634
11635     if (tape.recording || tape.playing)
11636       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11637   }
11638
11639   if (tape.recording || tape.playing)
11640     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11641
11642   UpdateAndDisplayGameControlValues();
11643 }
11644
11645 void AdvanceFrameAndPlayerCounters(int player_nr)
11646 {
11647   int i;
11648
11649   // advance frame counters (global frame counter and time frame counter)
11650   FrameCounter++;
11651   TimeFrames++;
11652
11653   // advance player counters (counters for move delay, move animation etc.)
11654   for (i = 0; i < MAX_PLAYERS; i++)
11655   {
11656     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11657     int move_delay_value = stored_player[i].move_delay_value;
11658     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11659
11660     if (!advance_player_counters)       // not all players may be affected
11661       continue;
11662
11663     if (move_frames == 0)       // less than one move per game frame
11664     {
11665       int stepsize = TILEX / move_delay_value;
11666       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11667       int count = (stored_player[i].is_moving ?
11668                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11669
11670       if (count % delay == 0)
11671         move_frames = 1;
11672     }
11673
11674     stored_player[i].Frame += move_frames;
11675
11676     if (stored_player[i].MovPos != 0)
11677       stored_player[i].StepFrame += move_frames;
11678
11679     if (stored_player[i].move_delay > 0)
11680       stored_player[i].move_delay--;
11681
11682     // due to bugs in previous versions, counter must count up, not down
11683     if (stored_player[i].push_delay != -1)
11684       stored_player[i].push_delay++;
11685
11686     if (stored_player[i].drop_delay > 0)
11687       stored_player[i].drop_delay--;
11688
11689     if (stored_player[i].is_dropping_pressed)
11690       stored_player[i].drop_pressed_delay++;
11691   }
11692 }
11693
11694 void AdvanceFrameCounter(void)
11695 {
11696   FrameCounter++;
11697 }
11698
11699 void AdvanceGfxFrame(void)
11700 {
11701   int x, y;
11702
11703   SCAN_PLAYFIELD(x, y)
11704   {
11705     GfxFrame[x][y]++;
11706   }
11707 }
11708
11709 void StartGameActions(boolean init_network_game, boolean record_tape,
11710                       int random_seed)
11711 {
11712   unsigned int new_random_seed = InitRND(random_seed);
11713
11714   if (record_tape)
11715     TapeStartRecording(new_random_seed);
11716
11717   if (setup.auto_pause_on_start && !tape.pausing)
11718     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11719
11720   if (init_network_game)
11721   {
11722     SendToServer_LevelFile();
11723     SendToServer_StartPlaying();
11724
11725     return;
11726   }
11727
11728   InitGame();
11729 }
11730
11731 static void GameActionsExt(void)
11732 {
11733 #if 0
11734   static unsigned int game_frame_delay = 0;
11735 #endif
11736   unsigned int game_frame_delay_value;
11737   byte *recorded_player_action;
11738   byte summarized_player_action = 0;
11739   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11740   int i;
11741
11742   // detect endless loops, caused by custom element programming
11743   if (recursion_loop_detected && recursion_loop_depth == 0)
11744   {
11745     char *message = getStringCat3("Internal Error! Element ",
11746                                   EL_NAME(recursion_loop_element),
11747                                   " caused endless loop! Quit the game?");
11748
11749     Warn("element '%s' caused endless loop in game engine",
11750          EL_NAME(recursion_loop_element));
11751
11752     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11753
11754     recursion_loop_detected = FALSE;    // if game should be continued
11755
11756     free(message);
11757
11758     return;
11759   }
11760
11761   if (game.restart_level)
11762     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11763
11764   CheckLevelSolved();
11765
11766   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11767     GameWon();
11768
11769   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11770     TapeStop();
11771
11772   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11773     return;
11774
11775   game_frame_delay_value =
11776     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11777
11778   if (tape.playing && tape.warp_forward && !tape.pausing)
11779     game_frame_delay_value = 0;
11780
11781   SetVideoFrameDelay(game_frame_delay_value);
11782
11783   // (de)activate virtual buttons depending on current game status
11784   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11785   {
11786     if (game.all_players_gone)  // if no players there to be controlled anymore
11787       SetOverlayActive(FALSE);
11788     else if (!tape.playing)     // if game continues after tape stopped playing
11789       SetOverlayActive(TRUE);
11790   }
11791
11792 #if 0
11793 #if 0
11794   // ---------- main game synchronization point ----------
11795
11796   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11797
11798   Debug("game:playing:skip", "skip == %d", skip);
11799
11800 #else
11801   // ---------- main game synchronization point ----------
11802
11803   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11804 #endif
11805 #endif
11806
11807   if (network_playing && !network_player_action_received)
11808   {
11809     // try to get network player actions in time
11810
11811     // last chance to get network player actions without main loop delay
11812     HandleNetworking();
11813
11814     // game was quit by network peer
11815     if (game_status != GAME_MODE_PLAYING)
11816       return;
11817
11818     // check if network player actions still missing and game still running
11819     if (!network_player_action_received && !checkGameEnded())
11820       return;           // failed to get network player actions in time
11821
11822     // do not yet reset "network_player_action_received" (for tape.pausing)
11823   }
11824
11825   if (tape.pausing)
11826     return;
11827
11828   // at this point we know that we really continue executing the game
11829
11830   network_player_action_received = FALSE;
11831
11832   // when playing tape, read previously recorded player input from tape data
11833   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11834
11835   local_player->effective_mouse_action = local_player->mouse_action;
11836
11837   if (recorded_player_action != NULL)
11838     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11839                                  recorded_player_action);
11840
11841   // TapePlayAction() may return NULL when toggling to "pause before death"
11842   if (tape.pausing)
11843     return;
11844
11845   if (tape.set_centered_player)
11846   {
11847     game.centered_player_nr_next = tape.centered_player_nr_next;
11848     game.set_centered_player = TRUE;
11849   }
11850
11851   for (i = 0; i < MAX_PLAYERS; i++)
11852   {
11853     summarized_player_action |= stored_player[i].action;
11854
11855     if (!network_playing && (game.team_mode || tape.playing))
11856       stored_player[i].effective_action = stored_player[i].action;
11857   }
11858
11859   if (network_playing && !checkGameEnded())
11860     SendToServer_MovePlayer(summarized_player_action);
11861
11862   // summarize all actions at local players mapped input device position
11863   // (this allows using different input devices in single player mode)
11864   if (!network.enabled && !game.team_mode)
11865     stored_player[map_player_action[local_player->index_nr]].effective_action =
11866       summarized_player_action;
11867
11868   // summarize all actions at centered player in local team mode
11869   if (tape.recording &&
11870       setup.team_mode && !network.enabled &&
11871       setup.input_on_focus &&
11872       game.centered_player_nr != -1)
11873   {
11874     for (i = 0; i < MAX_PLAYERS; i++)
11875       stored_player[map_player_action[i]].effective_action =
11876         (i == game.centered_player_nr ? summarized_player_action : 0);
11877   }
11878
11879   if (recorded_player_action != NULL)
11880     for (i = 0; i < MAX_PLAYERS; i++)
11881       stored_player[i].effective_action = recorded_player_action[i];
11882
11883   for (i = 0; i < MAX_PLAYERS; i++)
11884   {
11885     tape_action[i] = stored_player[i].effective_action;
11886
11887     /* (this may happen in the RND game engine if a player was not present on
11888        the playfield on level start, but appeared later from a custom element */
11889     if (setup.team_mode &&
11890         tape.recording &&
11891         tape_action[i] &&
11892         !tape.player_participates[i])
11893       tape.player_participates[i] = TRUE;
11894   }
11895
11896   SetTapeActionFromMouseAction(tape_action,
11897                                &local_player->effective_mouse_action);
11898
11899   // only record actions from input devices, but not programmed actions
11900   if (tape.recording)
11901     TapeRecordAction(tape_action);
11902
11903   // remember if game was played (especially after tape stopped playing)
11904   if (!tape.playing && summarized_player_action && !checkGameFailed())
11905     game.GamePlayed = TRUE;
11906
11907 #if USE_NEW_PLAYER_ASSIGNMENTS
11908   // !!! also map player actions in single player mode !!!
11909   // if (game.team_mode)
11910   if (1)
11911   {
11912     byte mapped_action[MAX_PLAYERS];
11913
11914 #if DEBUG_PLAYER_ACTIONS
11915     for (i = 0; i < MAX_PLAYERS; i++)
11916       DebugContinued("", "%d, ", stored_player[i].effective_action);
11917 #endif
11918
11919     for (i = 0; i < MAX_PLAYERS; i++)
11920       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11921
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       stored_player[i].effective_action = mapped_action[i];
11924
11925 #if DEBUG_PLAYER_ACTIONS
11926     DebugContinued("", "=> ");
11927     for (i = 0; i < MAX_PLAYERS; i++)
11928       DebugContinued("", "%d, ", stored_player[i].effective_action);
11929     DebugContinued("game:playing:player", "\n");
11930 #endif
11931   }
11932 #if DEBUG_PLAYER_ACTIONS
11933   else
11934   {
11935     for (i = 0; i < MAX_PLAYERS; i++)
11936       DebugContinued("", "%d, ", stored_player[i].effective_action);
11937     DebugContinued("game:playing:player", "\n");
11938   }
11939 #endif
11940 #endif
11941
11942   for (i = 0; i < MAX_PLAYERS; i++)
11943   {
11944     // allow engine snapshot in case of changed movement attempt
11945     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11946         (stored_player[i].effective_action & KEY_MOTION))
11947       game.snapshot.changed_action = TRUE;
11948
11949     // allow engine snapshot in case of snapping/dropping attempt
11950     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11951         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11952       game.snapshot.changed_action = TRUE;
11953
11954     game.snapshot.last_action[i] = stored_player[i].effective_action;
11955   }
11956
11957   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11958   {
11959     GameActions_EM_Main();
11960   }
11961   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11962   {
11963     GameActions_SP_Main();
11964   }
11965   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11966   {
11967     GameActions_MM_Main();
11968   }
11969   else
11970   {
11971     GameActions_RND_Main();
11972   }
11973
11974   BlitScreenToBitmap(backbuffer);
11975
11976   CheckLevelSolved();
11977   CheckLevelTime();
11978
11979   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11980
11981   if (global.show_frames_per_second)
11982   {
11983     static unsigned int fps_counter = 0;
11984     static int fps_frames = 0;
11985     unsigned int fps_delay_ms = Counter() - fps_counter;
11986
11987     fps_frames++;
11988
11989     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11990     {
11991       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11992
11993       fps_frames = 0;
11994       fps_counter = Counter();
11995
11996       // always draw FPS to screen after FPS value was updated
11997       redraw_mask |= REDRAW_FPS;
11998     }
11999
12000     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12001     if (GetDrawDeactivationMask() == REDRAW_NONE)
12002       redraw_mask |= REDRAW_FPS;
12003   }
12004 }
12005
12006 static void GameActions_CheckSaveEngineSnapshot(void)
12007 {
12008   if (!game.snapshot.save_snapshot)
12009     return;
12010
12011   // clear flag for saving snapshot _before_ saving snapshot
12012   game.snapshot.save_snapshot = FALSE;
12013
12014   SaveEngineSnapshotToList();
12015 }
12016
12017 void GameActions(void)
12018 {
12019   GameActionsExt();
12020
12021   GameActions_CheckSaveEngineSnapshot();
12022 }
12023
12024 void GameActions_EM_Main(void)
12025 {
12026   byte effective_action[MAX_PLAYERS];
12027   int i;
12028
12029   for (i = 0; i < MAX_PLAYERS; i++)
12030     effective_action[i] = stored_player[i].effective_action;
12031
12032   GameActions_EM(effective_action);
12033 }
12034
12035 void GameActions_SP_Main(void)
12036 {
12037   byte effective_action[MAX_PLAYERS];
12038   int i;
12039
12040   for (i = 0; i < MAX_PLAYERS; i++)
12041     effective_action[i] = stored_player[i].effective_action;
12042
12043   GameActions_SP(effective_action);
12044
12045   for (i = 0; i < MAX_PLAYERS; i++)
12046   {
12047     if (stored_player[i].force_dropping)
12048       stored_player[i].action |= KEY_BUTTON_DROP;
12049
12050     stored_player[i].force_dropping = FALSE;
12051   }
12052 }
12053
12054 void GameActions_MM_Main(void)
12055 {
12056   AdvanceGfxFrame();
12057
12058   GameActions_MM(local_player->effective_mouse_action);
12059 }
12060
12061 void GameActions_RND_Main(void)
12062 {
12063   GameActions_RND();
12064 }
12065
12066 void GameActions_RND(void)
12067 {
12068   static struct MouseActionInfo mouse_action_last = { 0 };
12069   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12070   int magic_wall_x = 0, magic_wall_y = 0;
12071   int i, x, y, element, graphic, last_gfx_frame;
12072
12073   InitPlayfieldScanModeVars();
12074
12075   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12076   {
12077     SCAN_PLAYFIELD(x, y)
12078     {
12079       ChangeCount[x][y] = 0;
12080       ChangeEvent[x][y] = -1;
12081     }
12082   }
12083
12084   if (game.set_centered_player)
12085   {
12086     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12087
12088     // switching to "all players" only possible if all players fit to screen
12089     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12090     {
12091       game.centered_player_nr_next = game.centered_player_nr;
12092       game.set_centered_player = FALSE;
12093     }
12094
12095     // do not switch focus to non-existing (or non-active) player
12096     if (game.centered_player_nr_next >= 0 &&
12097         !stored_player[game.centered_player_nr_next].active)
12098     {
12099       game.centered_player_nr_next = game.centered_player_nr;
12100       game.set_centered_player = FALSE;
12101     }
12102   }
12103
12104   if (game.set_centered_player &&
12105       ScreenMovPos == 0)        // screen currently aligned at tile position
12106   {
12107     int sx, sy;
12108
12109     if (game.centered_player_nr_next == -1)
12110     {
12111       setScreenCenteredToAllPlayers(&sx, &sy);
12112     }
12113     else
12114     {
12115       sx = stored_player[game.centered_player_nr_next].jx;
12116       sy = stored_player[game.centered_player_nr_next].jy;
12117     }
12118
12119     game.centered_player_nr = game.centered_player_nr_next;
12120     game.set_centered_player = FALSE;
12121
12122     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12123     DrawGameDoorValues();
12124   }
12125
12126   // check single step mode (set flag and clear again if any player is active)
12127   game.enter_single_step_mode =
12128     (tape.single_step && tape.recording && !tape.pausing);
12129
12130   for (i = 0; i < MAX_PLAYERS; i++)
12131   {
12132     int actual_player_action = stored_player[i].effective_action;
12133
12134 #if 1
12135     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12136        - rnd_equinox_tetrachloride 048
12137        - rnd_equinox_tetrachloride_ii 096
12138        - rnd_emanuel_schmieg 002
12139        - doctor_sloan_ww 001, 020
12140     */
12141     if (stored_player[i].MovPos == 0)
12142       CheckGravityMovement(&stored_player[i]);
12143 #endif
12144
12145     // overwrite programmed action with tape action
12146     if (stored_player[i].programmed_action)
12147       actual_player_action = stored_player[i].programmed_action;
12148
12149     PlayerActions(&stored_player[i], actual_player_action);
12150
12151     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12152   }
12153
12154   // single step pause mode may already have been toggled by "ScrollPlayer()"
12155   if (game.enter_single_step_mode && !tape.pausing)
12156     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12157
12158   ScrollScreen(NULL, SCROLL_GO_ON);
12159
12160   /* for backwards compatibility, the following code emulates a fixed bug that
12161      occured when pushing elements (causing elements that just made their last
12162      pushing step to already (if possible) make their first falling step in the
12163      same game frame, which is bad); this code is also needed to use the famous
12164      "spring push bug" which is used in older levels and might be wanted to be
12165      used also in newer levels, but in this case the buggy pushing code is only
12166      affecting the "spring" element and no other elements */
12167
12168   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12169   {
12170     for (i = 0; i < MAX_PLAYERS; i++)
12171     {
12172       struct PlayerInfo *player = &stored_player[i];
12173       int x = player->jx;
12174       int y = player->jy;
12175
12176       if (player->active && player->is_pushing && player->is_moving &&
12177           IS_MOVING(x, y) &&
12178           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12179            Tile[x][y] == EL_SPRING))
12180       {
12181         ContinueMoving(x, y);
12182
12183         // continue moving after pushing (this is actually a bug)
12184         if (!IS_MOVING(x, y))
12185           Stop[x][y] = FALSE;
12186       }
12187     }
12188   }
12189
12190   SCAN_PLAYFIELD(x, y)
12191   {
12192     Last[x][y] = Tile[x][y];
12193
12194     ChangeCount[x][y] = 0;
12195     ChangeEvent[x][y] = -1;
12196
12197     // this must be handled before main playfield loop
12198     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12199     {
12200       MovDelay[x][y]--;
12201       if (MovDelay[x][y] <= 0)
12202         RemoveField(x, y);
12203     }
12204
12205     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12206     {
12207       MovDelay[x][y]--;
12208       if (MovDelay[x][y] <= 0)
12209       {
12210         int element = Store[x][y];
12211         int move_direction = MovDir[x][y];
12212         int player_index_bit = Store2[x][y];
12213
12214         Store[x][y] = 0;
12215         Store2[x][y] = 0;
12216
12217         RemoveField(x, y);
12218         TEST_DrawLevelField(x, y);
12219
12220         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12221
12222         if (IS_ENVELOPE(element))
12223           local_player->show_envelope = element;
12224       }
12225     }
12226
12227 #if DEBUG
12228     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12229     {
12230       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12231             x, y);
12232       Debug("game:playing:GameActions_RND", "This should never happen!");
12233
12234       ChangePage[x][y] = -1;
12235     }
12236 #endif
12237
12238     Stop[x][y] = FALSE;
12239     if (WasJustMoving[x][y] > 0)
12240       WasJustMoving[x][y]--;
12241     if (WasJustFalling[x][y] > 0)
12242       WasJustFalling[x][y]--;
12243     if (CheckCollision[x][y] > 0)
12244       CheckCollision[x][y]--;
12245     if (CheckImpact[x][y] > 0)
12246       CheckImpact[x][y]--;
12247
12248     GfxFrame[x][y]++;
12249
12250     /* reset finished pushing action (not done in ContinueMoving() to allow
12251        continuous pushing animation for elements with zero push delay) */
12252     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12253     {
12254       ResetGfxAnimation(x, y);
12255       TEST_DrawLevelField(x, y);
12256     }
12257
12258 #if DEBUG
12259     if (IS_BLOCKED(x, y))
12260     {
12261       int oldx, oldy;
12262
12263       Blocked2Moving(x, y, &oldx, &oldy);
12264       if (!IS_MOVING(oldx, oldy))
12265       {
12266         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12267         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12268         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12269         Debug("game:playing:GameActions_RND", "This should never happen!");
12270       }
12271     }
12272 #endif
12273   }
12274
12275   if (mouse_action.button)
12276   {
12277     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12278     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12279
12280     x = mouse_action.lx;
12281     y = mouse_action.ly;
12282     element = Tile[x][y];
12283
12284     if (new_button)
12285     {
12286       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12287       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12288                                          ch_button);
12289     }
12290
12291     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12292     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12293                                        ch_button);
12294
12295     if (level.use_step_counter)
12296     {
12297       boolean counted_click = FALSE;
12298
12299       // element clicked that can change when clicked/pressed
12300       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12301           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12302            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12303         counted_click = TRUE;
12304
12305       // element clicked that can trigger change when clicked/pressed
12306       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12307           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12308         counted_click = TRUE;
12309
12310       if (new_button && counted_click)
12311         CheckLevelTime_StepCounter();
12312     }
12313   }
12314
12315   SCAN_PLAYFIELD(x, y)
12316   {
12317     element = Tile[x][y];
12318     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12319     last_gfx_frame = GfxFrame[x][y];
12320
12321     if (element == EL_EMPTY)
12322       graphic = el2img(GfxElementEmpty[x][y]);
12323
12324     ResetGfxFrame(x, y);
12325
12326     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12327       DrawLevelGraphicAnimation(x, y, graphic);
12328
12329     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12330         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12331       ResetRandomAnimationValue(x, y);
12332
12333     SetRandomAnimationValue(x, y);
12334
12335     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12336
12337     if (IS_INACTIVE(element))
12338     {
12339       if (IS_ANIMATED(graphic))
12340         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12341
12342       continue;
12343     }
12344
12345     // this may take place after moving, so 'element' may have changed
12346     if (IS_CHANGING(x, y) &&
12347         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12348     {
12349       int page = element_info[element].event_page_nr[CE_DELAY];
12350
12351       HandleElementChange(x, y, page);
12352
12353       element = Tile[x][y];
12354       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12355     }
12356
12357     CheckNextToConditions(x, y);
12358
12359     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12360     {
12361       StartMoving(x, y);
12362
12363       element = Tile[x][y];
12364       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12365
12366       if (IS_ANIMATED(graphic) &&
12367           !IS_MOVING(x, y) &&
12368           !Stop[x][y])
12369         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12370
12371       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12372         TEST_DrawTwinkleOnField(x, y);
12373     }
12374     else if (element == EL_ACID)
12375     {
12376       if (!Stop[x][y])
12377         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12378     }
12379     else if ((element == EL_EXIT_OPEN ||
12380               element == EL_EM_EXIT_OPEN ||
12381               element == EL_SP_EXIT_OPEN ||
12382               element == EL_STEEL_EXIT_OPEN ||
12383               element == EL_EM_STEEL_EXIT_OPEN ||
12384               element == EL_SP_TERMINAL ||
12385               element == EL_SP_TERMINAL_ACTIVE ||
12386               element == EL_EXTRA_TIME ||
12387               element == EL_SHIELD_NORMAL ||
12388               element == EL_SHIELD_DEADLY) &&
12389              IS_ANIMATED(graphic))
12390       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12391     else if (IS_MOVING(x, y))
12392       ContinueMoving(x, y);
12393     else if (IS_ACTIVE_BOMB(element))
12394       CheckDynamite(x, y);
12395     else if (element == EL_AMOEBA_GROWING)
12396       AmoebaGrowing(x, y);
12397     else if (element == EL_AMOEBA_SHRINKING)
12398       AmoebaShrinking(x, y);
12399
12400 #if !USE_NEW_AMOEBA_CODE
12401     else if (IS_AMOEBALIVE(element))
12402       AmoebaReproduce(x, y);
12403 #endif
12404
12405     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12406       Life(x, y);
12407     else if (element == EL_EXIT_CLOSED)
12408       CheckExit(x, y);
12409     else if (element == EL_EM_EXIT_CLOSED)
12410       CheckExitEM(x, y);
12411     else if (element == EL_STEEL_EXIT_CLOSED)
12412       CheckExitSteel(x, y);
12413     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12414       CheckExitSteelEM(x, y);
12415     else if (element == EL_SP_EXIT_CLOSED)
12416       CheckExitSP(x, y);
12417     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12418              element == EL_EXPANDABLE_STEELWALL_GROWING)
12419       WallGrowing(x, y);
12420     else if (element == EL_EXPANDABLE_WALL ||
12421              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12422              element == EL_EXPANDABLE_WALL_VERTICAL ||
12423              element == EL_EXPANDABLE_WALL_ANY ||
12424              element == EL_BD_EXPANDABLE_WALL ||
12425              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12426              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12427              element == EL_EXPANDABLE_STEELWALL_ANY)
12428       CheckWallGrowing(x, y);
12429     else if (element == EL_FLAMES)
12430       CheckForDragon(x, y);
12431     else if (element == EL_EXPLOSION)
12432       ; // drawing of correct explosion animation is handled separately
12433     else if (element == EL_ELEMENT_SNAPPING ||
12434              element == EL_DIAGONAL_SHRINKING ||
12435              element == EL_DIAGONAL_GROWING)
12436     {
12437       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12438
12439       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12440     }
12441     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12443
12444     if (IS_BELT_ACTIVE(element))
12445       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12446
12447     if (game.magic_wall_active)
12448     {
12449       int jx = local_player->jx, jy = local_player->jy;
12450
12451       // play the element sound at the position nearest to the player
12452       if ((element == EL_MAGIC_WALL_FULL ||
12453            element == EL_MAGIC_WALL_ACTIVE ||
12454            element == EL_MAGIC_WALL_EMPTYING ||
12455            element == EL_BD_MAGIC_WALL_FULL ||
12456            element == EL_BD_MAGIC_WALL_ACTIVE ||
12457            element == EL_BD_MAGIC_WALL_EMPTYING ||
12458            element == EL_DC_MAGIC_WALL_FULL ||
12459            element == EL_DC_MAGIC_WALL_ACTIVE ||
12460            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12461           ABS(x - jx) + ABS(y - jy) <
12462           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12463       {
12464         magic_wall_x = x;
12465         magic_wall_y = y;
12466       }
12467     }
12468   }
12469
12470 #if USE_NEW_AMOEBA_CODE
12471   // new experimental amoeba growth stuff
12472   if (!(FrameCounter % 8))
12473   {
12474     static unsigned int random = 1684108901;
12475
12476     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12477     {
12478       x = RND(lev_fieldx);
12479       y = RND(lev_fieldy);
12480       element = Tile[x][y];
12481
12482       if (!IS_PLAYER(x, y) &&
12483           (element == EL_EMPTY ||
12484            CAN_GROW_INTO(element) ||
12485            element == EL_QUICKSAND_EMPTY ||
12486            element == EL_QUICKSAND_FAST_EMPTY ||
12487            element == EL_ACID_SPLASH_LEFT ||
12488            element == EL_ACID_SPLASH_RIGHT))
12489       {
12490         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12491             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12492             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12493             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12494           Tile[x][y] = EL_AMOEBA_DROP;
12495       }
12496
12497       random = random * 129 + 1;
12498     }
12499   }
12500 #endif
12501
12502   game.explosions_delayed = FALSE;
12503
12504   SCAN_PLAYFIELD(x, y)
12505   {
12506     element = Tile[x][y];
12507
12508     if (ExplodeField[x][y])
12509       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12510     else if (element == EL_EXPLOSION)
12511       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12512
12513     ExplodeField[x][y] = EX_TYPE_NONE;
12514   }
12515
12516   game.explosions_delayed = TRUE;
12517
12518   if (game.magic_wall_active)
12519   {
12520     if (!(game.magic_wall_time_left % 4))
12521     {
12522       int element = Tile[magic_wall_x][magic_wall_y];
12523
12524       if (element == EL_BD_MAGIC_WALL_FULL ||
12525           element == EL_BD_MAGIC_WALL_ACTIVE ||
12526           element == EL_BD_MAGIC_WALL_EMPTYING)
12527         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12528       else if (element == EL_DC_MAGIC_WALL_FULL ||
12529                element == EL_DC_MAGIC_WALL_ACTIVE ||
12530                element == EL_DC_MAGIC_WALL_EMPTYING)
12531         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12532       else
12533         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12534     }
12535
12536     if (game.magic_wall_time_left > 0)
12537     {
12538       game.magic_wall_time_left--;
12539
12540       if (!game.magic_wall_time_left)
12541       {
12542         SCAN_PLAYFIELD(x, y)
12543         {
12544           element = Tile[x][y];
12545
12546           if (element == EL_MAGIC_WALL_ACTIVE ||
12547               element == EL_MAGIC_WALL_FULL)
12548           {
12549             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12550             TEST_DrawLevelField(x, y);
12551           }
12552           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12553                    element == EL_BD_MAGIC_WALL_FULL)
12554           {
12555             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12556             TEST_DrawLevelField(x, y);
12557           }
12558           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12559                    element == EL_DC_MAGIC_WALL_FULL)
12560           {
12561             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12562             TEST_DrawLevelField(x, y);
12563           }
12564         }
12565
12566         game.magic_wall_active = FALSE;
12567       }
12568     }
12569   }
12570
12571   if (game.light_time_left > 0)
12572   {
12573     game.light_time_left--;
12574
12575     if (game.light_time_left == 0)
12576       RedrawAllLightSwitchesAndInvisibleElements();
12577   }
12578
12579   if (game.timegate_time_left > 0)
12580   {
12581     game.timegate_time_left--;
12582
12583     if (game.timegate_time_left == 0)
12584       CloseAllOpenTimegates();
12585   }
12586
12587   if (game.lenses_time_left > 0)
12588   {
12589     game.lenses_time_left--;
12590
12591     if (game.lenses_time_left == 0)
12592       RedrawAllInvisibleElementsForLenses();
12593   }
12594
12595   if (game.magnify_time_left > 0)
12596   {
12597     game.magnify_time_left--;
12598
12599     if (game.magnify_time_left == 0)
12600       RedrawAllInvisibleElementsForMagnifier();
12601   }
12602
12603   for (i = 0; i < MAX_PLAYERS; i++)
12604   {
12605     struct PlayerInfo *player = &stored_player[i];
12606
12607     if (SHIELD_ON(player))
12608     {
12609       if (player->shield_deadly_time_left)
12610         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12611       else if (player->shield_normal_time_left)
12612         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12613     }
12614   }
12615
12616 #if USE_DELAYED_GFX_REDRAW
12617   SCAN_PLAYFIELD(x, y)
12618   {
12619     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12620     {
12621       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12622          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12623
12624       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12625         DrawLevelField(x, y);
12626
12627       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12628         DrawLevelFieldCrumbled(x, y);
12629
12630       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12631         DrawLevelFieldCrumbledNeighbours(x, y);
12632
12633       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12634         DrawTwinkleOnField(x, y);
12635     }
12636
12637     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12638   }
12639 #endif
12640
12641   DrawAllPlayers();
12642   PlayAllPlayersSound();
12643
12644   for (i = 0; i < MAX_PLAYERS; i++)
12645   {
12646     struct PlayerInfo *player = &stored_player[i];
12647
12648     if (player->show_envelope != 0 && (!player->active ||
12649                                        player->MovPos == 0))
12650     {
12651       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12652
12653       player->show_envelope = 0;
12654     }
12655   }
12656
12657   // use random number generator in every frame to make it less predictable
12658   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12659     RND(1);
12660
12661   mouse_action_last = mouse_action;
12662 }
12663
12664 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12665 {
12666   int min_x = x, min_y = y, max_x = x, max_y = y;
12667   int scr_fieldx = getScreenFieldSizeX();
12668   int scr_fieldy = getScreenFieldSizeY();
12669   int i;
12670
12671   for (i = 0; i < MAX_PLAYERS; i++)
12672   {
12673     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12674
12675     if (!stored_player[i].active || &stored_player[i] == player)
12676       continue;
12677
12678     min_x = MIN(min_x, jx);
12679     min_y = MIN(min_y, jy);
12680     max_x = MAX(max_x, jx);
12681     max_y = MAX(max_y, jy);
12682   }
12683
12684   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12685 }
12686
12687 static boolean AllPlayersInVisibleScreen(void)
12688 {
12689   int i;
12690
12691   for (i = 0; i < MAX_PLAYERS; i++)
12692   {
12693     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12694
12695     if (!stored_player[i].active)
12696       continue;
12697
12698     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12699       return FALSE;
12700   }
12701
12702   return TRUE;
12703 }
12704
12705 void ScrollLevel(int dx, int dy)
12706 {
12707   int scroll_offset = 2 * TILEX_VAR;
12708   int x, y;
12709
12710   BlitBitmap(drawto_field, drawto_field,
12711              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12712              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12713              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12714              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12715              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12716              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12717
12718   if (dx != 0)
12719   {
12720     x = (dx == 1 ? BX1 : BX2);
12721     for (y = BY1; y <= BY2; y++)
12722       DrawScreenField(x, y);
12723   }
12724
12725   if (dy != 0)
12726   {
12727     y = (dy == 1 ? BY1 : BY2);
12728     for (x = BX1; x <= BX2; x++)
12729       DrawScreenField(x, y);
12730   }
12731
12732   redraw_mask |= REDRAW_FIELD;
12733 }
12734
12735 static boolean canFallDown(struct PlayerInfo *player)
12736 {
12737   int jx = player->jx, jy = player->jy;
12738
12739   return (IN_LEV_FIELD(jx, jy + 1) &&
12740           (IS_FREE(jx, jy + 1) ||
12741            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12742           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12743           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12744 }
12745
12746 static boolean canPassField(int x, int y, int move_dir)
12747 {
12748   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12749   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12750   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12751   int nextx = x + dx;
12752   int nexty = y + dy;
12753   int element = Tile[x][y];
12754
12755   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12756           !CAN_MOVE(element) &&
12757           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12758           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12759           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12760 }
12761
12762 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12763 {
12764   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12765   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12766   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12767   int newx = x + dx;
12768   int newy = y + dy;
12769
12770   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12771           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12772           (IS_DIGGABLE(Tile[newx][newy]) ||
12773            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12774            canPassField(newx, newy, move_dir)));
12775 }
12776
12777 static void CheckGravityMovement(struct PlayerInfo *player)
12778 {
12779   if (player->gravity && !player->programmed_action)
12780   {
12781     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12782     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12783     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12784     int jx = player->jx, jy = player->jy;
12785     boolean player_is_moving_to_valid_field =
12786       (!player_is_snapping &&
12787        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12788         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12789     boolean player_can_fall_down = canFallDown(player);
12790
12791     if (player_can_fall_down &&
12792         !player_is_moving_to_valid_field)
12793       player->programmed_action = MV_DOWN;
12794   }
12795 }
12796
12797 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12798 {
12799   return CheckGravityMovement(player);
12800
12801   if (player->gravity && !player->programmed_action)
12802   {
12803     int jx = player->jx, jy = player->jy;
12804     boolean field_under_player_is_free =
12805       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12806     boolean player_is_standing_on_valid_field =
12807       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12808        (IS_WALKABLE(Tile[jx][jy]) &&
12809         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12810
12811     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12812       player->programmed_action = MV_DOWN;
12813   }
12814 }
12815
12816 /*
12817   MovePlayerOneStep()
12818   -----------------------------------------------------------------------------
12819   dx, dy:               direction (non-diagonal) to try to move the player to
12820   real_dx, real_dy:     direction as read from input device (can be diagonal)
12821 */
12822
12823 boolean MovePlayerOneStep(struct PlayerInfo *player,
12824                           int dx, int dy, int real_dx, int real_dy)
12825 {
12826   int jx = player->jx, jy = player->jy;
12827   int new_jx = jx + dx, new_jy = jy + dy;
12828   int can_move;
12829   boolean player_can_move = !player->cannot_move;
12830
12831   if (!player->active || (!dx && !dy))
12832     return MP_NO_ACTION;
12833
12834   player->MovDir = (dx < 0 ? MV_LEFT :
12835                     dx > 0 ? MV_RIGHT :
12836                     dy < 0 ? MV_UP :
12837                     dy > 0 ? MV_DOWN :  MV_NONE);
12838
12839   if (!IN_LEV_FIELD(new_jx, new_jy))
12840     return MP_NO_ACTION;
12841
12842   if (!player_can_move)
12843   {
12844     if (player->MovPos == 0)
12845     {
12846       player->is_moving = FALSE;
12847       player->is_digging = FALSE;
12848       player->is_collecting = FALSE;
12849       player->is_snapping = FALSE;
12850       player->is_pushing = FALSE;
12851     }
12852   }
12853
12854   if (!network.enabled && game.centered_player_nr == -1 &&
12855       !AllPlayersInSight(player, new_jx, new_jy))
12856     return MP_NO_ACTION;
12857
12858   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12859   if (can_move != MP_MOVING)
12860     return can_move;
12861
12862   // check if DigField() has caused relocation of the player
12863   if (player->jx != jx || player->jy != jy)
12864     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12865
12866   StorePlayer[jx][jy] = 0;
12867   player->last_jx = jx;
12868   player->last_jy = jy;
12869   player->jx = new_jx;
12870   player->jy = new_jy;
12871   StorePlayer[new_jx][new_jy] = player->element_nr;
12872
12873   if (player->move_delay_value_next != -1)
12874   {
12875     player->move_delay_value = player->move_delay_value_next;
12876     player->move_delay_value_next = -1;
12877   }
12878
12879   player->MovPos =
12880     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12881
12882   player->step_counter++;
12883
12884   PlayerVisit[jx][jy] = FrameCounter;
12885
12886   player->is_moving = TRUE;
12887
12888 #if 1
12889   // should better be called in MovePlayer(), but this breaks some tapes
12890   ScrollPlayer(player, SCROLL_INIT);
12891 #endif
12892
12893   return MP_MOVING;
12894 }
12895
12896 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12897 {
12898   int jx = player->jx, jy = player->jy;
12899   int old_jx = jx, old_jy = jy;
12900   int moved = MP_NO_ACTION;
12901
12902   if (!player->active)
12903     return FALSE;
12904
12905   if (!dx && !dy)
12906   {
12907     if (player->MovPos == 0)
12908     {
12909       player->is_moving = FALSE;
12910       player->is_digging = FALSE;
12911       player->is_collecting = FALSE;
12912       player->is_snapping = FALSE;
12913       player->is_pushing = FALSE;
12914     }
12915
12916     return FALSE;
12917   }
12918
12919   if (player->move_delay > 0)
12920     return FALSE;
12921
12922   player->move_delay = -1;              // set to "uninitialized" value
12923
12924   // store if player is automatically moved to next field
12925   player->is_auto_moving = (player->programmed_action != MV_NONE);
12926
12927   // remove the last programmed player action
12928   player->programmed_action = 0;
12929
12930   if (player->MovPos)
12931   {
12932     // should only happen if pre-1.2 tape recordings are played
12933     // this is only for backward compatibility
12934
12935     int original_move_delay_value = player->move_delay_value;
12936
12937 #if DEBUG
12938     Debug("game:playing:MovePlayer",
12939           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12940           tape.counter);
12941 #endif
12942
12943     // scroll remaining steps with finest movement resolution
12944     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12945
12946     while (player->MovPos)
12947     {
12948       ScrollPlayer(player, SCROLL_GO_ON);
12949       ScrollScreen(NULL, SCROLL_GO_ON);
12950
12951       AdvanceFrameAndPlayerCounters(player->index_nr);
12952
12953       DrawAllPlayers();
12954       BackToFront_WithFrameDelay(0);
12955     }
12956
12957     player->move_delay_value = original_move_delay_value;
12958   }
12959
12960   player->is_active = FALSE;
12961
12962   if (player->last_move_dir & MV_HORIZONTAL)
12963   {
12964     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12965       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12966   }
12967   else
12968   {
12969     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12970       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12971   }
12972
12973   if (!moved && !player->is_active)
12974   {
12975     player->is_moving = FALSE;
12976     player->is_digging = FALSE;
12977     player->is_collecting = FALSE;
12978     player->is_snapping = FALSE;
12979     player->is_pushing = FALSE;
12980   }
12981
12982   jx = player->jx;
12983   jy = player->jy;
12984
12985   if (moved & MP_MOVING && !ScreenMovPos &&
12986       (player->index_nr == game.centered_player_nr ||
12987        game.centered_player_nr == -1))
12988   {
12989     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12990
12991     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12992     {
12993       // actual player has left the screen -- scroll in that direction
12994       if (jx != old_jx)         // player has moved horizontally
12995         scroll_x += (jx - old_jx);
12996       else                      // player has moved vertically
12997         scroll_y += (jy - old_jy);
12998     }
12999     else
13000     {
13001       int offset_raw = game.scroll_delay_value;
13002
13003       if (jx != old_jx)         // player has moved horizontally
13004       {
13005         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13006         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13007         int new_scroll_x = jx - MIDPOSX + offset_x;
13008
13009         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13010             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13011           scroll_x = new_scroll_x;
13012
13013         // don't scroll over playfield boundaries
13014         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13015
13016         // don't scroll more than one field at a time
13017         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13018
13019         // don't scroll against the player's moving direction
13020         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13021             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13022           scroll_x = old_scroll_x;
13023       }
13024       else                      // player has moved vertically
13025       {
13026         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13027         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13028         int new_scroll_y = jy - MIDPOSY + offset_y;
13029
13030         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13031             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13032           scroll_y = new_scroll_y;
13033
13034         // don't scroll over playfield boundaries
13035         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13036
13037         // don't scroll more than one field at a time
13038         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13039
13040         // don't scroll against the player's moving direction
13041         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13042             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13043           scroll_y = old_scroll_y;
13044       }
13045     }
13046
13047     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13048     {
13049       if (!network.enabled && game.centered_player_nr == -1 &&
13050           !AllPlayersInVisibleScreen())
13051       {
13052         scroll_x = old_scroll_x;
13053         scroll_y = old_scroll_y;
13054       }
13055       else
13056       {
13057         ScrollScreen(player, SCROLL_INIT);
13058         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13059       }
13060     }
13061   }
13062
13063   player->StepFrame = 0;
13064
13065   if (moved & MP_MOVING)
13066   {
13067     if (old_jx != jx && old_jy == jy)
13068       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13069     else if (old_jx == jx && old_jy != jy)
13070       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13071
13072     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13073
13074     player->last_move_dir = player->MovDir;
13075     player->is_moving = TRUE;
13076     player->is_snapping = FALSE;
13077     player->is_switching = FALSE;
13078     player->is_dropping = FALSE;
13079     player->is_dropping_pressed = FALSE;
13080     player->drop_pressed_delay = 0;
13081
13082 #if 0
13083     // should better be called here than above, but this breaks some tapes
13084     ScrollPlayer(player, SCROLL_INIT);
13085 #endif
13086   }
13087   else
13088   {
13089     CheckGravityMovementWhenNotMoving(player);
13090
13091     player->is_moving = FALSE;
13092
13093     /* at this point, the player is allowed to move, but cannot move right now
13094        (e.g. because of something blocking the way) -- ensure that the player
13095        is also allowed to move in the next frame (in old versions before 3.1.1,
13096        the player was forced to wait again for eight frames before next try) */
13097
13098     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13099       player->move_delay = 0;   // allow direct movement in the next frame
13100   }
13101
13102   if (player->move_delay == -1)         // not yet initialized by DigField()
13103     player->move_delay = player->move_delay_value;
13104
13105   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13106   {
13107     TestIfPlayerTouchesBadThing(jx, jy);
13108     TestIfPlayerTouchesCustomElement(jx, jy);
13109   }
13110
13111   if (!player->active)
13112     RemovePlayer(player);
13113
13114   return moved;
13115 }
13116
13117 void ScrollPlayer(struct PlayerInfo *player, int mode)
13118 {
13119   int jx = player->jx, jy = player->jy;
13120   int last_jx = player->last_jx, last_jy = player->last_jy;
13121   int move_stepsize = TILEX / player->move_delay_value;
13122
13123   if (!player->active)
13124     return;
13125
13126   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13127     return;
13128
13129   if (mode == SCROLL_INIT)
13130   {
13131     player->actual_frame_counter.count = FrameCounter;
13132     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13133
13134     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13135         Tile[last_jx][last_jy] == EL_EMPTY)
13136     {
13137       int last_field_block_delay = 0;   // start with no blocking at all
13138       int block_delay_adjustment = player->block_delay_adjustment;
13139
13140       // if player blocks last field, add delay for exactly one move
13141       if (player->block_last_field)
13142       {
13143         last_field_block_delay += player->move_delay_value;
13144
13145         // when blocking enabled, prevent moving up despite gravity
13146         if (player->gravity && player->MovDir == MV_UP)
13147           block_delay_adjustment = -1;
13148       }
13149
13150       // add block delay adjustment (also possible when not blocking)
13151       last_field_block_delay += block_delay_adjustment;
13152
13153       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13154       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13155     }
13156
13157     if (player->MovPos != 0)    // player has not yet reached destination
13158       return;
13159   }
13160   else if (!FrameReached(&player->actual_frame_counter))
13161     return;
13162
13163   if (player->MovPos != 0)
13164   {
13165     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13166     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13167
13168     // before DrawPlayer() to draw correct player graphic for this case
13169     if (player->MovPos == 0)
13170       CheckGravityMovement(player);
13171   }
13172
13173   if (player->MovPos == 0)      // player reached destination field
13174   {
13175     if (player->move_delay_reset_counter > 0)
13176     {
13177       player->move_delay_reset_counter--;
13178
13179       if (player->move_delay_reset_counter == 0)
13180       {
13181         // continue with normal speed after quickly moving through gate
13182         HALVE_PLAYER_SPEED(player);
13183
13184         // be able to make the next move without delay
13185         player->move_delay = 0;
13186       }
13187     }
13188
13189     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13190         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13191         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13192         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13193         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13194         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13195         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13196         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13197     {
13198       ExitPlayer(player);
13199
13200       if (game.players_still_needed == 0 &&
13201           (game.friends_still_needed == 0 ||
13202            IS_SP_ELEMENT(Tile[jx][jy])))
13203         LevelSolved();
13204     }
13205
13206     player->last_jx = jx;
13207     player->last_jy = jy;
13208
13209     // this breaks one level: "machine", level 000
13210     {
13211       int move_direction = player->MovDir;
13212       int enter_side = MV_DIR_OPPOSITE(move_direction);
13213       int leave_side = move_direction;
13214       int old_jx = last_jx;
13215       int old_jy = last_jy;
13216       int old_element = Tile[old_jx][old_jy];
13217       int new_element = Tile[jx][jy];
13218
13219       if (IS_CUSTOM_ELEMENT(old_element))
13220         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13221                                    CE_LEFT_BY_PLAYER,
13222                                    player->index_bit, leave_side);
13223
13224       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13225                                           CE_PLAYER_LEAVES_X,
13226                                           player->index_bit, leave_side);
13227
13228       if (IS_CUSTOM_ELEMENT(new_element))
13229         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13230                                    player->index_bit, enter_side);
13231
13232       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13233                                           CE_PLAYER_ENTERS_X,
13234                                           player->index_bit, enter_side);
13235
13236       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13237                                         CE_MOVE_OF_X, move_direction);
13238     }
13239
13240     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13241     {
13242       TestIfPlayerTouchesBadThing(jx, jy);
13243       TestIfPlayerTouchesCustomElement(jx, jy);
13244
13245       /* needed because pushed element has not yet reached its destination,
13246          so it would trigger a change event at its previous field location */
13247       if (!player->is_pushing)
13248         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13249
13250       if (level.finish_dig_collect &&
13251           (player->is_digging || player->is_collecting))
13252       {
13253         int last_element = player->last_removed_element;
13254         int move_direction = player->MovDir;
13255         int enter_side = MV_DIR_OPPOSITE(move_direction);
13256         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13257                             CE_PLAYER_COLLECTS_X);
13258
13259         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13260                                             player->index_bit, enter_side);
13261
13262         player->last_removed_element = EL_UNDEFINED;
13263       }
13264
13265       if (!player->active)
13266         RemovePlayer(player);
13267     }
13268
13269     if (level.use_step_counter)
13270       CheckLevelTime_StepCounter();
13271
13272     if (tape.single_step && tape.recording && !tape.pausing &&
13273         !player->programmed_action)
13274       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13275
13276     if (!player->programmed_action)
13277       CheckSaveEngineSnapshot(player);
13278   }
13279 }
13280
13281 void ScrollScreen(struct PlayerInfo *player, int mode)
13282 {
13283   static DelayCounter screen_frame_counter = { 0 };
13284
13285   if (mode == SCROLL_INIT)
13286   {
13287     // set scrolling step size according to actual player's moving speed
13288     ScrollStepSize = TILEX / player->move_delay_value;
13289
13290     screen_frame_counter.count = FrameCounter;
13291     screen_frame_counter.value = 1;
13292
13293     ScreenMovDir = player->MovDir;
13294     ScreenMovPos = player->MovPos;
13295     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13296     return;
13297   }
13298   else if (!FrameReached(&screen_frame_counter))
13299     return;
13300
13301   if (ScreenMovPos)
13302   {
13303     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13304     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13305     redraw_mask |= REDRAW_FIELD;
13306   }
13307   else
13308     ScreenMovDir = MV_NONE;
13309 }
13310
13311 void CheckNextToConditions(int x, int y)
13312 {
13313   int element = Tile[x][y];
13314
13315   if (IS_PLAYER(x, y))
13316     TestIfPlayerNextToCustomElement(x, y);
13317
13318   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13319       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13320     TestIfElementNextToCustomElement(x, y);
13321 }
13322
13323 void TestIfPlayerNextToCustomElement(int x, int y)
13324 {
13325   struct XY *xy = xy_topdown;
13326   static int trigger_sides[4][2] =
13327   {
13328     // center side       border side
13329     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13330     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13331     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13332     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13333   };
13334   int i;
13335
13336   if (!IS_PLAYER(x, y))
13337     return;
13338
13339   struct PlayerInfo *player = PLAYERINFO(x, y);
13340
13341   if (player->is_moving)
13342     return;
13343
13344   for (i = 0; i < NUM_DIRECTIONS; i++)
13345   {
13346     int xx = x + xy[i].x;
13347     int yy = y + xy[i].y;
13348     int border_side = trigger_sides[i][1];
13349     int border_element;
13350
13351     if (!IN_LEV_FIELD(xx, yy))
13352       continue;
13353
13354     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13355       continue;         // center and border element not connected
13356
13357     border_element = Tile[xx][yy];
13358
13359     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13360                                player->index_bit, border_side);
13361     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13362                                         CE_PLAYER_NEXT_TO_X,
13363                                         player->index_bit, border_side);
13364
13365     /* use player element that is initially defined in the level playfield,
13366        not the player element that corresponds to the runtime player number
13367        (example: a level that contains EL_PLAYER_3 as the only player would
13368        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13369
13370     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13371                              CE_NEXT_TO_X, border_side);
13372   }
13373 }
13374
13375 void TestIfPlayerTouchesCustomElement(int x, int y)
13376 {
13377   struct XY *xy = xy_topdown;
13378   static int trigger_sides[4][2] =
13379   {
13380     // center side       border side
13381     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13382     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13383     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13384     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13385   };
13386   static int touch_dir[4] =
13387   {
13388     MV_LEFT | MV_RIGHT,
13389     MV_UP   | MV_DOWN,
13390     MV_UP   | MV_DOWN,
13391     MV_LEFT | MV_RIGHT
13392   };
13393   int center_element = Tile[x][y];      // should always be non-moving!
13394   int i;
13395
13396   for (i = 0; i < NUM_DIRECTIONS; i++)
13397   {
13398     int xx = x + xy[i].x;
13399     int yy = y + xy[i].y;
13400     int center_side = trigger_sides[i][0];
13401     int border_side = trigger_sides[i][1];
13402     int border_element;
13403
13404     if (!IN_LEV_FIELD(xx, yy))
13405       continue;
13406
13407     if (IS_PLAYER(x, y))                // player found at center element
13408     {
13409       struct PlayerInfo *player = PLAYERINFO(x, y);
13410
13411       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13412         border_element = Tile[xx][yy];          // may be moving!
13413       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13414         border_element = Tile[xx][yy];
13415       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13416         border_element = MovingOrBlocked2Element(xx, yy);
13417       else
13418         continue;               // center and border element do not touch
13419
13420       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13421                                  player->index_bit, border_side);
13422       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13423                                           CE_PLAYER_TOUCHES_X,
13424                                           player->index_bit, border_side);
13425
13426       {
13427         /* use player element that is initially defined in the level playfield,
13428            not the player element that corresponds to the runtime player number
13429            (example: a level that contains EL_PLAYER_3 as the only player would
13430            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13431         int player_element = PLAYERINFO(x, y)->initial_element;
13432
13433         CheckElementChangeBySide(xx, yy, border_element, player_element,
13434                                  CE_TOUCHING_X, border_side);
13435       }
13436     }
13437     else if (IS_PLAYER(xx, yy))         // player found at border element
13438     {
13439       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13440
13441       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13442       {
13443         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13444           continue;             // center and border element do not touch
13445       }
13446
13447       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13448                                  player->index_bit, center_side);
13449       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13450                                           CE_PLAYER_TOUCHES_X,
13451                                           player->index_bit, center_side);
13452
13453       {
13454         /* use player element that is initially defined in the level playfield,
13455            not the player element that corresponds to the runtime player number
13456            (example: a level that contains EL_PLAYER_3 as the only player would
13457            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13458         int player_element = PLAYERINFO(xx, yy)->initial_element;
13459
13460         CheckElementChangeBySide(x, y, center_element, player_element,
13461                                  CE_TOUCHING_X, center_side);
13462       }
13463
13464       break;
13465     }
13466   }
13467 }
13468
13469 void TestIfElementNextToCustomElement(int x, int y)
13470 {
13471   struct XY *xy = xy_topdown;
13472   static int trigger_sides[4][2] =
13473   {
13474     // center side      border side
13475     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13476     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13477     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13478     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13479   };
13480   int center_element = Tile[x][y];      // should always be non-moving!
13481   int i;
13482
13483   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13484     return;
13485
13486   for (i = 0; i < NUM_DIRECTIONS; i++)
13487   {
13488     int xx = x + xy[i].x;
13489     int yy = y + xy[i].y;
13490     int border_side = trigger_sides[i][1];
13491     int border_element;
13492
13493     if (!IN_LEV_FIELD(xx, yy))
13494       continue;
13495
13496     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13497       continue;                 // center and border element not connected
13498
13499     border_element = Tile[xx][yy];
13500
13501     // check for change of center element (but change it only once)
13502     if (CheckElementChangeBySide(x, y, center_element, border_element,
13503                                  CE_NEXT_TO_X, border_side))
13504       break;
13505   }
13506 }
13507
13508 void TestIfElementTouchesCustomElement(int x, int y)
13509 {
13510   struct XY *xy = xy_topdown;
13511   static int trigger_sides[4][2] =
13512   {
13513     // center side      border side
13514     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13515     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13516     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13517     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13518   };
13519   static int touch_dir[4] =
13520   {
13521     MV_LEFT | MV_RIGHT,
13522     MV_UP   | MV_DOWN,
13523     MV_UP   | MV_DOWN,
13524     MV_LEFT | MV_RIGHT
13525   };
13526   boolean change_center_element = FALSE;
13527   int center_element = Tile[x][y];      // should always be non-moving!
13528   int border_element_old[NUM_DIRECTIONS];
13529   int i;
13530
13531   for (i = 0; i < NUM_DIRECTIONS; i++)
13532   {
13533     int xx = x + xy[i].x;
13534     int yy = y + xy[i].y;
13535     int border_element;
13536
13537     border_element_old[i] = -1;
13538
13539     if (!IN_LEV_FIELD(xx, yy))
13540       continue;
13541
13542     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13543       border_element = Tile[xx][yy];    // may be moving!
13544     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13545       border_element = Tile[xx][yy];
13546     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13547       border_element = MovingOrBlocked2Element(xx, yy);
13548     else
13549       continue;                 // center and border element do not touch
13550
13551     border_element_old[i] = border_element;
13552   }
13553
13554   for (i = 0; i < NUM_DIRECTIONS; i++)
13555   {
13556     int xx = x + xy[i].x;
13557     int yy = y + xy[i].y;
13558     int center_side = trigger_sides[i][0];
13559     int border_element = border_element_old[i];
13560
13561     if (border_element == -1)
13562       continue;
13563
13564     // check for change of border element
13565     CheckElementChangeBySide(xx, yy, border_element, center_element,
13566                              CE_TOUCHING_X, center_side);
13567
13568     // (center element cannot be player, so we dont have to check this here)
13569   }
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i].x;
13574     int yy = y + xy[i].y;
13575     int border_side = trigger_sides[i][1];
13576     int border_element = border_element_old[i];
13577
13578     if (border_element == -1)
13579       continue;
13580
13581     // check for change of center element (but change it only once)
13582     if (!change_center_element)
13583       change_center_element =
13584         CheckElementChangeBySide(x, y, center_element, border_element,
13585                                  CE_TOUCHING_X, border_side);
13586
13587     if (IS_PLAYER(xx, yy))
13588     {
13589       /* use player element that is initially defined in the level playfield,
13590          not the player element that corresponds to the runtime player number
13591          (example: a level that contains EL_PLAYER_3 as the only player would
13592          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13593       int player_element = PLAYERINFO(xx, yy)->initial_element;
13594
13595       CheckElementChangeBySide(x, y, center_element, player_element,
13596                                CE_TOUCHING_X, border_side);
13597     }
13598   }
13599 }
13600
13601 void TestIfElementHitsCustomElement(int x, int y, int direction)
13602 {
13603   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13604   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13605   int hitx = x + dx, hity = y + dy;
13606   int hitting_element = Tile[x][y];
13607   int touched_element;
13608
13609   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13610     return;
13611
13612   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13613                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13614
13615   if (IN_LEV_FIELD(hitx, hity))
13616   {
13617     int opposite_direction = MV_DIR_OPPOSITE(direction);
13618     int hitting_side = direction;
13619     int touched_side = opposite_direction;
13620     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13621                           MovDir[hitx][hity] != direction ||
13622                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13623
13624     object_hit = TRUE;
13625
13626     if (object_hit)
13627     {
13628       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13629                                CE_HITTING_X, touched_side);
13630
13631       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13632                                CE_HIT_BY_X, hitting_side);
13633
13634       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13635                                CE_HIT_BY_SOMETHING, opposite_direction);
13636
13637       if (IS_PLAYER(hitx, hity))
13638       {
13639         /* use player element that is initially defined in the level playfield,
13640            not the player element that corresponds to the runtime player number
13641            (example: a level that contains EL_PLAYER_3 as the only player would
13642            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13643         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13644
13645         CheckElementChangeBySide(x, y, hitting_element, player_element,
13646                                  CE_HITTING_X, touched_side);
13647       }
13648     }
13649   }
13650
13651   // "hitting something" is also true when hitting the playfield border
13652   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13653                            CE_HITTING_SOMETHING, direction);
13654 }
13655
13656 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13657 {
13658   int i, kill_x = -1, kill_y = -1;
13659
13660   int bad_element = -1;
13661   struct XY *test_xy = xy_topdown;
13662   static int test_dir[4] =
13663   {
13664     MV_UP,
13665     MV_LEFT,
13666     MV_RIGHT,
13667     MV_DOWN
13668   };
13669
13670   for (i = 0; i < NUM_DIRECTIONS; i++)
13671   {
13672     int test_x, test_y, test_move_dir, test_element;
13673
13674     test_x = good_x + test_xy[i].x;
13675     test_y = good_y + test_xy[i].y;
13676
13677     if (!IN_LEV_FIELD(test_x, test_y))
13678       continue;
13679
13680     test_move_dir =
13681       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13682
13683     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13684
13685     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13686        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13687     */
13688     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13689         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13690     {
13691       kill_x = test_x;
13692       kill_y = test_y;
13693       bad_element = test_element;
13694
13695       break;
13696     }
13697   }
13698
13699   if (kill_x != -1 || kill_y != -1)
13700   {
13701     if (IS_PLAYER(good_x, good_y))
13702     {
13703       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13704
13705       if (player->shield_deadly_time_left > 0 &&
13706           !IS_INDESTRUCTIBLE(bad_element))
13707         Bang(kill_x, kill_y);
13708       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13709         KillPlayer(player);
13710     }
13711     else
13712       Bang(good_x, good_y);
13713   }
13714 }
13715
13716 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13717 {
13718   int i, kill_x = -1, kill_y = -1;
13719   int bad_element = Tile[bad_x][bad_y];
13720   struct XY *test_xy = xy_topdown;
13721   static int touch_dir[4] =
13722   {
13723     MV_LEFT | MV_RIGHT,
13724     MV_UP   | MV_DOWN,
13725     MV_UP   | MV_DOWN,
13726     MV_LEFT | MV_RIGHT
13727   };
13728   static int test_dir[4] =
13729   {
13730     MV_UP,
13731     MV_LEFT,
13732     MV_RIGHT,
13733     MV_DOWN
13734   };
13735
13736   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13737     return;
13738
13739   for (i = 0; i < NUM_DIRECTIONS; i++)
13740   {
13741     int test_x, test_y, test_move_dir, test_element;
13742
13743     test_x = bad_x + test_xy[i].x;
13744     test_y = bad_y + test_xy[i].y;
13745
13746     if (!IN_LEV_FIELD(test_x, test_y))
13747       continue;
13748
13749     test_move_dir =
13750       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13751
13752     test_element = Tile[test_x][test_y];
13753
13754     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13755        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13756     */
13757     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13758         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13759     {
13760       // good thing is player or penguin that does not move away
13761       if (IS_PLAYER(test_x, test_y))
13762       {
13763         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13764
13765         if (bad_element == EL_ROBOT && player->is_moving)
13766           continue;     // robot does not kill player if he is moving
13767
13768         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13769         {
13770           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13771             continue;           // center and border element do not touch
13772         }
13773
13774         kill_x = test_x;
13775         kill_y = test_y;
13776
13777         break;
13778       }
13779       else if (test_element == EL_PENGUIN)
13780       {
13781         kill_x = test_x;
13782         kill_y = test_y;
13783
13784         break;
13785       }
13786     }
13787   }
13788
13789   if (kill_x != -1 || kill_y != -1)
13790   {
13791     if (IS_PLAYER(kill_x, kill_y))
13792     {
13793       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13794
13795       if (player->shield_deadly_time_left > 0 &&
13796           !IS_INDESTRUCTIBLE(bad_element))
13797         Bang(bad_x, bad_y);
13798       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13799         KillPlayer(player);
13800     }
13801     else
13802       Bang(kill_x, kill_y);
13803   }
13804 }
13805
13806 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13807 {
13808   int bad_element = Tile[bad_x][bad_y];
13809   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13810   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13811   int test_x = bad_x + dx, test_y = bad_y + dy;
13812   int test_move_dir, test_element;
13813   int kill_x = -1, kill_y = -1;
13814
13815   if (!IN_LEV_FIELD(test_x, test_y))
13816     return;
13817
13818   test_move_dir =
13819     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13820
13821   test_element = Tile[test_x][test_y];
13822
13823   if (test_move_dir != bad_move_dir)
13824   {
13825     // good thing can be player or penguin that does not move away
13826     if (IS_PLAYER(test_x, test_y))
13827     {
13828       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13829
13830       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13831          player as being hit when he is moving towards the bad thing, because
13832          the "get hit by" condition would be lost after the player stops) */
13833       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13834         return;         // player moves away from bad thing
13835
13836       kill_x = test_x;
13837       kill_y = test_y;
13838     }
13839     else if (test_element == EL_PENGUIN)
13840     {
13841       kill_x = test_x;
13842       kill_y = test_y;
13843     }
13844   }
13845
13846   if (kill_x != -1 || kill_y != -1)
13847   {
13848     if (IS_PLAYER(kill_x, kill_y))
13849     {
13850       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13851
13852       if (player->shield_deadly_time_left > 0 &&
13853           !IS_INDESTRUCTIBLE(bad_element))
13854         Bang(bad_x, bad_y);
13855       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13856         KillPlayer(player);
13857     }
13858     else
13859       Bang(kill_x, kill_y);
13860   }
13861 }
13862
13863 void TestIfPlayerTouchesBadThing(int x, int y)
13864 {
13865   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13866 }
13867
13868 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13869 {
13870   TestIfGoodThingHitsBadThing(x, y, move_dir);
13871 }
13872
13873 void TestIfBadThingTouchesPlayer(int x, int y)
13874 {
13875   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13876 }
13877
13878 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13879 {
13880   TestIfBadThingHitsGoodThing(x, y, move_dir);
13881 }
13882
13883 void TestIfFriendTouchesBadThing(int x, int y)
13884 {
13885   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13886 }
13887
13888 void TestIfBadThingTouchesFriend(int x, int y)
13889 {
13890   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13891 }
13892
13893 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13894 {
13895   int i, kill_x = bad_x, kill_y = bad_y;
13896   struct XY *xy = xy_topdown;
13897
13898   for (i = 0; i < NUM_DIRECTIONS; i++)
13899   {
13900     int x, y, element;
13901
13902     x = bad_x + xy[i].x;
13903     y = bad_y + xy[i].y;
13904     if (!IN_LEV_FIELD(x, y))
13905       continue;
13906
13907     element = Tile[x][y];
13908     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13909         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13910     {
13911       kill_x = x;
13912       kill_y = y;
13913       break;
13914     }
13915   }
13916
13917   if (kill_x != bad_x || kill_y != bad_y)
13918     Bang(bad_x, bad_y);
13919 }
13920
13921 void KillPlayer(struct PlayerInfo *player)
13922 {
13923   int jx = player->jx, jy = player->jy;
13924
13925   if (!player->active)
13926     return;
13927
13928 #if 0
13929   Debug("game:playing:KillPlayer",
13930         "0: killed == %d, active == %d, reanimated == %d",
13931         player->killed, player->active, player->reanimated);
13932 #endif
13933
13934   /* the following code was introduced to prevent an infinite loop when calling
13935      -> Bang()
13936      -> CheckTriggeredElementChangeExt()
13937      -> ExecuteCustomElementAction()
13938      -> KillPlayer()
13939      -> (infinitely repeating the above sequence of function calls)
13940      which occurs when killing the player while having a CE with the setting
13941      "kill player X when explosion of <player X>"; the solution using a new
13942      field "player->killed" was chosen for backwards compatibility, although
13943      clever use of the fields "player->active" etc. would probably also work */
13944 #if 1
13945   if (player->killed)
13946     return;
13947 #endif
13948
13949   player->killed = TRUE;
13950
13951   // remove accessible field at the player's position
13952   RemoveField(jx, jy);
13953
13954   // deactivate shield (else Bang()/Explode() would not work right)
13955   player->shield_normal_time_left = 0;
13956   player->shield_deadly_time_left = 0;
13957
13958 #if 0
13959   Debug("game:playing:KillPlayer",
13960         "1: killed == %d, active == %d, reanimated == %d",
13961         player->killed, player->active, player->reanimated);
13962 #endif
13963
13964   Bang(jx, jy);
13965
13966 #if 0
13967   Debug("game:playing:KillPlayer",
13968         "2: killed == %d, active == %d, reanimated == %d",
13969         player->killed, player->active, player->reanimated);
13970 #endif
13971
13972   if (player->reanimated)       // killed player may have been reanimated
13973     player->killed = player->reanimated = FALSE;
13974   else
13975     BuryPlayer(player);
13976 }
13977
13978 static void KillPlayerUnlessEnemyProtected(int x, int y)
13979 {
13980   if (!PLAYER_ENEMY_PROTECTED(x, y))
13981     KillPlayer(PLAYERINFO(x, y));
13982 }
13983
13984 static void KillPlayerUnlessExplosionProtected(int x, int y)
13985 {
13986   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13987     KillPlayer(PLAYERINFO(x, y));
13988 }
13989
13990 void BuryPlayer(struct PlayerInfo *player)
13991 {
13992   int jx = player->jx, jy = player->jy;
13993
13994   if (!player->active)
13995     return;
13996
13997   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13998
13999   RemovePlayer(player);
14000
14001   player->buried = TRUE;
14002
14003   if (game.all_players_gone)
14004     game.GameOver = TRUE;
14005 }
14006
14007 void RemovePlayer(struct PlayerInfo *player)
14008 {
14009   int jx = player->jx, jy = player->jy;
14010   int i, found = FALSE;
14011
14012   player->present = FALSE;
14013   player->active = FALSE;
14014
14015   // required for some CE actions (even if the player is not active anymore)
14016   player->MovPos = 0;
14017
14018   if (!ExplodeField[jx][jy])
14019     StorePlayer[jx][jy] = 0;
14020
14021   if (player->is_moving)
14022     TEST_DrawLevelField(player->last_jx, player->last_jy);
14023
14024   for (i = 0; i < MAX_PLAYERS; i++)
14025     if (stored_player[i].active)
14026       found = TRUE;
14027
14028   if (!found)
14029   {
14030     game.all_players_gone = TRUE;
14031     game.GameOver = TRUE;
14032   }
14033
14034   game.exit_x = game.robot_wheel_x = jx;
14035   game.exit_y = game.robot_wheel_y = jy;
14036 }
14037
14038 void ExitPlayer(struct PlayerInfo *player)
14039 {
14040   DrawPlayer(player);   // needed here only to cleanup last field
14041   RemovePlayer(player);
14042
14043   if (game.players_still_needed > 0)
14044     game.players_still_needed--;
14045 }
14046
14047 static void SetFieldForSnapping(int x, int y, int element, int direction,
14048                                 int player_index_bit)
14049 {
14050   struct ElementInfo *ei = &element_info[element];
14051   int direction_bit = MV_DIR_TO_BIT(direction);
14052   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14053   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14054                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14055
14056   Tile[x][y] = EL_ELEMENT_SNAPPING;
14057   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14058   MovDir[x][y] = direction;
14059   Store[x][y] = element;
14060   Store2[x][y] = player_index_bit;
14061
14062   ResetGfxAnimation(x, y);
14063
14064   GfxElement[x][y] = element;
14065   GfxAction[x][y] = action;
14066   GfxDir[x][y] = direction;
14067   GfxFrame[x][y] = -1;
14068 }
14069
14070 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14071                                    int player_index_bit)
14072 {
14073   TestIfElementTouchesCustomElement(x, y);      // for empty space
14074
14075   if (level.finish_dig_collect)
14076   {
14077     int dig_side = MV_DIR_OPPOSITE(direction);
14078     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14079                         CE_PLAYER_COLLECTS_X);
14080
14081     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14082                                         player_index_bit, dig_side);
14083     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14084                                         player_index_bit, dig_side);
14085   }
14086 }
14087
14088 /*
14089   =============================================================================
14090   checkDiagonalPushing()
14091   -----------------------------------------------------------------------------
14092   check if diagonal input device direction results in pushing of object
14093   (by checking if the alternative direction is walkable, diggable, ...)
14094   =============================================================================
14095 */
14096
14097 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14098                                     int x, int y, int real_dx, int real_dy)
14099 {
14100   int jx, jy, dx, dy, xx, yy;
14101
14102   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14103     return TRUE;
14104
14105   // diagonal direction: check alternative direction
14106   jx = player->jx;
14107   jy = player->jy;
14108   dx = x - jx;
14109   dy = y - jy;
14110   xx = jx + (dx == 0 ? real_dx : 0);
14111   yy = jy + (dy == 0 ? real_dy : 0);
14112
14113   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14114 }
14115
14116 /*
14117   =============================================================================
14118   DigField()
14119   -----------------------------------------------------------------------------
14120   x, y:                 field next to player (non-diagonal) to try to dig to
14121   real_dx, real_dy:     direction as read from input device (can be diagonal)
14122   =============================================================================
14123 */
14124
14125 static int DigField(struct PlayerInfo *player,
14126                     int oldx, int oldy, int x, int y,
14127                     int real_dx, int real_dy, int mode)
14128 {
14129   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14130   boolean player_was_pushing = player->is_pushing;
14131   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14132   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14133   int jx = oldx, jy = oldy;
14134   int dx = x - jx, dy = y - jy;
14135   int nextx = x + dx, nexty = y + dy;
14136   int move_direction = (dx == -1 ? MV_LEFT  :
14137                         dx == +1 ? MV_RIGHT :
14138                         dy == -1 ? MV_UP    :
14139                         dy == +1 ? MV_DOWN  : MV_NONE);
14140   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14141   int dig_side = MV_DIR_OPPOSITE(move_direction);
14142   int old_element = Tile[jx][jy];
14143   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14144   int collect_count;
14145
14146   if (is_player)                // function can also be called by EL_PENGUIN
14147   {
14148     if (player->MovPos == 0)
14149     {
14150       player->is_digging = FALSE;
14151       player->is_collecting = FALSE;
14152     }
14153
14154     if (player->MovPos == 0)    // last pushing move finished
14155       player->is_pushing = FALSE;
14156
14157     if (mode == DF_NO_PUSH)     // player just stopped pushing
14158     {
14159       player->is_switching = FALSE;
14160       player->push_delay = -1;
14161
14162       return MP_NO_ACTION;
14163     }
14164   }
14165   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14166     old_element = Back[jx][jy];
14167
14168   // in case of element dropped at player position, check background
14169   else if (Back[jx][jy] != EL_EMPTY &&
14170            game.engine_version >= VERSION_IDENT(2,2,0,0))
14171     old_element = Back[jx][jy];
14172
14173   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14174     return MP_NO_ACTION;        // field has no opening in this direction
14175
14176   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14177     return MP_NO_ACTION;        // field has no opening in this direction
14178
14179   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14180   {
14181     SplashAcid(x, y);
14182
14183     Tile[jx][jy] = player->artwork_element;
14184     InitMovingField(jx, jy, MV_DOWN);
14185     Store[jx][jy] = EL_ACID;
14186     ContinueMoving(jx, jy);
14187     BuryPlayer(player);
14188
14189     return MP_DONT_RUN_INTO;
14190   }
14191
14192   if (player_can_move && DONT_RUN_INTO(element))
14193   {
14194     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14195
14196     return MP_DONT_RUN_INTO;
14197   }
14198
14199   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14200     return MP_NO_ACTION;
14201
14202   collect_count = element_info[element].collect_count_initial;
14203
14204   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14205     return MP_NO_ACTION;
14206
14207   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14208     player_can_move = player_can_move_or_snap;
14209
14210   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14211       game.engine_version >= VERSION_IDENT(2,2,0,0))
14212   {
14213     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14214                                player->index_bit, dig_side);
14215     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14216                                         player->index_bit, dig_side);
14217
14218     if (element == EL_DC_LANDMINE)
14219       Bang(x, y);
14220
14221     if (Tile[x][y] != element)          // field changed by snapping
14222       return MP_ACTION;
14223
14224     return MP_NO_ACTION;
14225   }
14226
14227   if (player->gravity && is_player && !player->is_auto_moving &&
14228       canFallDown(player) && move_direction != MV_DOWN &&
14229       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14230     return MP_NO_ACTION;        // player cannot walk here due to gravity
14231
14232   if (player_can_move &&
14233       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14234   {
14235     int sound_element = SND_ELEMENT(element);
14236     int sound_action = ACTION_WALKING;
14237
14238     if (IS_RND_GATE(element))
14239     {
14240       if (!player->key[RND_GATE_NR(element)])
14241         return MP_NO_ACTION;
14242     }
14243     else if (IS_RND_GATE_GRAY(element))
14244     {
14245       if (!player->key[RND_GATE_GRAY_NR(element)])
14246         return MP_NO_ACTION;
14247     }
14248     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14249     {
14250       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14251         return MP_NO_ACTION;
14252     }
14253     else if (element == EL_EXIT_OPEN ||
14254              element == EL_EM_EXIT_OPEN ||
14255              element == EL_EM_EXIT_OPENING ||
14256              element == EL_STEEL_EXIT_OPEN ||
14257              element == EL_EM_STEEL_EXIT_OPEN ||
14258              element == EL_EM_STEEL_EXIT_OPENING ||
14259              element == EL_SP_EXIT_OPEN ||
14260              element == EL_SP_EXIT_OPENING)
14261     {
14262       sound_action = ACTION_PASSING;    // player is passing exit
14263     }
14264     else if (element == EL_EMPTY)
14265     {
14266       sound_action = ACTION_MOVING;             // nothing to walk on
14267     }
14268
14269     // play sound from background or player, whatever is available
14270     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14271       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14272     else
14273       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14274   }
14275   else if (player_can_move &&
14276            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14277   {
14278     if (!ACCESS_FROM(element, opposite_direction))
14279       return MP_NO_ACTION;      // field not accessible from this direction
14280
14281     if (CAN_MOVE(element))      // only fixed elements can be passed!
14282       return MP_NO_ACTION;
14283
14284     if (IS_EM_GATE(element))
14285     {
14286       if (!player->key[EM_GATE_NR(element)])
14287         return MP_NO_ACTION;
14288     }
14289     else if (IS_EM_GATE_GRAY(element))
14290     {
14291       if (!player->key[EM_GATE_GRAY_NR(element)])
14292         return MP_NO_ACTION;
14293     }
14294     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14295     {
14296       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14297         return MP_NO_ACTION;
14298     }
14299     else if (IS_EMC_GATE(element))
14300     {
14301       if (!player->key[EMC_GATE_NR(element)])
14302         return MP_NO_ACTION;
14303     }
14304     else if (IS_EMC_GATE_GRAY(element))
14305     {
14306       if (!player->key[EMC_GATE_GRAY_NR(element)])
14307         return MP_NO_ACTION;
14308     }
14309     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14310     {
14311       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14312         return MP_NO_ACTION;
14313     }
14314     else if (element == EL_DC_GATE_WHITE ||
14315              element == EL_DC_GATE_WHITE_GRAY ||
14316              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14317     {
14318       if (player->num_white_keys == 0)
14319         return MP_NO_ACTION;
14320
14321       player->num_white_keys--;
14322     }
14323     else if (IS_SP_PORT(element))
14324     {
14325       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14326           element == EL_SP_GRAVITY_PORT_RIGHT ||
14327           element == EL_SP_GRAVITY_PORT_UP ||
14328           element == EL_SP_GRAVITY_PORT_DOWN)
14329         player->gravity = !player->gravity;
14330       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14331                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14332                element == EL_SP_GRAVITY_ON_PORT_UP ||
14333                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14334         player->gravity = TRUE;
14335       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14336                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14337                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14338                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14339         player->gravity = FALSE;
14340     }
14341
14342     // automatically move to the next field with double speed
14343     player->programmed_action = move_direction;
14344
14345     if (player->move_delay_reset_counter == 0)
14346     {
14347       player->move_delay_reset_counter = 2;     // two double speed steps
14348
14349       DOUBLE_PLAYER_SPEED(player);
14350     }
14351
14352     PlayLevelSoundAction(x, y, ACTION_PASSING);
14353   }
14354   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14355   {
14356     RemoveField(x, y);
14357
14358     if (mode != DF_SNAP)
14359     {
14360       GfxElement[x][y] = GFX_ELEMENT(element);
14361       player->is_digging = TRUE;
14362     }
14363
14364     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14365
14366     // use old behaviour for old levels (digging)
14367     if (!level.finish_dig_collect)
14368     {
14369       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14370                                           player->index_bit, dig_side);
14371
14372       // if digging triggered player relocation, finish digging tile
14373       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14374         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14375     }
14376
14377     if (mode == DF_SNAP)
14378     {
14379       if (level.block_snap_field)
14380         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14381       else
14382         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14383
14384       // use old behaviour for old levels (snapping)
14385       if (!level.finish_dig_collect)
14386         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14387                                             player->index_bit, dig_side);
14388     }
14389   }
14390   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14391   {
14392     RemoveField(x, y);
14393
14394     if (is_player && mode != DF_SNAP)
14395     {
14396       GfxElement[x][y] = element;
14397       player->is_collecting = TRUE;
14398     }
14399
14400     if (element == EL_SPEED_PILL)
14401     {
14402       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14403     }
14404     else if (element == EL_EXTRA_TIME && level.time > 0)
14405     {
14406       TimeLeft += level.extra_time;
14407
14408       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14409
14410       DisplayGameControlValues();
14411     }
14412     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14413     {
14414       int shield_time = (element == EL_SHIELD_DEADLY ?
14415                          level.shield_deadly_time :
14416                          level.shield_normal_time);
14417
14418       player->shield_normal_time_left += shield_time;
14419       if (element == EL_SHIELD_DEADLY)
14420         player->shield_deadly_time_left += shield_time;
14421     }
14422     else if (element == EL_DYNAMITE ||
14423              element == EL_EM_DYNAMITE ||
14424              element == EL_SP_DISK_RED)
14425     {
14426       if (player->inventory_size < MAX_INVENTORY_SIZE)
14427         player->inventory_element[player->inventory_size++] = element;
14428
14429       DrawGameDoorValues();
14430     }
14431     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14432     {
14433       player->dynabomb_count++;
14434       player->dynabombs_left++;
14435     }
14436     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14437     {
14438       player->dynabomb_size++;
14439     }
14440     else if (element == EL_DYNABOMB_INCREASE_POWER)
14441     {
14442       player->dynabomb_xl = TRUE;
14443     }
14444     else if (IS_KEY(element))
14445     {
14446       player->key[KEY_NR(element)] = TRUE;
14447
14448       DrawGameDoorValues();
14449     }
14450     else if (element == EL_DC_KEY_WHITE)
14451     {
14452       player->num_white_keys++;
14453
14454       // display white keys?
14455       // DrawGameDoorValues();
14456     }
14457     else if (IS_ENVELOPE(element))
14458     {
14459       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14460
14461       if (!wait_for_snapping)
14462         player->show_envelope = element;
14463     }
14464     else if (element == EL_EMC_LENSES)
14465     {
14466       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14467
14468       RedrawAllInvisibleElementsForLenses();
14469     }
14470     else if (element == EL_EMC_MAGNIFIER)
14471     {
14472       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14473
14474       RedrawAllInvisibleElementsForMagnifier();
14475     }
14476     else if (IS_DROPPABLE(element) ||
14477              IS_THROWABLE(element))     // can be collected and dropped
14478     {
14479       int i;
14480
14481       if (collect_count == 0)
14482         player->inventory_infinite_element = element;
14483       else
14484         for (i = 0; i < collect_count; i++)
14485           if (player->inventory_size < MAX_INVENTORY_SIZE)
14486             player->inventory_element[player->inventory_size++] = element;
14487
14488       DrawGameDoorValues();
14489     }
14490     else if (collect_count > 0)
14491     {
14492       game.gems_still_needed -= collect_count;
14493       if (game.gems_still_needed < 0)
14494         game.gems_still_needed = 0;
14495
14496       game.snapshot.collected_item = TRUE;
14497
14498       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14499
14500       DisplayGameControlValues();
14501     }
14502
14503     RaiseScoreElement(element);
14504     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14505
14506     // use old behaviour for old levels (collecting)
14507     if (!level.finish_dig_collect && is_player)
14508     {
14509       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14510                                           player->index_bit, dig_side);
14511
14512       // if collecting triggered player relocation, finish collecting tile
14513       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14514         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14515     }
14516
14517     if (mode == DF_SNAP)
14518     {
14519       if (level.block_snap_field)
14520         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14521       else
14522         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14523
14524       // use old behaviour for old levels (snapping)
14525       if (!level.finish_dig_collect)
14526         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14527                                             player->index_bit, dig_side);
14528     }
14529   }
14530   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14531   {
14532     if (mode == DF_SNAP && element != EL_BD_ROCK)
14533       return MP_NO_ACTION;
14534
14535     if (CAN_FALL(element) && dy)
14536       return MP_NO_ACTION;
14537
14538     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14539         !(element == EL_SPRING && level.use_spring_bug))
14540       return MP_NO_ACTION;
14541
14542     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14543         ((move_direction & MV_VERTICAL &&
14544           ((element_info[element].move_pattern & MV_LEFT &&
14545             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14546            (element_info[element].move_pattern & MV_RIGHT &&
14547             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14548          (move_direction & MV_HORIZONTAL &&
14549           ((element_info[element].move_pattern & MV_UP &&
14550             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14551            (element_info[element].move_pattern & MV_DOWN &&
14552             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14553       return MP_NO_ACTION;
14554
14555     // do not push elements already moving away faster than player
14556     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14557         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14558       return MP_NO_ACTION;
14559
14560     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14561     {
14562       if (player->push_delay_value == -1 || !player_was_pushing)
14563         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14564     }
14565     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14566     {
14567       if (player->push_delay_value == -1)
14568         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14569     }
14570     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14571     {
14572       if (!player->is_pushing)
14573         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14574     }
14575
14576     player->is_pushing = TRUE;
14577     player->is_active = TRUE;
14578
14579     if (!(IN_LEV_FIELD(nextx, nexty) &&
14580           (IS_FREE(nextx, nexty) ||
14581            (IS_SB_ELEMENT(element) &&
14582             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14583            (IS_CUSTOM_ELEMENT(element) &&
14584             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14585       return MP_NO_ACTION;
14586
14587     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14588       return MP_NO_ACTION;
14589
14590     if (player->push_delay == -1)       // new pushing; restart delay
14591       player->push_delay = 0;
14592
14593     if (player->push_delay < player->push_delay_value &&
14594         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14595         element != EL_SPRING && element != EL_BALLOON)
14596     {
14597       // make sure that there is no move delay before next try to push
14598       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14599         player->move_delay = 0;
14600
14601       return MP_NO_ACTION;
14602     }
14603
14604     if (IS_CUSTOM_ELEMENT(element) &&
14605         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14606     {
14607       if (!DigFieldByCE(nextx, nexty, element))
14608         return MP_NO_ACTION;
14609     }
14610
14611     if (IS_SB_ELEMENT(element))
14612     {
14613       boolean sokoban_task_solved = FALSE;
14614
14615       if (element == EL_SOKOBAN_FIELD_FULL)
14616       {
14617         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14618
14619         IncrementSokobanFieldsNeeded();
14620         IncrementSokobanObjectsNeeded();
14621       }
14622
14623       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14624       {
14625         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14626
14627         DecrementSokobanFieldsNeeded();
14628         DecrementSokobanObjectsNeeded();
14629
14630         // sokoban object was pushed from empty field to sokoban field
14631         if (Back[x][y] == EL_EMPTY)
14632           sokoban_task_solved = TRUE;
14633       }
14634
14635       Tile[x][y] = EL_SOKOBAN_OBJECT;
14636
14637       if (Back[x][y] == Back[nextx][nexty])
14638         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14639       else if (Back[x][y] != 0)
14640         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14641                                     ACTION_EMPTYING);
14642       else
14643         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14644                                     ACTION_FILLING);
14645
14646       if (sokoban_task_solved &&
14647           game.sokoban_fields_still_needed == 0 &&
14648           game.sokoban_objects_still_needed == 0 &&
14649           level.auto_exit_sokoban)
14650       {
14651         game.players_still_needed = 0;
14652
14653         LevelSolved();
14654
14655         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14656       }
14657     }
14658     else
14659       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14660
14661     InitMovingField(x, y, move_direction);
14662     GfxAction[x][y] = ACTION_PUSHING;
14663
14664     if (mode == DF_SNAP)
14665       ContinueMoving(x, y);
14666     else
14667       MovPos[x][y] = (dx != 0 ? dx : dy);
14668
14669     Pushed[x][y] = TRUE;
14670     Pushed[nextx][nexty] = TRUE;
14671
14672     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14673       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14674     else
14675       player->push_delay_value = -1;    // get new value later
14676
14677     // check for element change _after_ element has been pushed
14678     if (game.use_change_when_pushing_bug)
14679     {
14680       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14681                                  player->index_bit, dig_side);
14682       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14683                                           player->index_bit, dig_side);
14684     }
14685   }
14686   else if (IS_SWITCHABLE(element))
14687   {
14688     if (PLAYER_SWITCHING(player, x, y))
14689     {
14690       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14691                                           player->index_bit, dig_side);
14692
14693       return MP_ACTION;
14694     }
14695
14696     player->is_switching = TRUE;
14697     player->switch_x = x;
14698     player->switch_y = y;
14699
14700     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14701
14702     if (element == EL_ROBOT_WHEEL)
14703     {
14704       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14705
14706       game.robot_wheel_x = x;
14707       game.robot_wheel_y = y;
14708       game.robot_wheel_active = TRUE;
14709
14710       TEST_DrawLevelField(x, y);
14711     }
14712     else if (element == EL_SP_TERMINAL)
14713     {
14714       int xx, yy;
14715
14716       SCAN_PLAYFIELD(xx, yy)
14717       {
14718         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14719         {
14720           Bang(xx, yy);
14721         }
14722         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14723         {
14724           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14725
14726           ResetGfxAnimation(xx, yy);
14727           TEST_DrawLevelField(xx, yy);
14728         }
14729       }
14730     }
14731     else if (IS_BELT_SWITCH(element))
14732     {
14733       ToggleBeltSwitch(x, y);
14734     }
14735     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14736              element == EL_SWITCHGATE_SWITCH_DOWN ||
14737              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14738              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14739     {
14740       ToggleSwitchgateSwitch();
14741     }
14742     else if (element == EL_LIGHT_SWITCH ||
14743              element == EL_LIGHT_SWITCH_ACTIVE)
14744     {
14745       ToggleLightSwitch(x, y);
14746     }
14747     else if (element == EL_TIMEGATE_SWITCH ||
14748              element == EL_DC_TIMEGATE_SWITCH)
14749     {
14750       ActivateTimegateSwitch(x, y);
14751     }
14752     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14753              element == EL_BALLOON_SWITCH_RIGHT ||
14754              element == EL_BALLOON_SWITCH_UP    ||
14755              element == EL_BALLOON_SWITCH_DOWN  ||
14756              element == EL_BALLOON_SWITCH_NONE  ||
14757              element == EL_BALLOON_SWITCH_ANY)
14758     {
14759       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14760                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14761                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14762                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14763                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14764                              move_direction);
14765     }
14766     else if (element == EL_LAMP)
14767     {
14768       Tile[x][y] = EL_LAMP_ACTIVE;
14769       game.lights_still_needed--;
14770
14771       ResetGfxAnimation(x, y);
14772       TEST_DrawLevelField(x, y);
14773     }
14774     else if (element == EL_TIME_ORB_FULL)
14775     {
14776       Tile[x][y] = EL_TIME_ORB_EMPTY;
14777
14778       if (level.time > 0 || level.use_time_orb_bug)
14779       {
14780         TimeLeft += level.time_orb_time;
14781         game.no_level_time_limit = FALSE;
14782
14783         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14784
14785         DisplayGameControlValues();
14786       }
14787
14788       ResetGfxAnimation(x, y);
14789       TEST_DrawLevelField(x, y);
14790     }
14791     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14792              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14793     {
14794       int xx, yy;
14795
14796       game.ball_active = !game.ball_active;
14797
14798       SCAN_PLAYFIELD(xx, yy)
14799       {
14800         int e = Tile[xx][yy];
14801
14802         if (game.ball_active)
14803         {
14804           if (e == EL_EMC_MAGIC_BALL)
14805             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14806           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14807             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14808         }
14809         else
14810         {
14811           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14812             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14813           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14814             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14815         }
14816       }
14817     }
14818
14819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14820                                         player->index_bit, dig_side);
14821
14822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14823                                         player->index_bit, dig_side);
14824
14825     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14826                                         player->index_bit, dig_side);
14827
14828     return MP_ACTION;
14829   }
14830   else
14831   {
14832     if (!PLAYER_SWITCHING(player, x, y))
14833     {
14834       player->is_switching = TRUE;
14835       player->switch_x = x;
14836       player->switch_y = y;
14837
14838       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14839                                  player->index_bit, dig_side);
14840       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14841                                           player->index_bit, dig_side);
14842
14843       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14844                                  player->index_bit, dig_side);
14845       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14846                                           player->index_bit, dig_side);
14847     }
14848
14849     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14850                                player->index_bit, dig_side);
14851     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14852                                         player->index_bit, dig_side);
14853
14854     return MP_NO_ACTION;
14855   }
14856
14857   player->push_delay = -1;
14858
14859   if (is_player)                // function can also be called by EL_PENGUIN
14860   {
14861     if (Tile[x][y] != element)          // really digged/collected something
14862     {
14863       player->is_collecting = !player->is_digging;
14864       player->is_active = TRUE;
14865
14866       player->last_removed_element = element;
14867     }
14868   }
14869
14870   return MP_MOVING;
14871 }
14872
14873 static boolean DigFieldByCE(int x, int y, int digging_element)
14874 {
14875   int element = Tile[x][y];
14876
14877   if (!IS_FREE(x, y))
14878   {
14879     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14880                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14881                   ACTION_BREAKING);
14882
14883     // no element can dig solid indestructible elements
14884     if (IS_INDESTRUCTIBLE(element) &&
14885         !IS_DIGGABLE(element) &&
14886         !IS_COLLECTIBLE(element))
14887       return FALSE;
14888
14889     if (AmoebaNr[x][y] &&
14890         (element == EL_AMOEBA_FULL ||
14891          element == EL_BD_AMOEBA ||
14892          element == EL_AMOEBA_GROWING))
14893     {
14894       AmoebaCnt[AmoebaNr[x][y]]--;
14895       AmoebaCnt2[AmoebaNr[x][y]]--;
14896     }
14897
14898     if (IS_MOVING(x, y))
14899       RemoveMovingField(x, y);
14900     else
14901     {
14902       RemoveField(x, y);
14903       TEST_DrawLevelField(x, y);
14904     }
14905
14906     // if digged element was about to explode, prevent the explosion
14907     ExplodeField[x][y] = EX_TYPE_NONE;
14908
14909     PlayLevelSoundAction(x, y, action);
14910   }
14911
14912   Store[x][y] = EL_EMPTY;
14913
14914   // this makes it possible to leave the removed element again
14915   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14916     Store[x][y] = element;
14917
14918   return TRUE;
14919 }
14920
14921 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14922 {
14923   int jx = player->jx, jy = player->jy;
14924   int x = jx + dx, y = jy + dy;
14925   int snap_direction = (dx == -1 ? MV_LEFT  :
14926                         dx == +1 ? MV_RIGHT :
14927                         dy == -1 ? MV_UP    :
14928                         dy == +1 ? MV_DOWN  : MV_NONE);
14929   boolean can_continue_snapping = (level.continuous_snapping &&
14930                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14931
14932   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14933     return FALSE;
14934
14935   if (!player->active || !IN_LEV_FIELD(x, y))
14936     return FALSE;
14937
14938   if (dx && dy)
14939     return FALSE;
14940
14941   if (!dx && !dy)
14942   {
14943     if (player->MovPos == 0)
14944       player->is_pushing = FALSE;
14945
14946     player->is_snapping = FALSE;
14947
14948     if (player->MovPos == 0)
14949     {
14950       player->is_moving = FALSE;
14951       player->is_digging = FALSE;
14952       player->is_collecting = FALSE;
14953     }
14954
14955     return FALSE;
14956   }
14957
14958   // prevent snapping with already pressed snap key when not allowed
14959   if (player->is_snapping && !can_continue_snapping)
14960     return FALSE;
14961
14962   player->MovDir = snap_direction;
14963
14964   if (player->MovPos == 0)
14965   {
14966     player->is_moving = FALSE;
14967     player->is_digging = FALSE;
14968     player->is_collecting = FALSE;
14969   }
14970
14971   player->is_dropping = FALSE;
14972   player->is_dropping_pressed = FALSE;
14973   player->drop_pressed_delay = 0;
14974
14975   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14976     return FALSE;
14977
14978   player->is_snapping = TRUE;
14979   player->is_active = TRUE;
14980
14981   if (player->MovPos == 0)
14982   {
14983     player->is_moving = FALSE;
14984     player->is_digging = FALSE;
14985     player->is_collecting = FALSE;
14986   }
14987
14988   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14989     TEST_DrawLevelField(player->last_jx, player->last_jy);
14990
14991   TEST_DrawLevelField(x, y);
14992
14993   return TRUE;
14994 }
14995
14996 static boolean DropElement(struct PlayerInfo *player)
14997 {
14998   int old_element, new_element;
14999   int dropx = player->jx, dropy = player->jy;
15000   int drop_direction = player->MovDir;
15001   int drop_side = drop_direction;
15002   int drop_element = get_next_dropped_element(player);
15003
15004   /* do not drop an element on top of another element; when holding drop key
15005      pressed without moving, dropped element must move away before the next
15006      element can be dropped (this is especially important if the next element
15007      is dynamite, which can be placed on background for historical reasons) */
15008   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15009     return MP_ACTION;
15010
15011   if (IS_THROWABLE(drop_element))
15012   {
15013     dropx += GET_DX_FROM_DIR(drop_direction);
15014     dropy += GET_DY_FROM_DIR(drop_direction);
15015
15016     if (!IN_LEV_FIELD(dropx, dropy))
15017       return FALSE;
15018   }
15019
15020   old_element = Tile[dropx][dropy];     // old element at dropping position
15021   new_element = drop_element;           // default: no change when dropping
15022
15023   // check if player is active, not moving and ready to drop
15024   if (!player->active || player->MovPos || player->drop_delay > 0)
15025     return FALSE;
15026
15027   // check if player has anything that can be dropped
15028   if (new_element == EL_UNDEFINED)
15029     return FALSE;
15030
15031   // only set if player has anything that can be dropped
15032   player->is_dropping_pressed = TRUE;
15033
15034   // check if drop key was pressed long enough for EM style dynamite
15035   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15036     return FALSE;
15037
15038   // check if anything can be dropped at the current position
15039   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15040     return FALSE;
15041
15042   // collected custom elements can only be dropped on empty fields
15043   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15044     return FALSE;
15045
15046   if (old_element != EL_EMPTY)
15047     Back[dropx][dropy] = old_element;   // store old element on this field
15048
15049   ResetGfxAnimation(dropx, dropy);
15050   ResetRandomAnimationValue(dropx, dropy);
15051
15052   if (player->inventory_size > 0 ||
15053       player->inventory_infinite_element != EL_UNDEFINED)
15054   {
15055     if (player->inventory_size > 0)
15056     {
15057       player->inventory_size--;
15058
15059       DrawGameDoorValues();
15060
15061       if (new_element == EL_DYNAMITE)
15062         new_element = EL_DYNAMITE_ACTIVE;
15063       else if (new_element == EL_EM_DYNAMITE)
15064         new_element = EL_EM_DYNAMITE_ACTIVE;
15065       else if (new_element == EL_SP_DISK_RED)
15066         new_element = EL_SP_DISK_RED_ACTIVE;
15067     }
15068
15069     Tile[dropx][dropy] = new_element;
15070
15071     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15072       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15073                           el2img(Tile[dropx][dropy]), 0);
15074
15075     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15076
15077     // needed if previous element just changed to "empty" in the last frame
15078     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15079
15080     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15081                                player->index_bit, drop_side);
15082     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15083                                         CE_PLAYER_DROPS_X,
15084                                         player->index_bit, drop_side);
15085
15086     TestIfElementTouchesCustomElement(dropx, dropy);
15087   }
15088   else          // player is dropping a dyna bomb
15089   {
15090     player->dynabombs_left--;
15091
15092     Tile[dropx][dropy] = new_element;
15093
15094     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15095       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15096                           el2img(Tile[dropx][dropy]), 0);
15097
15098     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15099   }
15100
15101   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15102     InitField_WithBug1(dropx, dropy, FALSE);
15103
15104   new_element = Tile[dropx][dropy];     // element might have changed
15105
15106   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15107       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15108   {
15109     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15110       MovDir[dropx][dropy] = drop_direction;
15111
15112     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15113
15114     // do not cause impact style collision by dropping elements that can fall
15115     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15116   }
15117
15118   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15119   player->is_dropping = TRUE;
15120
15121   player->drop_pressed_delay = 0;
15122   player->is_dropping_pressed = FALSE;
15123
15124   player->drop_x = dropx;
15125   player->drop_y = dropy;
15126
15127   return TRUE;
15128 }
15129
15130 // ----------------------------------------------------------------------------
15131 // game sound playing functions
15132 // ----------------------------------------------------------------------------
15133
15134 static int *loop_sound_frame = NULL;
15135 static int *loop_sound_volume = NULL;
15136
15137 void InitPlayLevelSound(void)
15138 {
15139   int num_sounds = getSoundListSize();
15140
15141   checked_free(loop_sound_frame);
15142   checked_free(loop_sound_volume);
15143
15144   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15145   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15146 }
15147
15148 static void PlayLevelSound(int x, int y, int nr)
15149 {
15150   int sx = SCREENX(x), sy = SCREENY(y);
15151   int volume, stereo_position;
15152   int max_distance = 8;
15153   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15154
15155   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15156       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15157     return;
15158
15159   if (!IN_LEV_FIELD(x, y) ||
15160       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15161       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15162     return;
15163
15164   volume = SOUND_MAX_VOLUME;
15165
15166   if (!IN_SCR_FIELD(sx, sy))
15167   {
15168     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15169     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15170
15171     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15172   }
15173
15174   stereo_position = (SOUND_MAX_LEFT +
15175                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15176                      (SCR_FIELDX + 2 * max_distance));
15177
15178   if (IS_LOOP_SOUND(nr))
15179   {
15180     /* This assures that quieter loop sounds do not overwrite louder ones,
15181        while restarting sound volume comparison with each new game frame. */
15182
15183     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15184       return;
15185
15186     loop_sound_volume[nr] = volume;
15187     loop_sound_frame[nr] = FrameCounter;
15188   }
15189
15190   PlaySoundExt(nr, volume, stereo_position, type);
15191 }
15192
15193 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15194 {
15195   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15196                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15197                  y < LEVELY(BY1) ? LEVELY(BY1) :
15198                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15199                  sound_action);
15200 }
15201
15202 static void PlayLevelSoundAction(int x, int y, int action)
15203 {
15204   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15205 }
15206
15207 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15208 {
15209   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15210
15211   if (sound_effect != SND_UNDEFINED)
15212     PlayLevelSound(x, y, sound_effect);
15213 }
15214
15215 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15216                                               int action)
15217 {
15218   int sound_effect = element_info[SND_ELEMENT(element)].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 PlayLevelSoundActionIfLoop(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     PlayLevelSound(x, y, sound_effect);
15230 }
15231
15232 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15233 {
15234   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15235
15236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15237     StopSound(sound_effect);
15238 }
15239
15240 static int getLevelMusicNr(void)
15241 {
15242   if (levelset.music[level_nr] != MUS_UNDEFINED)
15243     return levelset.music[level_nr];            // from config file
15244   else
15245     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15246 }
15247
15248 static void FadeLevelSounds(void)
15249 {
15250   FadeSounds();
15251 }
15252
15253 static void FadeLevelMusic(void)
15254 {
15255   int music_nr = getLevelMusicNr();
15256   char *curr_music = getCurrentlyPlayingMusicFilename();
15257   char *next_music = getMusicInfoEntryFilename(music_nr);
15258
15259   if (!strEqual(curr_music, next_music))
15260     FadeMusic();
15261 }
15262
15263 void FadeLevelSoundsAndMusic(void)
15264 {
15265   FadeLevelSounds();
15266   FadeLevelMusic();
15267 }
15268
15269 static void PlayLevelMusic(void)
15270 {
15271   int music_nr = getLevelMusicNr();
15272   char *curr_music = getCurrentlyPlayingMusicFilename();
15273   char *next_music = getMusicInfoEntryFilename(music_nr);
15274
15275   if (!strEqual(curr_music, next_music))
15276     PlayMusicLoop(music_nr);
15277 }
15278
15279 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15280 {
15281   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15282   int offset = 0;
15283   int x = xx - offset;
15284   int y = yy - offset;
15285
15286   switch (sample)
15287   {
15288     case SOUND_blank:
15289       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15290       break;
15291
15292     case SOUND_roll:
15293       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15294       break;
15295
15296     case SOUND_stone:
15297       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15298       break;
15299
15300     case SOUND_nut:
15301       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15302       break;
15303
15304     case SOUND_crack:
15305       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15306       break;
15307
15308     case SOUND_bug:
15309       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15310       break;
15311
15312     case SOUND_tank:
15313       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15314       break;
15315
15316     case SOUND_android_clone:
15317       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15318       break;
15319
15320     case SOUND_android_move:
15321       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15322       break;
15323
15324     case SOUND_spring:
15325       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15326       break;
15327
15328     case SOUND_slurp:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15330       break;
15331
15332     case SOUND_eater:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15334       break;
15335
15336     case SOUND_eater_eat:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15338       break;
15339
15340     case SOUND_alien:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15342       break;
15343
15344     case SOUND_collect:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15346       break;
15347
15348     case SOUND_diamond:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15350       break;
15351
15352     case SOUND_squash:
15353       // !!! CHECK THIS !!!
15354 #if 1
15355       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15356 #else
15357       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15358 #endif
15359       break;
15360
15361     case SOUND_wonderfall:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15363       break;
15364
15365     case SOUND_drip:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15367       break;
15368
15369     case SOUND_push:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15371       break;
15372
15373     case SOUND_dirt:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15375       break;
15376
15377     case SOUND_acid:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15379       break;
15380
15381     case SOUND_ball:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15383       break;
15384
15385     case SOUND_slide:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15387       break;
15388
15389     case SOUND_wonder:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15391       break;
15392
15393     case SOUND_door:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15395       break;
15396
15397     case SOUND_exit_open:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15399       break;
15400
15401     case SOUND_exit_leave:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15403       break;
15404
15405     case SOUND_dynamite:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15407       break;
15408
15409     case SOUND_tick:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15411       break;
15412
15413     case SOUND_press:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15415       break;
15416
15417     case SOUND_wheel:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15419       break;
15420
15421     case SOUND_boom:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15423       break;
15424
15425     case SOUND_die:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15427       break;
15428
15429     case SOUND_time:
15430       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15431       break;
15432
15433     default:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15435       break;
15436   }
15437 }
15438
15439 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15440 {
15441   int element = map_element_SP_to_RND(element_sp);
15442   int action = map_action_SP_to_RND(action_sp);
15443   int offset = (setup.sp_show_border_elements ? 0 : 1);
15444   int x = xx - offset;
15445   int y = yy - offset;
15446
15447   PlayLevelSoundElementAction(x, y, element, action);
15448 }
15449
15450 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15451 {
15452   int element = map_element_MM_to_RND(element_mm);
15453   int action = map_action_MM_to_RND(action_mm);
15454   int offset = 0;
15455   int x = xx - offset;
15456   int y = yy - offset;
15457
15458   if (!IS_MM_ELEMENT(element))
15459     element = EL_MM_DEFAULT;
15460
15461   PlayLevelSoundElementAction(x, y, element, action);
15462 }
15463
15464 void PlaySound_MM(int sound_mm)
15465 {
15466   int sound = map_sound_MM_to_RND(sound_mm);
15467
15468   if (sound == SND_UNDEFINED)
15469     return;
15470
15471   PlaySound(sound);
15472 }
15473
15474 void PlaySoundLoop_MM(int sound_mm)
15475 {
15476   int sound = map_sound_MM_to_RND(sound_mm);
15477
15478   if (sound == SND_UNDEFINED)
15479     return;
15480
15481   PlaySoundLoop(sound);
15482 }
15483
15484 void StopSound_MM(int sound_mm)
15485 {
15486   int sound = map_sound_MM_to_RND(sound_mm);
15487
15488   if (sound == SND_UNDEFINED)
15489     return;
15490
15491   StopSound(sound);
15492 }
15493
15494 void RaiseScore(int value)
15495 {
15496   game.score += value;
15497
15498   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15499
15500   DisplayGameControlValues();
15501 }
15502
15503 void RaiseScoreElement(int element)
15504 {
15505   switch (element)
15506   {
15507     case EL_EMERALD:
15508     case EL_BD_DIAMOND:
15509     case EL_EMERALD_YELLOW:
15510     case EL_EMERALD_RED:
15511     case EL_EMERALD_PURPLE:
15512     case EL_SP_INFOTRON:
15513       RaiseScore(level.score[SC_EMERALD]);
15514       break;
15515     case EL_DIAMOND:
15516       RaiseScore(level.score[SC_DIAMOND]);
15517       break;
15518     case EL_CRYSTAL:
15519       RaiseScore(level.score[SC_CRYSTAL]);
15520       break;
15521     case EL_PEARL:
15522       RaiseScore(level.score[SC_PEARL]);
15523       break;
15524     case EL_BUG:
15525     case EL_BD_BUTTERFLY:
15526     case EL_SP_ELECTRON:
15527       RaiseScore(level.score[SC_BUG]);
15528       break;
15529     case EL_SPACESHIP:
15530     case EL_BD_FIREFLY:
15531     case EL_SP_SNIKSNAK:
15532       RaiseScore(level.score[SC_SPACESHIP]);
15533       break;
15534     case EL_YAMYAM:
15535     case EL_DARK_YAMYAM:
15536       RaiseScore(level.score[SC_YAMYAM]);
15537       break;
15538     case EL_ROBOT:
15539       RaiseScore(level.score[SC_ROBOT]);
15540       break;
15541     case EL_PACMAN:
15542       RaiseScore(level.score[SC_PACMAN]);
15543       break;
15544     case EL_NUT:
15545       RaiseScore(level.score[SC_NUT]);
15546       break;
15547     case EL_DYNAMITE:
15548     case EL_EM_DYNAMITE:
15549     case EL_SP_DISK_RED:
15550     case EL_DYNABOMB_INCREASE_NUMBER:
15551     case EL_DYNABOMB_INCREASE_SIZE:
15552     case EL_DYNABOMB_INCREASE_POWER:
15553       RaiseScore(level.score[SC_DYNAMITE]);
15554       break;
15555     case EL_SHIELD_NORMAL:
15556     case EL_SHIELD_DEADLY:
15557       RaiseScore(level.score[SC_SHIELD]);
15558       break;
15559     case EL_EXTRA_TIME:
15560       RaiseScore(level.extra_time_score);
15561       break;
15562     case EL_KEY_1:
15563     case EL_KEY_2:
15564     case EL_KEY_3:
15565     case EL_KEY_4:
15566     case EL_EM_KEY_1:
15567     case EL_EM_KEY_2:
15568     case EL_EM_KEY_3:
15569     case EL_EM_KEY_4:
15570     case EL_EMC_KEY_5:
15571     case EL_EMC_KEY_6:
15572     case EL_EMC_KEY_7:
15573     case EL_EMC_KEY_8:
15574     case EL_DC_KEY_WHITE:
15575       RaiseScore(level.score[SC_KEY]);
15576       break;
15577     default:
15578       RaiseScore(element_info[element].collect_score);
15579       break;
15580   }
15581 }
15582
15583 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15584 {
15585   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15586   {
15587     if (!quick_quit)
15588     {
15589       // prevent short reactivation of overlay buttons while closing door
15590       SetOverlayActive(FALSE);
15591       UnmapGameButtons();
15592
15593       // door may still be open due to skipped or envelope style request
15594       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15595     }
15596
15597     if (network.enabled)
15598       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15599     else
15600     {
15601       if (quick_quit)
15602         FadeSkipNextFadeIn();
15603
15604       SetGameStatus(GAME_MODE_MAIN);
15605
15606       DrawMainMenu();
15607     }
15608   }
15609   else          // continue playing the game
15610   {
15611     if (tape.playing && tape.deactivate_display)
15612       TapeDeactivateDisplayOff(TRUE);
15613
15614     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15615
15616     if (tape.playing && tape.deactivate_display)
15617       TapeDeactivateDisplayOn();
15618   }
15619 }
15620
15621 void RequestQuitGame(boolean escape_key_pressed)
15622 {
15623   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15624   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15625                         level_editor_test_game);
15626   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15627                           quick_quit || score_info_tape_play);
15628
15629   RequestQuitGameExt(skip_request, quick_quit,
15630                      "Do you really want to quit the game?");
15631 }
15632
15633 static char *getRestartGameMessage(void)
15634 {
15635   boolean play_again = hasStartedNetworkGame();
15636   static char message[MAX_OUTPUT_LINESIZE];
15637   char *game_over_text = "Game over!";
15638   char *play_again_text = " Play it again?";
15639
15640   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15641       game_mm.game_over_message != NULL)
15642     game_over_text = game_mm.game_over_message;
15643
15644   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15645            (play_again ? play_again_text : ""));
15646
15647   return message;
15648 }
15649
15650 static void RequestRestartGame(void)
15651 {
15652   char *message = getRestartGameMessage();
15653   boolean has_started_game = hasStartedNetworkGame();
15654   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15655   int door_state = DOOR_CLOSE_1;
15656
15657   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15658   {
15659     CloseDoor(door_state);
15660
15661     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15662   }
15663   else
15664   {
15665     // if game was invoked from level editor, also close tape recorder door
15666     if (level_editor_test_game)
15667       door_state = DOOR_CLOSE_ALL;
15668
15669     CloseDoor(door_state);
15670
15671     SetGameStatus(GAME_MODE_MAIN);
15672
15673     DrawMainMenu();
15674   }
15675 }
15676
15677 boolean CheckRestartGame(void)
15678 {
15679   static int game_over_delay = 0;
15680   int game_over_delay_value = 50;
15681   boolean game_over = checkGameFailed();
15682
15683   if (!game_over)
15684   {
15685     game_over_delay = game_over_delay_value;
15686
15687     return FALSE;
15688   }
15689
15690   if (game_over_delay > 0)
15691   {
15692     if (game_over_delay == game_over_delay_value / 2)
15693       PlaySound(SND_GAME_LOSING);
15694
15695     game_over_delay--;
15696
15697     return FALSE;
15698   }
15699
15700   // do not handle game over if request dialog is already active
15701   if (game.request_active)
15702     return FALSE;
15703
15704   // do not ask to play again if game was never actually played
15705   if (!game.GamePlayed)
15706     return FALSE;
15707
15708   // do not ask to play again if this was disabled in setup menu
15709   if (!setup.ask_on_game_over)
15710     return FALSE;
15711
15712   RequestRestartGame();
15713
15714   return TRUE;
15715 }
15716
15717 boolean checkGameSolved(void)
15718 {
15719   // set for all game engines if level was solved
15720   return game.LevelSolved_GameEnd;
15721 }
15722
15723 boolean checkGameFailed(void)
15724 {
15725   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15726     return (game_em.game_over && !game_em.level_solved);
15727   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15728     return (game_sp.game_over && !game_sp.level_solved);
15729   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15730     return (game_mm.game_over && !game_mm.level_solved);
15731   else                          // GAME_ENGINE_TYPE_RND
15732     return (game.GameOver && !game.LevelSolved);
15733 }
15734
15735 boolean checkGameEnded(void)
15736 {
15737   return (checkGameSolved() || checkGameFailed());
15738 }
15739
15740
15741 // ----------------------------------------------------------------------------
15742 // random generator functions
15743 // ----------------------------------------------------------------------------
15744
15745 unsigned int InitEngineRandom_RND(int seed)
15746 {
15747   game.num_random_calls = 0;
15748
15749   return InitEngineRandom(seed);
15750 }
15751
15752 unsigned int RND(int max)
15753 {
15754   if (max > 0)
15755   {
15756     game.num_random_calls++;
15757
15758     return GetEngineRandom(max);
15759   }
15760
15761   return 0;
15762 }
15763
15764
15765 // ----------------------------------------------------------------------------
15766 // game engine snapshot handling functions
15767 // ----------------------------------------------------------------------------
15768
15769 struct EngineSnapshotInfo
15770 {
15771   // runtime values for custom element collect score
15772   int collect_score[NUM_CUSTOM_ELEMENTS];
15773
15774   // runtime values for group element choice position
15775   int choice_pos[NUM_GROUP_ELEMENTS];
15776
15777   // runtime values for belt position animations
15778   int belt_graphic[4][NUM_BELT_PARTS];
15779   int belt_anim_mode[4][NUM_BELT_PARTS];
15780 };
15781
15782 static struct EngineSnapshotInfo engine_snapshot_rnd;
15783 static char *snapshot_level_identifier = NULL;
15784 static int snapshot_level_nr = -1;
15785
15786 static void SaveEngineSnapshotValues_RND(void)
15787 {
15788   static int belt_base_active_element[4] =
15789   {
15790     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15791     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15792     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15793     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15794   };
15795   int i, j;
15796
15797   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15798   {
15799     int element = EL_CUSTOM_START + i;
15800
15801     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15802   }
15803
15804   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15805   {
15806     int element = EL_GROUP_START + i;
15807
15808     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15809   }
15810
15811   for (i = 0; i < 4; i++)
15812   {
15813     for (j = 0; j < NUM_BELT_PARTS; j++)
15814     {
15815       int element = belt_base_active_element[i] + j;
15816       int graphic = el2img(element);
15817       int anim_mode = graphic_info[graphic].anim_mode;
15818
15819       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15820       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15821     }
15822   }
15823 }
15824
15825 static void LoadEngineSnapshotValues_RND(void)
15826 {
15827   unsigned int num_random_calls = game.num_random_calls;
15828   int i, j;
15829
15830   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15831   {
15832     int element = EL_CUSTOM_START + i;
15833
15834     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15835   }
15836
15837   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15838   {
15839     int element = EL_GROUP_START + i;
15840
15841     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15842   }
15843
15844   for (i = 0; i < 4; i++)
15845   {
15846     for (j = 0; j < NUM_BELT_PARTS; j++)
15847     {
15848       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15849       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15850
15851       graphic_info[graphic].anim_mode = anim_mode;
15852     }
15853   }
15854
15855   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15856   {
15857     InitRND(tape.random_seed);
15858     for (i = 0; i < num_random_calls; i++)
15859       RND(1);
15860   }
15861
15862   if (game.num_random_calls != num_random_calls)
15863   {
15864     Error("number of random calls out of sync");
15865     Error("number of random calls should be %d", num_random_calls);
15866     Error("number of random calls is %d", game.num_random_calls);
15867
15868     Fail("this should not happen -- please debug");
15869   }
15870 }
15871
15872 void FreeEngineSnapshotSingle(void)
15873 {
15874   FreeSnapshotSingle();
15875
15876   setString(&snapshot_level_identifier, NULL);
15877   snapshot_level_nr = -1;
15878 }
15879
15880 void FreeEngineSnapshotList(void)
15881 {
15882   FreeSnapshotList();
15883 }
15884
15885 static ListNode *SaveEngineSnapshotBuffers(void)
15886 {
15887   ListNode *buffers = NULL;
15888
15889   // copy some special values to a structure better suited for the snapshot
15890
15891   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15892     SaveEngineSnapshotValues_RND();
15893   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15894     SaveEngineSnapshotValues_EM();
15895   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15896     SaveEngineSnapshotValues_SP(&buffers);
15897   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15898     SaveEngineSnapshotValues_MM();
15899
15900   // save values stored in special snapshot structure
15901
15902   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15903     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15905     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15906   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15907     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15908   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15909     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15910
15911   // save further RND engine values
15912
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15914   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15915   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15916
15917   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15918   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15921   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15922
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15926
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15928
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15931
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15950
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15953
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15957
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15967
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15970
15971 #if 0
15972   ListNode *node = engine_snapshot_list_rnd;
15973   int num_bytes = 0;
15974
15975   while (node != NULL)
15976   {
15977     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15978
15979     node = node->next;
15980   }
15981
15982   Debug("game:playing:SaveEngineSnapshotBuffers",
15983         "size of engine snapshot: %d bytes", num_bytes);
15984 #endif
15985
15986   return buffers;
15987 }
15988
15989 void SaveEngineSnapshotSingle(void)
15990 {
15991   ListNode *buffers = SaveEngineSnapshotBuffers();
15992
15993   // finally save all snapshot buffers to single snapshot
15994   SaveSnapshotSingle(buffers);
15995
15996   // save level identification information
15997   setString(&snapshot_level_identifier, leveldir_current->identifier);
15998   snapshot_level_nr = level_nr;
15999 }
16000
16001 boolean CheckSaveEngineSnapshotToList(void)
16002 {
16003   boolean save_snapshot =
16004     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16005      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16006       game.snapshot.changed_action) ||
16007      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16008       game.snapshot.collected_item));
16009
16010   game.snapshot.changed_action = FALSE;
16011   game.snapshot.collected_item = FALSE;
16012   game.snapshot.save_snapshot = save_snapshot;
16013
16014   return save_snapshot;
16015 }
16016
16017 void SaveEngineSnapshotToList(void)
16018 {
16019   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16020       tape.quick_resume)
16021     return;
16022
16023   ListNode *buffers = SaveEngineSnapshotBuffers();
16024
16025   // finally save all snapshot buffers to snapshot list
16026   SaveSnapshotToList(buffers);
16027 }
16028
16029 void SaveEngineSnapshotToListInitial(void)
16030 {
16031   FreeEngineSnapshotList();
16032
16033   SaveEngineSnapshotToList();
16034 }
16035
16036 static void LoadEngineSnapshotValues(void)
16037 {
16038   // restore special values from snapshot structure
16039
16040   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16041     LoadEngineSnapshotValues_RND();
16042   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16043     LoadEngineSnapshotValues_EM();
16044   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16045     LoadEngineSnapshotValues_SP();
16046   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16047     LoadEngineSnapshotValues_MM();
16048 }
16049
16050 void LoadEngineSnapshotSingle(void)
16051 {
16052   LoadSnapshotSingle();
16053
16054   LoadEngineSnapshotValues();
16055 }
16056
16057 static void LoadEngineSnapshot_Undo(int steps)
16058 {
16059   LoadSnapshotFromList_Older(steps);
16060
16061   LoadEngineSnapshotValues();
16062 }
16063
16064 static void LoadEngineSnapshot_Redo(int steps)
16065 {
16066   LoadSnapshotFromList_Newer(steps);
16067
16068   LoadEngineSnapshotValues();
16069 }
16070
16071 boolean CheckEngineSnapshotSingle(void)
16072 {
16073   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16074           snapshot_level_nr == level_nr);
16075 }
16076
16077 boolean CheckEngineSnapshotList(void)
16078 {
16079   return CheckSnapshotList();
16080 }
16081
16082
16083 // ---------- new game button stuff -------------------------------------------
16084
16085 static struct
16086 {
16087   int graphic;
16088   struct XY *pos;
16089   int gadget_id;
16090   boolean *setup_value;
16091   boolean allowed_on_tape;
16092   boolean is_touch_button;
16093   char *infotext;
16094 } gamebutton_info[NUM_GAME_BUTTONS] =
16095 {
16096   {
16097     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16098     GAME_CTRL_ID_STOP,                          NULL,
16099     TRUE, FALSE,                                "stop game"
16100   },
16101   {
16102     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16103     GAME_CTRL_ID_PAUSE,                         NULL,
16104     TRUE, FALSE,                                "pause game"
16105   },
16106   {
16107     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16108     GAME_CTRL_ID_PLAY,                          NULL,
16109     TRUE, FALSE,                                "play game"
16110   },
16111   {
16112     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16113     GAME_CTRL_ID_UNDO,                          NULL,
16114     TRUE, FALSE,                                "undo step"
16115   },
16116   {
16117     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16118     GAME_CTRL_ID_REDO,                          NULL,
16119     TRUE, FALSE,                                "redo step"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16123     GAME_CTRL_ID_SAVE,                          NULL,
16124     TRUE, FALSE,                                "save game"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16128     GAME_CTRL_ID_PAUSE2,                        NULL,
16129     TRUE, FALSE,                                "pause game"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16133     GAME_CTRL_ID_LOAD,                          NULL,
16134     TRUE, FALSE,                                "load game"
16135   },
16136   {
16137     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16138     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16139     FALSE, FALSE,                               "stop game"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16143     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16144     FALSE, FALSE,                               "pause game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16148     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16149     FALSE, FALSE,                               "play game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16153     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16154     FALSE, TRUE,                                "stop game"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16158     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16159     FALSE, TRUE,                                "pause game"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16163     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16164     TRUE, FALSE,                                "background music on/off"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16168     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16169     TRUE, FALSE,                                "sound loops on/off"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16173     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16174     TRUE, FALSE,                                "normal sounds on/off"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16178     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16179     FALSE, FALSE,                               "background music on/off"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16183     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16184     FALSE, FALSE,                               "sound loops on/off"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16188     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16189     FALSE, FALSE,                               "normal sounds on/off"
16190   }
16191 };
16192
16193 void CreateGameButtons(void)
16194 {
16195   int i;
16196
16197   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16198   {
16199     int graphic = gamebutton_info[i].graphic;
16200     struct GraphicInfo *gfx = &graphic_info[graphic];
16201     struct XY *pos = gamebutton_info[i].pos;
16202     struct GadgetInfo *gi;
16203     int button_type;
16204     boolean checked;
16205     unsigned int event_mask;
16206     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16207     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16208     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16209     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16210     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16211     int gd_x   = gfx->src_x;
16212     int gd_y   = gfx->src_y;
16213     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16214     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16215     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16216     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16217     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16218     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16219     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16220     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16221     int id = i;
16222
16223     // do not use touch buttons if overlay touch buttons are disabled
16224     if (is_touch_button && !setup.touch.overlay_buttons)
16225       continue;
16226
16227     if (gfx->bitmap == NULL)
16228     {
16229       game_gadget[id] = NULL;
16230
16231       continue;
16232     }
16233
16234     if (id == GAME_CTRL_ID_STOP ||
16235         id == GAME_CTRL_ID_PANEL_STOP ||
16236         id == GAME_CTRL_ID_TOUCH_STOP ||
16237         id == GAME_CTRL_ID_PLAY ||
16238         id == GAME_CTRL_ID_PANEL_PLAY ||
16239         id == GAME_CTRL_ID_SAVE ||
16240         id == GAME_CTRL_ID_LOAD)
16241     {
16242       button_type = GD_TYPE_NORMAL_BUTTON;
16243       checked = FALSE;
16244       event_mask = GD_EVENT_RELEASED;
16245     }
16246     else if (id == GAME_CTRL_ID_UNDO ||
16247              id == GAME_CTRL_ID_REDO)
16248     {
16249       button_type = GD_TYPE_NORMAL_BUTTON;
16250       checked = FALSE;
16251       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16252     }
16253     else
16254     {
16255       button_type = GD_TYPE_CHECK_BUTTON;
16256       checked = (gamebutton_info[i].setup_value != NULL ?
16257                  *gamebutton_info[i].setup_value : FALSE);
16258       event_mask = GD_EVENT_PRESSED;
16259     }
16260
16261     gi = CreateGadget(GDI_CUSTOM_ID, id,
16262                       GDI_IMAGE_ID, graphic,
16263                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16264                       GDI_X, base_x + x,
16265                       GDI_Y, base_y + y,
16266                       GDI_WIDTH, gfx->width,
16267                       GDI_HEIGHT, gfx->height,
16268                       GDI_TYPE, button_type,
16269                       GDI_STATE, GD_BUTTON_UNPRESSED,
16270                       GDI_CHECKED, checked,
16271                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16272                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16273                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16274                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16275                       GDI_DIRECT_DRAW, FALSE,
16276                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16277                       GDI_EVENT_MASK, event_mask,
16278                       GDI_CALLBACK_ACTION, HandleGameButtons,
16279                       GDI_END);
16280
16281     if (gi == NULL)
16282       Fail("cannot create gadget");
16283
16284     game_gadget[id] = gi;
16285   }
16286 }
16287
16288 void FreeGameButtons(void)
16289 {
16290   int i;
16291
16292   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16293     FreeGadget(game_gadget[i]);
16294 }
16295
16296 static void UnmapGameButtonsAtSamePosition(int id)
16297 {
16298   int i;
16299
16300   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16301     if (i != id &&
16302         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16303         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16304       UnmapGadget(game_gadget[i]);
16305 }
16306
16307 static void UnmapGameButtonsAtSamePosition_All(void)
16308 {
16309   if (setup.show_load_save_buttons)
16310   {
16311     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16312     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16313     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16314   }
16315   else if (setup.show_undo_redo_buttons)
16316   {
16317     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16319     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16320   }
16321   else
16322   {
16323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16325     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16326
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16330   }
16331 }
16332
16333 void MapLoadSaveButtons(void)
16334 {
16335   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16336   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16337
16338   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16339   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16340 }
16341
16342 void MapUndoRedoButtons(void)
16343 {
16344   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16345   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16346
16347   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16348   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16349 }
16350
16351 void ModifyPauseButtons(void)
16352 {
16353   static int ids[] =
16354   {
16355     GAME_CTRL_ID_PAUSE,
16356     GAME_CTRL_ID_PAUSE2,
16357     GAME_CTRL_ID_PANEL_PAUSE,
16358     GAME_CTRL_ID_TOUCH_PAUSE,
16359     -1
16360   };
16361   int i;
16362
16363   for (i = 0; ids[i] > -1; i++)
16364     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16365 }
16366
16367 static void MapGameButtonsExt(boolean on_tape)
16368 {
16369   int i;
16370
16371   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16372   {
16373     if ((i == GAME_CTRL_ID_UNDO ||
16374          i == GAME_CTRL_ID_REDO) &&
16375         game_status != GAME_MODE_PLAYING)
16376       continue;
16377
16378     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16379       MapGadget(game_gadget[i]);
16380   }
16381
16382   UnmapGameButtonsAtSamePosition_All();
16383
16384   RedrawGameButtons();
16385 }
16386
16387 static void UnmapGameButtonsExt(boolean on_tape)
16388 {
16389   int i;
16390
16391   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16392     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16393       UnmapGadget(game_gadget[i]);
16394 }
16395
16396 static void RedrawGameButtonsExt(boolean on_tape)
16397 {
16398   int i;
16399
16400   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16401     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16402       RedrawGadget(game_gadget[i]);
16403 }
16404
16405 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16406 {
16407   if (gi == NULL)
16408     return;
16409
16410   gi->checked = state;
16411 }
16412
16413 static void RedrawSoundButtonGadget(int id)
16414 {
16415   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16416              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16417              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16418              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16419              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16420              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16421              id);
16422
16423   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16424   RedrawGadget(game_gadget[id2]);
16425 }
16426
16427 void MapGameButtons(void)
16428 {
16429   MapGameButtonsExt(FALSE);
16430 }
16431
16432 void UnmapGameButtons(void)
16433 {
16434   UnmapGameButtonsExt(FALSE);
16435 }
16436
16437 void RedrawGameButtons(void)
16438 {
16439   RedrawGameButtonsExt(FALSE);
16440 }
16441
16442 void MapGameButtonsOnTape(void)
16443 {
16444   MapGameButtonsExt(TRUE);
16445 }
16446
16447 void UnmapGameButtonsOnTape(void)
16448 {
16449   UnmapGameButtonsExt(TRUE);
16450 }
16451
16452 void RedrawGameButtonsOnTape(void)
16453 {
16454   RedrawGameButtonsExt(TRUE);
16455 }
16456
16457 static void GameUndoRedoExt(void)
16458 {
16459   ClearPlayerAction();
16460
16461   tape.pausing = TRUE;
16462
16463   RedrawPlayfield();
16464   UpdateAndDisplayGameControlValues();
16465
16466   DrawCompleteVideoDisplay();
16467   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16468   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16469   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16470
16471   ModifyPauseButtons();
16472
16473   BackToFront();
16474 }
16475
16476 static void GameUndo(int steps)
16477 {
16478   if (!CheckEngineSnapshotList())
16479     return;
16480
16481   int tape_property_bits = tape.property_bits;
16482
16483   LoadEngineSnapshot_Undo(steps);
16484
16485   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16486
16487   GameUndoRedoExt();
16488 }
16489
16490 static void GameRedo(int steps)
16491 {
16492   if (!CheckEngineSnapshotList())
16493     return;
16494
16495   int tape_property_bits = tape.property_bits;
16496
16497   LoadEngineSnapshot_Redo(steps);
16498
16499   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16500
16501   GameUndoRedoExt();
16502 }
16503
16504 static void HandleGameButtonsExt(int id, int button)
16505 {
16506   static boolean game_undo_executed = FALSE;
16507   int steps = BUTTON_STEPSIZE(button);
16508   boolean handle_game_buttons =
16509     (game_status == GAME_MODE_PLAYING ||
16510      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16511
16512   if (!handle_game_buttons)
16513     return;
16514
16515   switch (id)
16516   {
16517     case GAME_CTRL_ID_STOP:
16518     case GAME_CTRL_ID_PANEL_STOP:
16519     case GAME_CTRL_ID_TOUCH_STOP:
16520       TapeStopGame();
16521
16522       break;
16523
16524     case GAME_CTRL_ID_PAUSE:
16525     case GAME_CTRL_ID_PAUSE2:
16526     case GAME_CTRL_ID_PANEL_PAUSE:
16527     case GAME_CTRL_ID_TOUCH_PAUSE:
16528       if (network.enabled && game_status == GAME_MODE_PLAYING)
16529       {
16530         if (tape.pausing)
16531           SendToServer_ContinuePlaying();
16532         else
16533           SendToServer_PausePlaying();
16534       }
16535       else
16536         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16537
16538       game_undo_executed = FALSE;
16539
16540       break;
16541
16542     case GAME_CTRL_ID_PLAY:
16543     case GAME_CTRL_ID_PANEL_PLAY:
16544       if (game_status == GAME_MODE_MAIN)
16545       {
16546         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16547       }
16548       else if (tape.pausing)
16549       {
16550         if (network.enabled)
16551           SendToServer_ContinuePlaying();
16552         else
16553           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16554       }
16555       break;
16556
16557     case GAME_CTRL_ID_UNDO:
16558       // Important: When using "save snapshot when collecting an item" mode,
16559       // load last (current) snapshot for first "undo" after pressing "pause"
16560       // (else the last-but-one snapshot would be loaded, because the snapshot
16561       // pointer already points to the last snapshot when pressing "pause",
16562       // which is fine for "every step/move" mode, but not for "every collect")
16563       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16564           !game_undo_executed)
16565         steps--;
16566
16567       game_undo_executed = TRUE;
16568
16569       GameUndo(steps);
16570       break;
16571
16572     case GAME_CTRL_ID_REDO:
16573       GameRedo(steps);
16574       break;
16575
16576     case GAME_CTRL_ID_SAVE:
16577       TapeQuickSave();
16578       break;
16579
16580     case GAME_CTRL_ID_LOAD:
16581       TapeQuickLoad();
16582       break;
16583
16584     case SOUND_CTRL_ID_MUSIC:
16585     case SOUND_CTRL_ID_PANEL_MUSIC:
16586       if (setup.sound_music)
16587       { 
16588         setup.sound_music = FALSE;
16589
16590         FadeMusic();
16591       }
16592       else if (audio.music_available)
16593       { 
16594         setup.sound = setup.sound_music = TRUE;
16595
16596         SetAudioMode(setup.sound);
16597
16598         if (game_status == GAME_MODE_PLAYING)
16599           PlayLevelMusic();
16600       }
16601
16602       RedrawSoundButtonGadget(id);
16603
16604       break;
16605
16606     case SOUND_CTRL_ID_LOOPS:
16607     case SOUND_CTRL_ID_PANEL_LOOPS:
16608       if (setup.sound_loops)
16609         setup.sound_loops = FALSE;
16610       else if (audio.loops_available)
16611       {
16612         setup.sound = setup.sound_loops = TRUE;
16613
16614         SetAudioMode(setup.sound);
16615       }
16616
16617       RedrawSoundButtonGadget(id);
16618
16619       break;
16620
16621     case SOUND_CTRL_ID_SIMPLE:
16622     case SOUND_CTRL_ID_PANEL_SIMPLE:
16623       if (setup.sound_simple)
16624         setup.sound_simple = FALSE;
16625       else if (audio.sound_available)
16626       {
16627         setup.sound = setup.sound_simple = TRUE;
16628
16629         SetAudioMode(setup.sound);
16630       }
16631
16632       RedrawSoundButtonGadget(id);
16633
16634       break;
16635
16636     default:
16637       break;
16638   }
16639 }
16640
16641 static void HandleGameButtons(struct GadgetInfo *gi)
16642 {
16643   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16644 }
16645
16646 void HandleSoundButtonKeys(Key key)
16647 {
16648   if (key == setup.shortcut.sound_simple)
16649     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16650   else if (key == setup.shortcut.sound_loops)
16651     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16652   else if (key == setup.shortcut.sound_music)
16653     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16654 }