added separate time counter for tape time frames
[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_RESTART            8
1021 #define GAME_CTRL_ID_PANEL_STOP         9
1022 #define GAME_CTRL_ID_PANEL_PAUSE        10
1023 #define GAME_CTRL_ID_PANEL_PLAY         11
1024 #define GAME_CTRL_ID_PANEL_RESTART      12
1025 #define GAME_CTRL_ID_TOUCH_STOP         13
1026 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1027 #define GAME_CTRL_ID_TOUCH_RESTART      15
1028 #define SOUND_CTRL_ID_MUSIC             16
1029 #define SOUND_CTRL_ID_LOOPS             17
1030 #define SOUND_CTRL_ID_SIMPLE            18
1031 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1032 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1033 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1034
1035 #define NUM_GAME_BUTTONS                22
1036
1037
1038 // forward declaration for internal use
1039
1040 static void CreateField(int, int, int);
1041
1042 static void ResetGfxAnimation(int, int);
1043
1044 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1045 static void AdvanceFrameAndPlayerCounters(int);
1046
1047 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1048 static boolean MovePlayer(struct PlayerInfo *, int, int);
1049 static void ScrollPlayer(struct PlayerInfo *, int);
1050 static void ScrollScreen(struct PlayerInfo *, int);
1051
1052 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1053 static boolean DigFieldByCE(int, int, int);
1054 static boolean SnapField(struct PlayerInfo *, int, int);
1055 static boolean DropElement(struct PlayerInfo *);
1056
1057 static void InitBeltMovement(void);
1058 static void CloseAllOpenTimegates(void);
1059 static void CheckGravityMovement(struct PlayerInfo *);
1060 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1061 static void KillPlayerUnlessEnemyProtected(int, int);
1062 static void KillPlayerUnlessExplosionProtected(int, int);
1063
1064 static void CheckNextToConditions(int, int);
1065 static void TestIfPlayerNextToCustomElement(int, int);
1066 static void TestIfPlayerTouchesCustomElement(int, int);
1067 static void TestIfElementNextToCustomElement(int, int);
1068 static void TestIfElementTouchesCustomElement(int, int);
1069 static void TestIfElementHitsCustomElement(int, int, int);
1070
1071 static void HandleElementChange(int, int, int);
1072 static void ExecuteCustomElementAction(int, int, int, int);
1073 static boolean ChangeElement(int, int, int, int);
1074
1075 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1078 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1080 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1081         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1082 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1083         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1084 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1085         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1086
1087 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1088 #define CheckElementChange(x, y, e, te, ev)                             \
1089         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1090 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1091         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1092 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1093         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1094 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1095         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1096
1097 static void PlayLevelSound(int, int, int);
1098 static void PlayLevelSoundNearest(int, int, int);
1099 static void PlayLevelSoundAction(int, int, int);
1100 static void PlayLevelSoundElementAction(int, int, int, int);
1101 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1102 static void PlayLevelSoundActionIfLoop(int, int, int);
1103 static void StopLevelSoundActionIfLoop(int, int, int);
1104 static void PlayLevelMusic(void);
1105 static void FadeLevelSoundsAndMusic(void);
1106
1107 static void HandleGameButtons(struct GadgetInfo *);
1108
1109 int AmoebaNeighbourNr(int, int);
1110 void AmoebaToDiamond(int, int);
1111 void ContinueMoving(int, int);
1112 void Bang(int, int);
1113 void InitMovDir(int, int);
1114 void InitAmoebaNr(int, int);
1115 void NewHighScore(int, boolean);
1116
1117 void TestIfGoodThingHitsBadThing(int, int, int);
1118 void TestIfBadThingHitsGoodThing(int, int, int);
1119 void TestIfPlayerTouchesBadThing(int, int);
1120 void TestIfPlayerRunsIntoBadThing(int, int, int);
1121 void TestIfBadThingTouchesPlayer(int, int);
1122 void TestIfBadThingRunsIntoPlayer(int, int, int);
1123 void TestIfFriendTouchesBadThing(int, int);
1124 void TestIfBadThingTouchesFriend(int, int);
1125 void TestIfBadThingTouchesOtherBadThing(int, int);
1126 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1127
1128 void KillPlayer(struct PlayerInfo *);
1129 void BuryPlayer(struct PlayerInfo *);
1130 void RemovePlayer(struct PlayerInfo *);
1131 void ExitPlayer(struct PlayerInfo *);
1132
1133 static int getInvisibleActiveFromInvisibleElement(int);
1134 static int getInvisibleFromInvisibleActiveElement(int);
1135
1136 static void TestFieldAfterSnapping(int, int, int, int, int);
1137
1138 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1139
1140 // for detection of endless loops, caused by custom element programming
1141 // (using maximal playfield width x 10 is just a rough approximation)
1142 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1143
1144 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1145 {                                                                       \
1146   if (recursion_loop_detected)                                          \
1147     return (rc);                                                        \
1148                                                                         \
1149   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1150   {                                                                     \
1151     recursion_loop_detected = TRUE;                                     \
1152     recursion_loop_element = (e);                                       \
1153   }                                                                     \
1154                                                                         \
1155   recursion_loop_depth++;                                               \
1156 }
1157
1158 #define RECURSION_LOOP_DETECTION_END()                                  \
1159 {                                                                       \
1160   recursion_loop_depth--;                                               \
1161 }
1162
1163 static int recursion_loop_depth;
1164 static boolean recursion_loop_detected;
1165 static boolean recursion_loop_element;
1166
1167 static int map_player_action[MAX_PLAYERS];
1168
1169
1170 // ----------------------------------------------------------------------------
1171 // definition of elements that automatically change to other elements after
1172 // a specified time, eventually calling a function when changing
1173 // ----------------------------------------------------------------------------
1174
1175 // forward declaration for changer functions
1176 static void InitBuggyBase(int, int);
1177 static void WarnBuggyBase(int, int);
1178
1179 static void InitTrap(int, int);
1180 static void ActivateTrap(int, int);
1181 static void ChangeActiveTrap(int, int);
1182
1183 static void InitRobotWheel(int, int);
1184 static void RunRobotWheel(int, int);
1185 static void StopRobotWheel(int, int);
1186
1187 static void InitTimegateWheel(int, int);
1188 static void RunTimegateWheel(int, int);
1189
1190 static void InitMagicBallDelay(int, int);
1191 static void ActivateMagicBall(int, int);
1192
1193 struct ChangingElementInfo
1194 {
1195   int element;
1196   int target_element;
1197   int change_delay;
1198   void (*pre_change_function)(int x, int y);
1199   void (*change_function)(int x, int y);
1200   void (*post_change_function)(int x, int y);
1201 };
1202
1203 static struct ChangingElementInfo change_delay_list[] =
1204 {
1205   {
1206     EL_NUT_BREAKING,
1207     EL_EMERALD,
1208     6,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_PEARL_BREAKING,
1215     EL_EMPTY,
1216     8,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EXIT_OPENING,
1223     EL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EXIT_CLOSING,
1231     EL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_STEEL_EXIT_OPENING,
1239     EL_STEEL_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_STEEL_EXIT_CLOSING,
1247     EL_STEEL_EXIT_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_EXIT_OPENING,
1255     EL_EM_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_EXIT_CLOSING,
1263     EL_EMPTY,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_EM_STEEL_EXIT_OPENING,
1271     EL_EM_STEEL_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_EM_STEEL_EXIT_CLOSING,
1279     EL_STEELWALL,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SP_EXIT_OPENING,
1287     EL_SP_EXIT_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SP_EXIT_CLOSING,
1295     EL_SP_EXIT_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_SWITCHGATE_OPENING,
1303     EL_SWITCHGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_SWITCHGATE_CLOSING,
1311     EL_SWITCHGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317   {
1318     EL_TIMEGATE_OPENING,
1319     EL_TIMEGATE_OPEN,
1320     29,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_TIMEGATE_CLOSING,
1327     EL_TIMEGATE_CLOSED,
1328     29,
1329     NULL,
1330     NULL,
1331     NULL
1332   },
1333
1334   {
1335     EL_ACID_SPLASH_LEFT,
1336     EL_EMPTY,
1337     8,
1338     NULL,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_ACID_SPLASH_RIGHT,
1344     EL_EMPTY,
1345     8,
1346     NULL,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE,
1352     EL_SP_BUGGY_BASE_ACTIVATING,
1353     0,
1354     InitBuggyBase,
1355     NULL,
1356     NULL
1357   },
1358   {
1359     EL_SP_BUGGY_BASE_ACTIVATING,
1360     EL_SP_BUGGY_BASE_ACTIVE,
1361     0,
1362     InitBuggyBase,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_SP_BUGGY_BASE_ACTIVE,
1368     EL_SP_BUGGY_BASE,
1369     0,
1370     InitBuggyBase,
1371     WarnBuggyBase,
1372     NULL
1373   },
1374   {
1375     EL_TRAP,
1376     EL_TRAP_ACTIVE,
1377     0,
1378     InitTrap,
1379     NULL,
1380     ActivateTrap
1381   },
1382   {
1383     EL_TRAP_ACTIVE,
1384     EL_TRAP,
1385     31,
1386     NULL,
1387     ChangeActiveTrap,
1388     NULL
1389   },
1390   {
1391     EL_ROBOT_WHEEL_ACTIVE,
1392     EL_ROBOT_WHEEL,
1393     0,
1394     InitRobotWheel,
1395     RunRobotWheel,
1396     StopRobotWheel
1397   },
1398   {
1399     EL_TIMEGATE_SWITCH_ACTIVE,
1400     EL_TIMEGATE_SWITCH,
1401     0,
1402     InitTimegateWheel,
1403     RunTimegateWheel,
1404     NULL
1405   },
1406   {
1407     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1408     EL_DC_TIMEGATE_SWITCH,
1409     0,
1410     InitTimegateWheel,
1411     RunTimegateWheel,
1412     NULL
1413   },
1414   {
1415     EL_EMC_MAGIC_BALL_ACTIVE,
1416     EL_EMC_MAGIC_BALL_ACTIVE,
1417     0,
1418     InitMagicBallDelay,
1419     NULL,
1420     ActivateMagicBall
1421   },
1422   {
1423     EL_EMC_SPRING_BUMPER_ACTIVE,
1424     EL_EMC_SPRING_BUMPER,
1425     8,
1426     NULL,
1427     NULL,
1428     NULL
1429   },
1430   {
1431     EL_DIAGONAL_SHRINKING,
1432     EL_UNDEFINED,
1433     0,
1434     NULL,
1435     NULL,
1436     NULL
1437   },
1438   {
1439     EL_DIAGONAL_GROWING,
1440     EL_UNDEFINED,
1441     0,
1442     NULL,
1443     NULL,
1444     NULL,
1445   },
1446
1447   {
1448     EL_UNDEFINED,
1449     EL_UNDEFINED,
1450     -1,
1451     NULL,
1452     NULL,
1453     NULL
1454   }
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int push_delay_fixed, push_delay_random;
1461 }
1462 push_delay_list[] =
1463 {
1464   { EL_SPRING,                  0, 0 },
1465   { EL_BALLOON,                 0, 0 },
1466
1467   { EL_SOKOBAN_OBJECT,          2, 0 },
1468   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1469   { EL_SATELLITE,               2, 0 },
1470   { EL_SP_DISK_YELLOW,          2, 0 },
1471
1472   { EL_UNDEFINED,               0, 0 },
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int move_stepsize;
1479 }
1480 move_stepsize_list[] =
1481 {
1482   { EL_AMOEBA_DROP,             2 },
1483   { EL_AMOEBA_DROPPING,         2 },
1484   { EL_QUICKSAND_FILLING,       1 },
1485   { EL_QUICKSAND_EMPTYING,      1 },
1486   { EL_QUICKSAND_FAST_FILLING,  2 },
1487   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1488   { EL_MAGIC_WALL_FILLING,      2 },
1489   { EL_MAGIC_WALL_EMPTYING,     2 },
1490   { EL_BD_MAGIC_WALL_FILLING,   2 },
1491   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1492   { EL_DC_MAGIC_WALL_FILLING,   2 },
1493   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1494
1495   { EL_UNDEFINED,               0 },
1496 };
1497
1498 struct
1499 {
1500   int element;
1501   int count;
1502 }
1503 collect_count_list[] =
1504 {
1505   { EL_EMERALD,                 1 },
1506   { EL_BD_DIAMOND,              1 },
1507   { EL_EMERALD_YELLOW,          1 },
1508   { EL_EMERALD_RED,             1 },
1509   { EL_EMERALD_PURPLE,          1 },
1510   { EL_DIAMOND,                 3 },
1511   { EL_SP_INFOTRON,             1 },
1512   { EL_PEARL,                   5 },
1513   { EL_CRYSTAL,                 8 },
1514
1515   { EL_UNDEFINED,               0 },
1516 };
1517
1518 struct
1519 {
1520   int element;
1521   int direction;
1522 }
1523 access_direction_list[] =
1524 {
1525   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1528   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1529   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1531   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1532   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1533   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1534   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1535   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1536
1537   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1538   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1539   { EL_SP_PORT_UP,                                                   MV_DOWN },
1540   { EL_SP_PORT_DOWN,                                         MV_UP           },
1541   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1542   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1543   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1545   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1546   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1547   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1548   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1549   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1550   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1551   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1552   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1553   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1554   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1555   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1556
1557   { EL_UNDEFINED,                       MV_NONE                              }
1558 };
1559
1560 static struct XY xy_topdown[] =
1561 {
1562   {  0, -1 },
1563   { -1,  0 },
1564   { +1,  0 },
1565   {  0, +1 }
1566 };
1567
1568 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1569
1570 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1571 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1572 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1573                                  IS_JUST_CHANGING(x, y))
1574
1575 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1576
1577 // static variables for playfield scan mode (scanning forward or backward)
1578 static int playfield_scan_start_x = 0;
1579 static int playfield_scan_start_y = 0;
1580 static int playfield_scan_delta_x = 1;
1581 static int playfield_scan_delta_y = 1;
1582
1583 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1584                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1585                                      (y) += playfield_scan_delta_y)     \
1586                                 for ((x) = playfield_scan_start_x;      \
1587                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1588                                      (x) += playfield_scan_delta_x)
1589
1590 #ifdef DEBUG
1591 void DEBUG_SetMaximumDynamite(void)
1592 {
1593   int i;
1594
1595   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1596     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1597       local_player->inventory_element[local_player->inventory_size++] =
1598         EL_DYNAMITE;
1599 }
1600 #endif
1601
1602 static void InitPlayfieldScanModeVars(void)
1603 {
1604   if (game.use_reverse_scan_direction)
1605   {
1606     playfield_scan_start_x = lev_fieldx - 1;
1607     playfield_scan_start_y = lev_fieldy - 1;
1608
1609     playfield_scan_delta_x = -1;
1610     playfield_scan_delta_y = -1;
1611   }
1612   else
1613   {
1614     playfield_scan_start_x = 0;
1615     playfield_scan_start_y = 0;
1616
1617     playfield_scan_delta_x = 1;
1618     playfield_scan_delta_y = 1;
1619   }
1620 }
1621
1622 static void InitPlayfieldScanMode(int mode)
1623 {
1624   game.use_reverse_scan_direction =
1625     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1626
1627   InitPlayfieldScanModeVars();
1628 }
1629
1630 static int get_move_delay_from_stepsize(int move_stepsize)
1631 {
1632   move_stepsize =
1633     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1634
1635   // make sure that stepsize value is always a power of 2
1636   move_stepsize = (1 << log_2(move_stepsize));
1637
1638   return TILEX / move_stepsize;
1639 }
1640
1641 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1642                                boolean init_game)
1643 {
1644   int player_nr = player->index_nr;
1645   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1646   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1647
1648   // do no immediately change move delay -- the player might just be moving
1649   player->move_delay_value_next = move_delay;
1650
1651   // information if player can move must be set separately
1652   player->cannot_move = cannot_move;
1653
1654   if (init_game)
1655   {
1656     player->move_delay       = game.initial_move_delay[player_nr];
1657     player->move_delay_value = game.initial_move_delay_value[player_nr];
1658
1659     player->move_delay_value_next = -1;
1660
1661     player->move_delay_reset_counter = 0;
1662   }
1663 }
1664
1665 void GetPlayerConfig(void)
1666 {
1667   GameFrameDelay = setup.game_frame_delay;
1668
1669   if (!audio.sound_available)
1670     setup.sound_simple = FALSE;
1671
1672   if (!audio.loops_available)
1673     setup.sound_loops = FALSE;
1674
1675   if (!audio.music_available)
1676     setup.sound_music = FALSE;
1677
1678   if (!video.fullscreen_available)
1679     setup.fullscreen = FALSE;
1680
1681   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1682
1683   SetAudioMode(setup.sound);
1684 }
1685
1686 int GetElementFromGroupElement(int element)
1687 {
1688   if (IS_GROUP_ELEMENT(element))
1689   {
1690     struct ElementGroupInfo *group = element_info[element].group;
1691     int last_anim_random_frame = gfx.anim_random_frame;
1692     int element_pos;
1693
1694     if (group->choice_mode == ANIM_RANDOM)
1695       gfx.anim_random_frame = RND(group->num_elements_resolved);
1696
1697     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1698                                     group->choice_mode, 0,
1699                                     group->choice_pos);
1700
1701     if (group->choice_mode == ANIM_RANDOM)
1702       gfx.anim_random_frame = last_anim_random_frame;
1703
1704     group->choice_pos++;
1705
1706     element = group->element_resolved[element_pos];
1707   }
1708
1709   return element;
1710 }
1711
1712 static void IncrementSokobanFieldsNeeded(void)
1713 {
1714   if (level.sb_fields_needed)
1715     game.sokoban_fields_still_needed++;
1716 }
1717
1718 static void IncrementSokobanObjectsNeeded(void)
1719 {
1720   if (level.sb_objects_needed)
1721     game.sokoban_objects_still_needed++;
1722 }
1723
1724 static void DecrementSokobanFieldsNeeded(void)
1725 {
1726   if (game.sokoban_fields_still_needed > 0)
1727     game.sokoban_fields_still_needed--;
1728 }
1729
1730 static void DecrementSokobanObjectsNeeded(void)
1731 {
1732   if (game.sokoban_objects_still_needed > 0)
1733     game.sokoban_objects_still_needed--;
1734 }
1735
1736 static void InitPlayerField(int x, int y, int element, boolean init_game)
1737 {
1738   if (element == EL_SP_MURPHY)
1739   {
1740     if (init_game)
1741     {
1742       if (stored_player[0].present)
1743       {
1744         Tile[x][y] = EL_SP_MURPHY_CLONE;
1745
1746         return;
1747       }
1748       else
1749       {
1750         stored_player[0].initial_element = element;
1751         stored_player[0].use_murphy = TRUE;
1752
1753         if (!level.use_artwork_element[0])
1754           stored_player[0].artwork_element = EL_SP_MURPHY;
1755       }
1756
1757       Tile[x][y] = EL_PLAYER_1;
1758     }
1759   }
1760
1761   if (init_game)
1762   {
1763     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1764     int jx = player->jx, jy = player->jy;
1765
1766     player->present = TRUE;
1767
1768     player->block_last_field = (element == EL_SP_MURPHY ?
1769                                 level.sp_block_last_field :
1770                                 level.block_last_field);
1771
1772     // ---------- initialize player's last field block delay ------------------
1773
1774     // always start with reliable default value (no adjustment needed)
1775     player->block_delay_adjustment = 0;
1776
1777     // special case 1: in Supaplex, Murphy blocks last field one more frame
1778     if (player->block_last_field && element == EL_SP_MURPHY)
1779       player->block_delay_adjustment = 1;
1780
1781     // special case 2: in game engines before 3.1.1, blocking was different
1782     if (game.use_block_last_field_bug)
1783       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1784
1785     if (!network.enabled || player->connected_network)
1786     {
1787       player->active = TRUE;
1788
1789       // remove potentially duplicate players
1790       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1791         StorePlayer[jx][jy] = 0;
1792
1793       StorePlayer[x][y] = Tile[x][y];
1794
1795 #if DEBUG_INIT_PLAYER
1796       Debug("game:init:player", "- player element %d activated",
1797             player->element_nr);
1798       Debug("game:init:player", "  (local player is %d and currently %s)",
1799             local_player->element_nr,
1800             local_player->active ? "active" : "not active");
1801     }
1802 #endif
1803
1804     Tile[x][y] = EL_EMPTY;
1805
1806     player->jx = player->last_jx = x;
1807     player->jy = player->last_jy = y;
1808   }
1809
1810   // always check if player was just killed and should be reanimated
1811   {
1812     int player_nr = GET_PLAYER_NR(element);
1813     struct PlayerInfo *player = &stored_player[player_nr];
1814
1815     if (player->active && player->killed)
1816       player->reanimated = TRUE; // if player was just killed, reanimate him
1817   }
1818 }
1819
1820 static void InitField(int x, int y, boolean init_game)
1821 {
1822   int element = Tile[x][y];
1823
1824   switch (element)
1825   {
1826     case EL_SP_MURPHY:
1827     case EL_PLAYER_1:
1828     case EL_PLAYER_2:
1829     case EL_PLAYER_3:
1830     case EL_PLAYER_4:
1831       InitPlayerField(x, y, element, init_game);
1832       break;
1833
1834     case EL_SOKOBAN_FIELD_PLAYER:
1835       element = Tile[x][y] = EL_PLAYER_1;
1836       InitField(x, y, init_game);
1837
1838       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1839       InitField(x, y, init_game);
1840       break;
1841
1842     case EL_SOKOBAN_FIELD_EMPTY:
1843       IncrementSokobanFieldsNeeded();
1844       break;
1845
1846     case EL_SOKOBAN_OBJECT:
1847       IncrementSokobanObjectsNeeded();
1848       break;
1849
1850     case EL_STONEBLOCK:
1851       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1852         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1853       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1854         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1855       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1856         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1857       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1858         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1859       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1860         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1861       break;
1862
1863     case EL_BUG:
1864     case EL_BUG_RIGHT:
1865     case EL_BUG_UP:
1866     case EL_BUG_LEFT:
1867     case EL_BUG_DOWN:
1868     case EL_SPACESHIP:
1869     case EL_SPACESHIP_RIGHT:
1870     case EL_SPACESHIP_UP:
1871     case EL_SPACESHIP_LEFT:
1872     case EL_SPACESHIP_DOWN:
1873     case EL_BD_BUTTERFLY:
1874     case EL_BD_BUTTERFLY_RIGHT:
1875     case EL_BD_BUTTERFLY_UP:
1876     case EL_BD_BUTTERFLY_LEFT:
1877     case EL_BD_BUTTERFLY_DOWN:
1878     case EL_BD_FIREFLY:
1879     case EL_BD_FIREFLY_RIGHT:
1880     case EL_BD_FIREFLY_UP:
1881     case EL_BD_FIREFLY_LEFT:
1882     case EL_BD_FIREFLY_DOWN:
1883     case EL_PACMAN_RIGHT:
1884     case EL_PACMAN_UP:
1885     case EL_PACMAN_LEFT:
1886     case EL_PACMAN_DOWN:
1887     case EL_YAMYAM:
1888     case EL_YAMYAM_LEFT:
1889     case EL_YAMYAM_RIGHT:
1890     case EL_YAMYAM_UP:
1891     case EL_YAMYAM_DOWN:
1892     case EL_DARK_YAMYAM:
1893     case EL_ROBOT:
1894     case EL_PACMAN:
1895     case EL_SP_SNIKSNAK:
1896     case EL_SP_ELECTRON:
1897     case EL_MOLE:
1898     case EL_MOLE_LEFT:
1899     case EL_MOLE_RIGHT:
1900     case EL_MOLE_UP:
1901     case EL_MOLE_DOWN:
1902     case EL_SPRING_LEFT:
1903     case EL_SPRING_RIGHT:
1904       InitMovDir(x, y);
1905       break;
1906
1907     case EL_AMOEBA_FULL:
1908     case EL_BD_AMOEBA:
1909       InitAmoebaNr(x, y);
1910       break;
1911
1912     case EL_AMOEBA_DROP:
1913       if (y == lev_fieldy - 1)
1914       {
1915         Tile[x][y] = EL_AMOEBA_GROWING;
1916         Store[x][y] = EL_AMOEBA_WET;
1917       }
1918       break;
1919
1920     case EL_DYNAMITE_ACTIVE:
1921     case EL_SP_DISK_RED_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1923     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1924     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1925     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1926       MovDelay[x][y] = 96;
1927       break;
1928
1929     case EL_EM_DYNAMITE_ACTIVE:
1930       MovDelay[x][y] = 32;
1931       break;
1932
1933     case EL_LAMP:
1934       game.lights_still_needed++;
1935       break;
1936
1937     case EL_PENGUIN:
1938       game.friends_still_needed++;
1939       break;
1940
1941     case EL_PIG:
1942     case EL_DRAGON:
1943       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1944       break;
1945
1946     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1955     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1956     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1957     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1958       if (init_game)
1959       {
1960         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1961         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1962         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1963
1964         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1965         {
1966           game.belt_dir[belt_nr] = belt_dir;
1967           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1968         }
1969         else    // more than one switch -- set it like the first switch
1970         {
1971           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1972         }
1973       }
1974       break;
1975
1976     case EL_LIGHT_SWITCH_ACTIVE:
1977       if (init_game)
1978         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1979       break;
1980
1981     case EL_INVISIBLE_STEELWALL:
1982     case EL_INVISIBLE_WALL:
1983     case EL_INVISIBLE_SAND:
1984       if (game.light_time_left > 0 ||
1985           game.lenses_time_left > 0)
1986         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1987       break;
1988
1989     case EL_EMC_MAGIC_BALL:
1990       if (game.ball_active)
1991         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1992       break;
1993
1994     case EL_EMC_MAGIC_BALL_SWITCH:
1995       if (game.ball_active)
1996         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1997       break;
1998
1999     case EL_TRIGGER_PLAYER:
2000     case EL_TRIGGER_ELEMENT:
2001     case EL_TRIGGER_CE_VALUE:
2002     case EL_TRIGGER_CE_SCORE:
2003     case EL_SELF:
2004     case EL_ANY_ELEMENT:
2005     case EL_CURRENT_CE_VALUE:
2006     case EL_CURRENT_CE_SCORE:
2007     case EL_PREV_CE_1:
2008     case EL_PREV_CE_2:
2009     case EL_PREV_CE_3:
2010     case EL_PREV_CE_4:
2011     case EL_PREV_CE_5:
2012     case EL_PREV_CE_6:
2013     case EL_PREV_CE_7:
2014     case EL_PREV_CE_8:
2015     case EL_NEXT_CE_1:
2016     case EL_NEXT_CE_2:
2017     case EL_NEXT_CE_3:
2018     case EL_NEXT_CE_4:
2019     case EL_NEXT_CE_5:
2020     case EL_NEXT_CE_6:
2021     case EL_NEXT_CE_7:
2022     case EL_NEXT_CE_8:
2023       // reference elements should not be used on the playfield
2024       Tile[x][y] = EL_EMPTY;
2025       break;
2026
2027     default:
2028       if (IS_CUSTOM_ELEMENT(element))
2029       {
2030         if (CAN_MOVE(element))
2031           InitMovDir(x, y);
2032
2033         if (!element_info[element].use_last_ce_value || init_game)
2034           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2035       }
2036       else if (IS_GROUP_ELEMENT(element))
2037       {
2038         Tile[x][y] = GetElementFromGroupElement(element);
2039
2040         InitField(x, y, init_game);
2041       }
2042       else if (IS_EMPTY_ELEMENT(element))
2043       {
2044         GfxElementEmpty[x][y] = element;
2045         Tile[x][y] = EL_EMPTY;
2046
2047         if (element_info[element].use_gfx_element)
2048           game.use_masked_elements = TRUE;
2049       }
2050
2051       break;
2052   }
2053
2054   if (!init_game)
2055     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2056 }
2057
2058 static void InitField_WithBug1(int x, int y, boolean init_game)
2059 {
2060   InitField(x, y, init_game);
2061
2062   // not needed to call InitMovDir() -- already done by InitField()!
2063   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2064       CAN_MOVE(Tile[x][y]))
2065     InitMovDir(x, y);
2066 }
2067
2068 static void InitField_WithBug2(int x, int y, boolean init_game)
2069 {
2070   int old_element = Tile[x][y];
2071
2072   InitField(x, y, init_game);
2073
2074   // not needed to call InitMovDir() -- already done by InitField()!
2075   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2076       CAN_MOVE(old_element) &&
2077       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2078     InitMovDir(x, y);
2079
2080   /* this case is in fact a combination of not less than three bugs:
2081      first, it calls InitMovDir() for elements that can move, although this is
2082      already done by InitField(); then, it checks the element that was at this
2083      field _before_ the call to InitField() (which can change it); lastly, it
2084      was not called for "mole with direction" elements, which were treated as
2085      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2086   */
2087 }
2088
2089 static int get_key_element_from_nr(int key_nr)
2090 {
2091   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2092                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2093                           EL_EM_KEY_1 : EL_KEY_1);
2094
2095   return key_base_element + key_nr;
2096 }
2097
2098 static int get_next_dropped_element(struct PlayerInfo *player)
2099 {
2100   return (player->inventory_size > 0 ?
2101           player->inventory_element[player->inventory_size - 1] :
2102           player->inventory_infinite_element != EL_UNDEFINED ?
2103           player->inventory_infinite_element :
2104           player->dynabombs_left > 0 ?
2105           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2106           EL_UNDEFINED);
2107 }
2108
2109 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2110 {
2111   // pos >= 0: get element from bottom of the stack;
2112   // pos <  0: get element from top of the stack
2113
2114   if (pos < 0)
2115   {
2116     int min_inventory_size = -pos;
2117     int inventory_pos = player->inventory_size - min_inventory_size;
2118     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2119
2120     return (player->inventory_size >= min_inventory_size ?
2121             player->inventory_element[inventory_pos] :
2122             player->inventory_infinite_element != EL_UNDEFINED ?
2123             player->inventory_infinite_element :
2124             player->dynabombs_left >= min_dynabombs_left ?
2125             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2126             EL_UNDEFINED);
2127   }
2128   else
2129   {
2130     int min_dynabombs_left = pos + 1;
2131     int min_inventory_size = pos + 1 - player->dynabombs_left;
2132     int inventory_pos = pos - player->dynabombs_left;
2133
2134     return (player->inventory_infinite_element != EL_UNDEFINED ?
2135             player->inventory_infinite_element :
2136             player->dynabombs_left >= min_dynabombs_left ?
2137             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2138             player->inventory_size >= min_inventory_size ?
2139             player->inventory_element[inventory_pos] :
2140             EL_UNDEFINED);
2141   }
2142 }
2143
2144 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2145 {
2146   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2147   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2148   int compare_result;
2149
2150   if (gpo1->sort_priority != gpo2->sort_priority)
2151     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2152   else
2153     compare_result = gpo1->nr - gpo2->nr;
2154
2155   return compare_result;
2156 }
2157
2158 int getPlayerInventorySize(int player_nr)
2159 {
2160   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2161     return game_em.ply[player_nr]->dynamite;
2162   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2163     return game_sp.red_disk_count;
2164   else
2165     return stored_player[player_nr].inventory_size;
2166 }
2167
2168 static void InitGameControlValues(void)
2169 {
2170   int i;
2171
2172   for (i = 0; game_panel_controls[i].nr != -1; i++)
2173   {
2174     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2175     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2176     struct TextPosInfo *pos = gpc->pos;
2177     int nr = gpc->nr;
2178     int type = gpc->type;
2179
2180     if (nr != i)
2181     {
2182       Error("'game_panel_controls' structure corrupted at %d", i);
2183
2184       Fail("this should not happen -- please debug");
2185     }
2186
2187     // force update of game controls after initialization
2188     gpc->value = gpc->last_value = -1;
2189     gpc->frame = gpc->last_frame = -1;
2190     gpc->gfx_frame = -1;
2191
2192     // determine panel value width for later calculation of alignment
2193     if (type == TYPE_INTEGER || type == TYPE_STRING)
2194     {
2195       pos->width = pos->size * getFontWidth(pos->font);
2196       pos->height = getFontHeight(pos->font);
2197     }
2198     else if (type == TYPE_ELEMENT)
2199     {
2200       pos->width = pos->size;
2201       pos->height = pos->size;
2202     }
2203
2204     // fill structure for game panel draw order
2205     gpo->nr = gpc->nr;
2206     gpo->sort_priority = pos->sort_priority;
2207   }
2208
2209   // sort game panel controls according to sort_priority and control number
2210   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2211         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2212 }
2213
2214 static void UpdatePlayfieldElementCount(void)
2215 {
2216   boolean use_element_count = FALSE;
2217   int i, j, x, y;
2218
2219   // first check if it is needed at all to calculate playfield element count
2220   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2221     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2222       use_element_count = TRUE;
2223
2224   if (!use_element_count)
2225     return;
2226
2227   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2228     element_info[i].element_count = 0;
2229
2230   SCAN_PLAYFIELD(x, y)
2231   {
2232     element_info[Tile[x][y]].element_count++;
2233   }
2234
2235   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2236     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2237       if (IS_IN_GROUP(j, i))
2238         element_info[EL_GROUP_START + i].element_count +=
2239           element_info[j].element_count;
2240 }
2241
2242 static void UpdateGameControlValues(void)
2243 {
2244   int i, k;
2245   int time = (game.LevelSolved ?
2246               game.LevelSolved_CountingTime :
2247               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2248               game_em.lev->time :
2249               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2250               game_sp.time_played :
2251               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252               game_mm.energy_left :
2253               game.no_level_time_limit ? TimePlayed : TimeLeft);
2254   int score = (game.LevelSolved ?
2255                game.LevelSolved_CountingScore :
2256                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2257                game_em.lev->score :
2258                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2259                game_sp.score :
2260                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2261                game_mm.score :
2262                game.score);
2263   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2264               game_em.lev->gems_needed :
2265               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2266               game_sp.infotrons_still_needed :
2267               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2268               game_mm.kettles_still_needed :
2269               game.gems_still_needed);
2270   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2271                      game_em.lev->gems_needed > 0 :
2272                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2273                      game_sp.infotrons_still_needed > 0 :
2274                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2275                      game_mm.kettles_still_needed > 0 ||
2276                      game_mm.lights_still_needed > 0 :
2277                      game.gems_still_needed > 0 ||
2278                      game.sokoban_fields_still_needed > 0 ||
2279                      game.sokoban_objects_still_needed > 0 ||
2280                      game.lights_still_needed > 0);
2281   int health = (game.LevelSolved ?
2282                 game.LevelSolved_CountingHealth :
2283                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2284                 MM_HEALTH(game_mm.laser_overload_value) :
2285                 game.health);
2286   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2287
2288   UpdatePlayfieldElementCount();
2289
2290   // update game panel control values
2291
2292   // used instead of "level_nr" (for network games)
2293   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2294   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2295
2296   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2297   for (i = 0; i < MAX_NUM_KEYS; i++)
2298     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2299   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2300   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2301
2302   if (game.centered_player_nr == -1)
2303   {
2304     for (i = 0; i < MAX_PLAYERS; i++)
2305     {
2306       // only one player in Supaplex game engine
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2308         break;
2309
2310       for (k = 0; k < MAX_NUM_KEYS; k++)
2311       {
2312         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313         {
2314           if (game_em.ply[i]->keys & (1 << k))
2315             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316               get_key_element_from_nr(k);
2317         }
2318         else if (stored_player[i].key[k])
2319           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2320             get_key_element_from_nr(k);
2321       }
2322
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         getPlayerInventorySize(i);
2325
2326       if (stored_player[i].num_white_keys > 0)
2327         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2328           EL_DC_KEY_WHITE;
2329
2330       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2331         stored_player[i].num_white_keys;
2332     }
2333   }
2334   else
2335   {
2336     int player_nr = game.centered_player_nr;
2337
2338     for (k = 0; k < MAX_NUM_KEYS; k++)
2339     {
2340       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2341       {
2342         if (game_em.ply[player_nr]->keys & (1 << k))
2343           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2344             get_key_element_from_nr(k);
2345       }
2346       else if (stored_player[player_nr].key[k])
2347         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2348           get_key_element_from_nr(k);
2349     }
2350
2351     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2352       getPlayerInventorySize(player_nr);
2353
2354     if (stored_player[player_nr].num_white_keys > 0)
2355       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2356
2357     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2358       stored_player[player_nr].num_white_keys;
2359   }
2360
2361   // re-arrange keys on game panel, if needed or if defined by style settings
2362   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2363   {
2364     int nr = GAME_PANEL_KEY_1 + i;
2365     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2366     struct TextPosInfo *pos = gpc->pos;
2367
2368     // skip check if key is not in the player's inventory
2369     if (gpc->value == EL_EMPTY)
2370       continue;
2371
2372     // check if keys should be arranged on panel from left to right
2373     if (pos->style == STYLE_LEFTMOST_POSITION)
2374     {
2375       // check previous key positions (left from current key)
2376       for (k = 0; k < i; k++)
2377       {
2378         int nr_new = GAME_PANEL_KEY_1 + k;
2379
2380         if (game_panel_controls[nr_new].value == EL_EMPTY)
2381         {
2382           game_panel_controls[nr_new].value = gpc->value;
2383           gpc->value = EL_EMPTY;
2384
2385           break;
2386         }
2387       }
2388     }
2389
2390     // check if "undefined" keys can be placed at some other position
2391     if (pos->x == -1 && pos->y == -1)
2392     {
2393       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2394
2395       // 1st try: display key at the same position as normal or EM keys
2396       if (game_panel_controls[nr_new].value == EL_EMPTY)
2397       {
2398         game_panel_controls[nr_new].value = gpc->value;
2399       }
2400       else
2401       {
2402         // 2nd try: display key at the next free position in the key panel
2403         for (k = 0; k < STD_NUM_KEYS; k++)
2404         {
2405           nr_new = GAME_PANEL_KEY_1 + k;
2406
2407           if (game_panel_controls[nr_new].value == EL_EMPTY)
2408           {
2409             game_panel_controls[nr_new].value = gpc->value;
2410
2411             break;
2412           }
2413         }
2414       }
2415     }
2416   }
2417
2418   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2419   {
2420     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2421       get_inventory_element_from_pos(local_player, i);
2422     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2423       get_inventory_element_from_pos(local_player, -i - 1);
2424   }
2425
2426   game_panel_controls[GAME_PANEL_SCORE].value = score;
2427   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2428
2429   game_panel_controls[GAME_PANEL_TIME].value = time;
2430
2431   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2432   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2433   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2434
2435   if (level.time == 0)
2436     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2437   else
2438     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2439
2440   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2441   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2442
2443   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2444
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2446     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2447      EL_EMPTY);
2448   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2449     local_player->shield_normal_time_left;
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2451     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2452      EL_EMPTY);
2453   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2454     local_player->shield_deadly_time_left;
2455
2456   game_panel_controls[GAME_PANEL_EXIT].value =
2457     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2458
2459   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2460     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2461   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2462     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2463      EL_EMC_MAGIC_BALL_SWITCH);
2464
2465   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2466     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2467   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2468     game.light_time_left;
2469
2470   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2471     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2472   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2473     game.timegate_time_left;
2474
2475   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2476     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2477
2478   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2479     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2480   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2481     game.lenses_time_left;
2482
2483   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2484     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2485   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2486     game.magnify_time_left;
2487
2488   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2489     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2490      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2491      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2492      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2493      EL_BALLOON_SWITCH_NONE);
2494
2495   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2496     local_player->dynabomb_count;
2497   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2498     local_player->dynabomb_size;
2499   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2500     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2501
2502   game_panel_controls[GAME_PANEL_PENGUINS].value =
2503     game.friends_still_needed;
2504
2505   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2506     game.sokoban_objects_still_needed;
2507   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2508     game.sokoban_fields_still_needed;
2509
2510   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2511     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2512
2513   for (i = 0; i < NUM_BELTS; i++)
2514   {
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2516       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2517        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2518     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2519       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2520   }
2521
2522   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2523     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2524   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2525     game.magic_wall_time_left;
2526
2527   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2528     local_player->gravity;
2529
2530   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2531     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2532
2533   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2534     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2535       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2536        game.panel.element[i].id : EL_UNDEFINED);
2537
2538   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2539     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2540       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2541        element_info[game.panel.element_count[i].id].element_count : 0);
2542
2543   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2544     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2545       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2546        element_info[game.panel.ce_score[i].id].collect_score : 0);
2547
2548   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2549     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2550       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2551        element_info[game.panel.ce_score_element[i].id].collect_score :
2552        EL_UNDEFINED);
2553
2554   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2555   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2556   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2557
2558   // update game panel control frames
2559
2560   for (i = 0; game_panel_controls[i].nr != -1; i++)
2561   {
2562     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2563
2564     if (gpc->type == TYPE_ELEMENT)
2565     {
2566       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2567       {
2568         int last_anim_random_frame = gfx.anim_random_frame;
2569         int element = gpc->value;
2570         int graphic = el2panelimg(element);
2571         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2572                                sync_random_frame :
2573                                graphic_info[graphic].anim_global_anim_sync ?
2574                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2575
2576         if (gpc->value != gpc->last_value)
2577         {
2578           gpc->gfx_frame = 0;
2579           gpc->gfx_random = init_gfx_random;
2580         }
2581         else
2582         {
2583           gpc->gfx_frame++;
2584
2585           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2586               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2587             gpc->gfx_random = init_gfx_random;
2588         }
2589
2590         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2591           gfx.anim_random_frame = gpc->gfx_random;
2592
2593         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2594           gpc->gfx_frame = element_info[element].collect_score;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602     else if (gpc->type == TYPE_GRAPHIC)
2603     {
2604       if (gpc->graphic != IMG_UNDEFINED)
2605       {
2606         int last_anim_random_frame = gfx.anim_random_frame;
2607         int graphic = gpc->graphic;
2608         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2609                                sync_random_frame :
2610                                graphic_info[graphic].anim_global_anim_sync ?
2611                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2612
2613         if (gpc->value != gpc->last_value)
2614         {
2615           gpc->gfx_frame = 0;
2616           gpc->gfx_random = init_gfx_random;
2617         }
2618         else
2619         {
2620           gpc->gfx_frame++;
2621
2622           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2623               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2624             gpc->gfx_random = init_gfx_random;
2625         }
2626
2627         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2628           gfx.anim_random_frame = gpc->gfx_random;
2629
2630         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2631
2632         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2633           gfx.anim_random_frame = last_anim_random_frame;
2634       }
2635     }
2636   }
2637 }
2638
2639 static void DisplayGameControlValues(void)
2640 {
2641   boolean redraw_panel = FALSE;
2642   int i;
2643
2644   for (i = 0; game_panel_controls[i].nr != -1; i++)
2645   {
2646     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2647
2648     if (PANEL_DEACTIVATED(gpc->pos))
2649       continue;
2650
2651     if (gpc->value == gpc->last_value &&
2652         gpc->frame == gpc->last_frame)
2653       continue;
2654
2655     redraw_panel = TRUE;
2656   }
2657
2658   if (!redraw_panel)
2659     return;
2660
2661   // copy default game door content to main double buffer
2662
2663   // !!! CHECK AGAIN !!!
2664   SetPanelBackground();
2665   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2666   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2667
2668   // redraw game control buttons
2669   RedrawGameButtons();
2670
2671   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2672
2673   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2674   {
2675     int nr = game_panel_order[i].nr;
2676     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2677     struct TextPosInfo *pos = gpc->pos;
2678     int type = gpc->type;
2679     int value = gpc->value;
2680     int frame = gpc->frame;
2681     int size = pos->size;
2682     int font = pos->font;
2683     boolean draw_masked = pos->draw_masked;
2684     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2685
2686     if (PANEL_DEACTIVATED(pos))
2687       continue;
2688
2689     if (pos->class == get_hash_from_key("extra_panel_items") &&
2690         !setup.prefer_extra_panel_items)
2691       continue;
2692
2693     gpc->last_value = value;
2694     gpc->last_frame = frame;
2695
2696     if (type == TYPE_INTEGER)
2697     {
2698       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2699           nr == GAME_PANEL_INVENTORY_COUNT ||
2700           nr == GAME_PANEL_SCORE ||
2701           nr == GAME_PANEL_HIGHSCORE ||
2702           nr == GAME_PANEL_TIME)
2703       {
2704         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2705
2706         if (use_dynamic_size)           // use dynamic number of digits
2707         {
2708           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2709                               nr == GAME_PANEL_INVENTORY_COUNT ||
2710                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2711           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2712                           nr == GAME_PANEL_INVENTORY_COUNT ||
2713                           nr == GAME_PANEL_TIME ? 1 : 2);
2714           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2715                        nr == GAME_PANEL_INVENTORY_COUNT ||
2716                        nr == GAME_PANEL_TIME ? 3 : 5);
2717           int size2 = size1 + size_add;
2718           int font1 = pos->font;
2719           int font2 = pos->font_alt;
2720
2721           size = (value < value_change ? size1 : size2);
2722           font = (value < value_change ? font1 : font2);
2723         }
2724       }
2725
2726       // correct text size if "digits" is zero or less
2727       if (size <= 0)
2728         size = strlen(int2str(value, size));
2729
2730       // dynamically correct text alignment
2731       pos->width = size * getFontWidth(font);
2732
2733       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                   int2str(value, size), font, mask_mode);
2735     }
2736     else if (type == TYPE_ELEMENT)
2737     {
2738       int element, graphic;
2739       Bitmap *src_bitmap;
2740       int src_x, src_y;
2741       int width, height;
2742       int dst_x = PANEL_XPOS(pos);
2743       int dst_y = PANEL_YPOS(pos);
2744
2745       if (value != EL_UNDEFINED && value != EL_EMPTY)
2746       {
2747         element = value;
2748         graphic = el2panelimg(value);
2749
2750 #if 0
2751         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2752               element, EL_NAME(element), size);
2753 #endif
2754
2755         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2756           size = TILESIZE;
2757
2758         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2759                               &src_x, &src_y);
2760
2761         width  = graphic_info[graphic].width  * size / TILESIZE;
2762         height = graphic_info[graphic].height * size / TILESIZE;
2763
2764         if (draw_masked)
2765           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2766                            dst_x, dst_y);
2767         else
2768           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2769                      dst_x, dst_y);
2770       }
2771     }
2772     else if (type == TYPE_GRAPHIC)
2773     {
2774       int graphic        = gpc->graphic;
2775       int graphic_active = gpc->graphic_active;
2776       Bitmap *src_bitmap;
2777       int src_x, src_y;
2778       int width, height;
2779       int dst_x = PANEL_XPOS(pos);
2780       int dst_y = PANEL_YPOS(pos);
2781       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2782                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2783
2784       if (graphic != IMG_UNDEFINED && !skip)
2785       {
2786         if (pos->style == STYLE_REVERSE)
2787           value = 100 - value;
2788
2789         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2790
2791         if (pos->direction & MV_HORIZONTAL)
2792         {
2793           width  = graphic_info[graphic_active].width * value / 100;
2794           height = graphic_info[graphic_active].height;
2795
2796           if (pos->direction == MV_LEFT)
2797           {
2798             src_x += graphic_info[graphic_active].width - width;
2799             dst_x += graphic_info[graphic_active].width - width;
2800           }
2801         }
2802         else
2803         {
2804           width  = graphic_info[graphic_active].width;
2805           height = graphic_info[graphic_active].height * value / 100;
2806
2807           if (pos->direction == MV_UP)
2808           {
2809             src_y += graphic_info[graphic_active].height - height;
2810             dst_y += graphic_info[graphic_active].height - height;
2811           }
2812         }
2813
2814         if (draw_masked)
2815           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2816                            dst_x, dst_y);
2817         else
2818           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2819                      dst_x, dst_y);
2820
2821         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2822
2823         if (pos->direction & MV_HORIZONTAL)
2824         {
2825           if (pos->direction == MV_RIGHT)
2826           {
2827             src_x += width;
2828             dst_x += width;
2829           }
2830           else
2831           {
2832             dst_x = PANEL_XPOS(pos);
2833           }
2834
2835           width = graphic_info[graphic].width - width;
2836         }
2837         else
2838         {
2839           if (pos->direction == MV_DOWN)
2840           {
2841             src_y += height;
2842             dst_y += height;
2843           }
2844           else
2845           {
2846             dst_y = PANEL_YPOS(pos);
2847           }
2848
2849           height = graphic_info[graphic].height - height;
2850         }
2851
2852         if (draw_masked)
2853           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2854                            dst_x, dst_y);
2855         else
2856           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2857                      dst_x, dst_y);
2858       }
2859     }
2860     else if (type == TYPE_STRING)
2861     {
2862       boolean active = (value != 0);
2863       char *state_normal = "off";
2864       char *state_active = "on";
2865       char *state = (active ? state_active : state_normal);
2866       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2867                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2868                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2869                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2870
2871       if (nr == GAME_PANEL_GRAVITY_STATE)
2872       {
2873         int font1 = pos->font;          // (used for normal state)
2874         int font2 = pos->font_alt;      // (used for active state)
2875
2876         font = (active ? font2 : font1);
2877       }
2878
2879       if (s != NULL)
2880       {
2881         char *s_cut;
2882
2883         if (size <= 0)
2884         {
2885           // don't truncate output if "chars" is zero or less
2886           size = strlen(s);
2887
2888           // dynamically correct text alignment
2889           pos->width = size * getFontWidth(font);
2890         }
2891
2892         s_cut = getStringCopyN(s, size);
2893
2894         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2895                     s_cut, font, mask_mode);
2896
2897         free(s_cut);
2898       }
2899     }
2900
2901     redraw_mask |= REDRAW_DOOR_1;
2902   }
2903
2904   SetGameStatus(GAME_MODE_PLAYING);
2905 }
2906
2907 void UpdateAndDisplayGameControlValues(void)
2908 {
2909   if (tape.deactivate_display)
2910     return;
2911
2912   UpdateGameControlValues();
2913   DisplayGameControlValues();
2914 }
2915
2916 void UpdateGameDoorValues(void)
2917 {
2918   UpdateGameControlValues();
2919 }
2920
2921 void DrawGameDoorValues(void)
2922 {
2923   DisplayGameControlValues();
2924 }
2925
2926
2927 // ============================================================================
2928 // InitGameEngine()
2929 // ----------------------------------------------------------------------------
2930 // initialize game engine due to level / tape version number
2931 // ============================================================================
2932
2933 static void InitGameEngine(void)
2934 {
2935   int i, j, k, l, x, y;
2936
2937   // set game engine from tape file when re-playing, else from level file
2938   game.engine_version = (tape.playing ? tape.engine_version :
2939                          level.game_version);
2940
2941   // set single or multi-player game mode (needed for re-playing tapes)
2942   game.team_mode = setup.team_mode;
2943
2944   if (tape.playing)
2945   {
2946     int num_players = 0;
2947
2948     for (i = 0; i < MAX_PLAYERS; i++)
2949       if (tape.player_participates[i])
2950         num_players++;
2951
2952     // multi-player tapes contain input data for more than one player
2953     game.team_mode = (num_players > 1);
2954   }
2955
2956 #if 0
2957   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2958         level.game_version);
2959   Debug("game:init:level", "          tape.file_version   == %06d",
2960         tape.file_version);
2961   Debug("game:init:level", "          tape.game_version   == %06d",
2962         tape.game_version);
2963   Debug("game:init:level", "          tape.engine_version == %06d",
2964         tape.engine_version);
2965   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2966         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2967 #endif
2968
2969   // --------------------------------------------------------------------------
2970   // set flags for bugs and changes according to active game engine version
2971   // --------------------------------------------------------------------------
2972
2973   /*
2974     Summary of bugfix:
2975     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2976
2977     Bug was introduced in version:
2978     2.0.1
2979
2980     Bug was fixed in version:
2981     4.2.0.0
2982
2983     Description:
2984     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2985     but the property "can fall" was missing, which caused some levels to be
2986     unsolvable. This was fixed in version 4.2.0.0.
2987
2988     Affected levels/tapes:
2989     An example for a tape that was fixed by this bugfix is tape 029 from the
2990     level set "rnd_sam_bateman".
2991     The wrong behaviour will still be used for all levels or tapes that were
2992     created/recorded with it. An example for this is tape 023 from the level
2993     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2994   */
2995
2996   boolean use_amoeba_dropping_cannot_fall_bug =
2997     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2998       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2999      (tape.playing &&
3000       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3001       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3002
3003   /*
3004     Summary of bugfix/change:
3005     Fixed move speed of elements entering or leaving magic wall.
3006
3007     Fixed/changed in version:
3008     2.0.1
3009
3010     Description:
3011     Before 2.0.1, move speed of elements entering or leaving magic wall was
3012     twice as fast as it is now.
3013     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3014
3015     Affected levels/tapes:
3016     The first condition is generally needed for all levels/tapes before version
3017     2.0.1, which might use the old behaviour before it was changed; known tapes
3018     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3019     The second condition is an exception from the above case and is needed for
3020     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3021     above, but before it was known that this change would break tapes like the
3022     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3023     although the engine version while recording maybe was before 2.0.1. There
3024     are a lot of tapes that are affected by this exception, like tape 006 from
3025     the level set "rnd_conor_mancone".
3026   */
3027
3028   boolean use_old_move_stepsize_for_magic_wall =
3029     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3030      !(tape.playing &&
3031        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3032        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3033
3034   /*
3035     Summary of bugfix/change:
3036     Fixed handling for custom elements that change when pushed by the player.
3037
3038     Fixed/changed in version:
3039     3.1.0
3040
3041     Description:
3042     Before 3.1.0, custom elements that "change when pushing" changed directly
3043     after the player started pushing them (until then handled in "DigField()").
3044     Since 3.1.0, these custom elements are not changed until the "pushing"
3045     move of the element is finished (now handled in "ContinueMoving()").
3046
3047     Affected levels/tapes:
3048     The first condition is generally needed for all levels/tapes before version
3049     3.1.0, which might use the old behaviour before it was changed; known tapes
3050     that are affected are some tapes from the level set "Walpurgis Gardens" by
3051     Jamie Cullen.
3052     The second condition is an exception from the above case and is needed for
3053     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3054     above (including some development versions of 3.1.0), but before it was
3055     known that this change would break tapes like the above and was fixed in
3056     3.1.1, so that the changed behaviour was active although the engine version
3057     while recording maybe was before 3.1.0. There is at least one tape that is
3058     affected by this exception, which is the tape for the one-level set "Bug
3059     Machine" by Juergen Bonhagen.
3060   */
3061
3062   game.use_change_when_pushing_bug =
3063     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3064      !(tape.playing &&
3065        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3066        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3067
3068   /*
3069     Summary of bugfix/change:
3070     Fixed handling for blocking the field the player leaves when moving.
3071
3072     Fixed/changed in version:
3073     3.1.1
3074
3075     Description:
3076     Before 3.1.1, when "block last field when moving" was enabled, the field
3077     the player is leaving when moving was blocked for the time of the move,
3078     and was directly unblocked afterwards. This resulted in the last field
3079     being blocked for exactly one less than the number of frames of one player
3080     move. Additionally, even when blocking was disabled, the last field was
3081     blocked for exactly one frame.
3082     Since 3.1.1, due to changes in player movement handling, the last field
3083     is not blocked at all when blocking is disabled. When blocking is enabled,
3084     the last field is blocked for exactly the number of frames of one player
3085     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3086     last field is blocked for exactly one more than the number of frames of
3087     one player move.
3088
3089     Affected levels/tapes:
3090     (!!! yet to be determined -- probably many !!!)
3091   */
3092
3093   game.use_block_last_field_bug =
3094     (game.engine_version < VERSION_IDENT(3,1,1,0));
3095
3096   /* various special flags and settings for native Emerald Mine game engine */
3097
3098   game_em.use_single_button =
3099     (game.engine_version > VERSION_IDENT(4,0,0,2));
3100
3101   game_em.use_push_delay =
3102     (game.engine_version > VERSION_IDENT(4,3,7,1));
3103
3104   game_em.use_snap_key_bug =
3105     (game.engine_version < VERSION_IDENT(4,0,1,0));
3106
3107   game_em.use_random_bug =
3108     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3109
3110   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3111
3112   game_em.use_old_explosions            = use_old_em_engine;
3113   game_em.use_old_android               = use_old_em_engine;
3114   game_em.use_old_push_elements         = use_old_em_engine;
3115   game_em.use_old_push_into_acid        = use_old_em_engine;
3116
3117   game_em.use_wrap_around               = !use_old_em_engine;
3118
3119   // --------------------------------------------------------------------------
3120
3121   // set maximal allowed number of custom element changes per game frame
3122   game.max_num_changes_per_frame = 1;
3123
3124   // default scan direction: scan playfield from top/left to bottom/right
3125   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3126
3127   // dynamically adjust element properties according to game engine version
3128   InitElementPropertiesEngine(game.engine_version);
3129
3130   // ---------- initialize special element properties -------------------------
3131
3132   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3133   if (use_amoeba_dropping_cannot_fall_bug)
3134     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3135
3136   // ---------- initialize player's initial move delay ------------------------
3137
3138   // dynamically adjust player properties according to level information
3139   for (i = 0; i < MAX_PLAYERS; i++)
3140     game.initial_move_delay_value[i] =
3141       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3142
3143   // dynamically adjust player properties according to game engine version
3144   for (i = 0; i < MAX_PLAYERS; i++)
3145     game.initial_move_delay[i] =
3146       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3147        game.initial_move_delay_value[i] : 0);
3148
3149   // ---------- initialize player's initial push delay ------------------------
3150
3151   // dynamically adjust player properties according to game engine version
3152   game.initial_push_delay_value =
3153     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3154
3155   // ---------- initialize changing elements ----------------------------------
3156
3157   // initialize changing elements information
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159   {
3160     struct ElementInfo *ei = &element_info[i];
3161
3162     // this pointer might have been changed in the level editor
3163     ei->change = &ei->change_page[0];
3164
3165     if (!IS_CUSTOM_ELEMENT(i))
3166     {
3167       ei->change->target_element = EL_EMPTY_SPACE;
3168       ei->change->delay_fixed = 0;
3169       ei->change->delay_random = 0;
3170       ei->change->delay_frames = 1;
3171     }
3172
3173     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3174     {
3175       ei->has_change_event[j] = FALSE;
3176
3177       ei->event_page_nr[j] = 0;
3178       ei->event_page[j] = &ei->change_page[0];
3179     }
3180   }
3181
3182   // add changing elements from pre-defined list
3183   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3184   {
3185     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3186     struct ElementInfo *ei = &element_info[ch_delay->element];
3187
3188     ei->change->target_element       = ch_delay->target_element;
3189     ei->change->delay_fixed          = ch_delay->change_delay;
3190
3191     ei->change->pre_change_function  = ch_delay->pre_change_function;
3192     ei->change->change_function      = ch_delay->change_function;
3193     ei->change->post_change_function = ch_delay->post_change_function;
3194
3195     ei->change->can_change = TRUE;
3196     ei->change->can_change_or_has_action = TRUE;
3197
3198     ei->has_change_event[CE_DELAY] = TRUE;
3199
3200     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3201     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3202   }
3203
3204   // ---------- initialize if element can trigger global animations -----------
3205
3206   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3207   {
3208     struct ElementInfo *ei = &element_info[i];
3209
3210     ei->has_anim_event = FALSE;
3211   }
3212
3213   InitGlobalAnimEventsForCustomElements();
3214
3215   // ---------- initialize internal run-time variables ------------------------
3216
3217   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       ei->change_page[j].can_change_or_has_action =
3224         (ei->change_page[j].can_change |
3225          ei->change_page[j].has_action);
3226     }
3227   }
3228
3229   // add change events from custom element configuration
3230   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3231   {
3232     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3233
3234     for (j = 0; j < ei->num_change_pages; j++)
3235     {
3236       if (!ei->change_page[j].can_change_or_has_action)
3237         continue;
3238
3239       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3240       {
3241         // only add event page for the first page found with this event
3242         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3243         {
3244           ei->has_change_event[k] = TRUE;
3245
3246           ei->event_page_nr[k] = j;
3247           ei->event_page[k] = &ei->change_page[j];
3248         }
3249       }
3250     }
3251   }
3252
3253   // ---------- initialize reference elements in change conditions ------------
3254
3255   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3256   {
3257     int element = EL_CUSTOM_START + i;
3258     struct ElementInfo *ei = &element_info[element];
3259
3260     for (j = 0; j < ei->num_change_pages; j++)
3261     {
3262       int trigger_element = ei->change_page[j].initial_trigger_element;
3263
3264       if (trigger_element >= EL_PREV_CE_8 &&
3265           trigger_element <= EL_NEXT_CE_8)
3266         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3267
3268       ei->change_page[j].trigger_element = trigger_element;
3269     }
3270   }
3271
3272   // ---------- initialize run-time trigger player and element ----------------
3273
3274   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3275   {
3276     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3277
3278     for (j = 0; j < ei->num_change_pages; j++)
3279     {
3280       struct ElementChangeInfo *change = &ei->change_page[j];
3281
3282       change->actual_trigger_element = EL_EMPTY;
3283       change->actual_trigger_player = EL_EMPTY;
3284       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3285       change->actual_trigger_side = CH_SIDE_NONE;
3286       change->actual_trigger_ce_value = 0;
3287       change->actual_trigger_ce_score = 0;
3288       change->actual_trigger_x = -1;
3289       change->actual_trigger_y = -1;
3290     }
3291   }
3292
3293   // ---------- initialize trigger events -------------------------------------
3294
3295   // initialize trigger events information
3296   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3298       trigger_events[i][j] = FALSE;
3299
3300   // add trigger events from element change event properties
3301   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3302   {
3303     struct ElementInfo *ei = &element_info[i];
3304
3305     for (j = 0; j < ei->num_change_pages; j++)
3306     {
3307       struct ElementChangeInfo *change = &ei->change_page[j];
3308
3309       if (!change->can_change_or_has_action)
3310         continue;
3311
3312       if (change->has_event[CE_BY_OTHER_ACTION])
3313       {
3314         int trigger_element = change->trigger_element;
3315
3316         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3317         {
3318           if (change->has_event[k])
3319           {
3320             if (IS_GROUP_ELEMENT(trigger_element))
3321             {
3322               struct ElementGroupInfo *group =
3323                 element_info[trigger_element].group;
3324
3325               for (l = 0; l < group->num_elements_resolved; l++)
3326                 trigger_events[group->element_resolved[l]][k] = TRUE;
3327             }
3328             else if (trigger_element == EL_ANY_ELEMENT)
3329               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3330                 trigger_events[l][k] = TRUE;
3331             else
3332               trigger_events[trigger_element][k] = TRUE;
3333           }
3334         }
3335       }
3336     }
3337   }
3338
3339   // ---------- initialize push delay -----------------------------------------
3340
3341   // initialize push delay values to default
3342   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3343   {
3344     if (!IS_CUSTOM_ELEMENT(i))
3345     {
3346       // set default push delay values (corrected since version 3.0.7-1)
3347       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3348       {
3349         element_info[i].push_delay_fixed = 2;
3350         element_info[i].push_delay_random = 8;
3351       }
3352       else
3353       {
3354         element_info[i].push_delay_fixed = 8;
3355         element_info[i].push_delay_random = 8;
3356       }
3357     }
3358   }
3359
3360   // set push delay value for certain elements from pre-defined list
3361   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3362   {
3363     int e = push_delay_list[i].element;
3364
3365     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3366     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3367   }
3368
3369   // set push delay value for Supaplex elements for newer engine versions
3370   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3371   {
3372     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373     {
3374       if (IS_SP_ELEMENT(i))
3375       {
3376         // set SP push delay to just enough to push under a falling zonk
3377         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3378
3379         element_info[i].push_delay_fixed  = delay;
3380         element_info[i].push_delay_random = 0;
3381       }
3382     }
3383   }
3384
3385   // ---------- initialize move stepsize --------------------------------------
3386
3387   // initialize move stepsize values to default
3388   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3389     if (!IS_CUSTOM_ELEMENT(i))
3390       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3391
3392   // set move stepsize value for certain elements from pre-defined list
3393   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3394   {
3395     int e = move_stepsize_list[i].element;
3396
3397     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3398
3399     // set move stepsize value for certain elements for older engine versions
3400     if (use_old_move_stepsize_for_magic_wall)
3401     {
3402       if (e == EL_MAGIC_WALL_FILLING ||
3403           e == EL_MAGIC_WALL_EMPTYING ||
3404           e == EL_BD_MAGIC_WALL_FILLING ||
3405           e == EL_BD_MAGIC_WALL_EMPTYING)
3406         element_info[e].move_stepsize *= 2;
3407     }
3408   }
3409
3410   // ---------- initialize collect score --------------------------------------
3411
3412   // initialize collect score values for custom elements from initial value
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414     if (IS_CUSTOM_ELEMENT(i))
3415       element_info[i].collect_score = element_info[i].collect_score_initial;
3416
3417   // ---------- initialize collect count --------------------------------------
3418
3419   // initialize collect count values for non-custom elements
3420   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     if (!IS_CUSTOM_ELEMENT(i))
3422       element_info[i].collect_count_initial = 0;
3423
3424   // add collect count values for all elements from pre-defined list
3425   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3426     element_info[collect_count_list[i].element].collect_count_initial =
3427       collect_count_list[i].count;
3428
3429   // ---------- initialize access direction -----------------------------------
3430
3431   // initialize access direction values to default (access from every side)
3432   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433     if (!IS_CUSTOM_ELEMENT(i))
3434       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3435
3436   // set access direction value for certain elements from pre-defined list
3437   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3438     element_info[access_direction_list[i].element].access_direction =
3439       access_direction_list[i].direction;
3440
3441   // ---------- initialize explosion content ----------------------------------
3442   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3443   {
3444     if (IS_CUSTOM_ELEMENT(i))
3445       continue;
3446
3447     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3448     {
3449       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3450
3451       element_info[i].content.e[x][y] =
3452         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3453          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3454          i == EL_PLAYER_3 ? EL_EMERALD :
3455          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3456          i == EL_MOLE ? EL_EMERALD_RED :
3457          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3458          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3459          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3460          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3461          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3462          i == EL_WALL_EMERALD ? EL_EMERALD :
3463          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3464          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3465          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3466          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3467          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3468          i == EL_WALL_PEARL ? EL_PEARL :
3469          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3470          EL_EMPTY);
3471     }
3472   }
3473
3474   // ---------- initialize recursion detection --------------------------------
3475   recursion_loop_depth = 0;
3476   recursion_loop_detected = FALSE;
3477   recursion_loop_element = EL_UNDEFINED;
3478
3479   // ---------- initialize graphics engine ------------------------------------
3480   game.scroll_delay_value =
3481     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3482      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3483      !setup.forced_scroll_delay           ? 0 :
3484      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3485   if (game.forced_scroll_delay_value == -1)
3486     game.scroll_delay_value =
3487       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3488
3489   // ---------- initialize game engine snapshots ------------------------------
3490   for (i = 0; i < MAX_PLAYERS; i++)
3491     game.snapshot.last_action[i] = 0;
3492   game.snapshot.changed_action = FALSE;
3493   game.snapshot.collected_item = FALSE;
3494   game.snapshot.mode =
3495     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3496      SNAPSHOT_MODE_EVERY_STEP :
3497      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3498      SNAPSHOT_MODE_EVERY_MOVE :
3499      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3500      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3501   game.snapshot.save_snapshot = FALSE;
3502
3503   // ---------- initialize level time for Supaplex engine ---------------------
3504   // Supaplex levels with time limit currently unsupported -- should be added
3505   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3506     level.time = 0;
3507
3508   // ---------- initialize flags for handling game actions --------------------
3509
3510   // set flags for game actions to default values
3511   game.use_key_actions = TRUE;
3512   game.use_mouse_actions = FALSE;
3513
3514   // when using Mirror Magic game engine, handle mouse events only
3515   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3516   {
3517     game.use_key_actions = FALSE;
3518     game.use_mouse_actions = TRUE;
3519   }
3520
3521   // check for custom elements with mouse click events
3522   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3523   {
3524     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3525     {
3526       int element = EL_CUSTOM_START + i;
3527
3528       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3529           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3530           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3531           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3532         game.use_mouse_actions = TRUE;
3533     }
3534   }
3535 }
3536
3537 static int get_num_special_action(int element, int action_first,
3538                                   int action_last)
3539 {
3540   int num_special_action = 0;
3541   int i, j;
3542
3543   for (i = action_first; i <= action_last; i++)
3544   {
3545     boolean found = FALSE;
3546
3547     for (j = 0; j < NUM_DIRECTIONS; j++)
3548       if (el_act_dir2img(element, i, j) !=
3549           el_act_dir2img(element, ACTION_DEFAULT, j))
3550         found = TRUE;
3551
3552     if (found)
3553       num_special_action++;
3554     else
3555       break;
3556   }
3557
3558   return num_special_action;
3559 }
3560
3561
3562 // ============================================================================
3563 // InitGame()
3564 // ----------------------------------------------------------------------------
3565 // initialize and start new game
3566 // ============================================================================
3567
3568 #if DEBUG_INIT_PLAYER
3569 static void DebugPrintPlayerStatus(char *message)
3570 {
3571   int i;
3572
3573   if (!options.debug)
3574     return;
3575
3576   Debug("game:init:player", "%s:", message);
3577
3578   for (i = 0; i < MAX_PLAYERS; i++)
3579   {
3580     struct PlayerInfo *player = &stored_player[i];
3581
3582     Debug("game:init:player",
3583           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3584           i + 1,
3585           player->present,
3586           player->connected,
3587           player->connected_locally,
3588           player->connected_network,
3589           player->active,
3590           (local_player == player ? " (local player)" : ""));
3591   }
3592 }
3593 #endif
3594
3595 void InitGame(void)
3596 {
3597   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3598   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3599   int fade_mask = REDRAW_FIELD;
3600   boolean restarting = (game_status == GAME_MODE_PLAYING);
3601   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3602   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3603   int initial_move_dir = MV_DOWN;
3604   int i, j, x, y;
3605
3606   // required here to update video display before fading (FIX THIS)
3607   DrawMaskedBorder(REDRAW_DOOR_2);
3608
3609   if (!game.restart_level)
3610     CloseDoor(DOOR_CLOSE_1);
3611
3612   if (restarting)
3613   {
3614     // force fading out global animations displayed during game play
3615     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3616   }
3617   else
3618   {
3619     SetGameStatus(GAME_MODE_PLAYING);
3620   }
3621
3622   if (level_editor_test_game)
3623     FadeSkipNextFadeOut();
3624   else
3625     FadeSetEnterScreen();
3626
3627   if (CheckFadeAll())
3628     fade_mask = REDRAW_ALL;
3629
3630   FadeLevelSoundsAndMusic();
3631
3632   ExpireSoundLoops(TRUE);
3633
3634   FadeOut(fade_mask);
3635
3636   if (restarting)
3637   {
3638     // force restarting global animations displayed during game play
3639     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3640
3641     // this is required for "transforming" fade modes like cross-fading
3642     // (else global animations will be stopped, but not restarted here)
3643     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3644
3645     SetGameStatus(GAME_MODE_PLAYING);
3646   }
3647
3648   if (level_editor_test_game)
3649     FadeSkipNextFadeIn();
3650
3651   // needed if different viewport properties defined for playing
3652   ChangeViewportPropertiesIfNeeded();
3653
3654   ClearField();
3655
3656   DrawCompleteVideoDisplay();
3657
3658   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3659
3660   InitGameEngine();
3661   InitGameControlValues();
3662
3663   if (tape.recording)
3664   {
3665     // initialize tape actions from game when recording tape
3666     tape.use_key_actions   = game.use_key_actions;
3667     tape.use_mouse_actions = game.use_mouse_actions;
3668
3669     // initialize visible playfield size when recording tape (for team mode)
3670     tape.scr_fieldx = SCR_FIELDX;
3671     tape.scr_fieldy = SCR_FIELDY;
3672   }
3673
3674   // don't play tapes over network
3675   network_playing = (network.enabled && !tape.playing);
3676
3677   for (i = 0; i < MAX_PLAYERS; i++)
3678   {
3679     struct PlayerInfo *player = &stored_player[i];
3680
3681     player->index_nr = i;
3682     player->index_bit = (1 << i);
3683     player->element_nr = EL_PLAYER_1 + i;
3684
3685     player->present = FALSE;
3686     player->active = FALSE;
3687     player->mapped = FALSE;
3688
3689     player->killed = FALSE;
3690     player->reanimated = FALSE;
3691     player->buried = FALSE;
3692
3693     player->action = 0;
3694     player->effective_action = 0;
3695     player->programmed_action = 0;
3696     player->snap_action = 0;
3697
3698     player->mouse_action.lx = 0;
3699     player->mouse_action.ly = 0;
3700     player->mouse_action.button = 0;
3701     player->mouse_action.button_hint = 0;
3702
3703     player->effective_mouse_action.lx = 0;
3704     player->effective_mouse_action.ly = 0;
3705     player->effective_mouse_action.button = 0;
3706     player->effective_mouse_action.button_hint = 0;
3707
3708     for (j = 0; j < MAX_NUM_KEYS; j++)
3709       player->key[j] = FALSE;
3710
3711     player->num_white_keys = 0;
3712
3713     player->dynabomb_count = 0;
3714     player->dynabomb_size = 1;
3715     player->dynabombs_left = 0;
3716     player->dynabomb_xl = FALSE;
3717
3718     player->MovDir = initial_move_dir;
3719     player->MovPos = 0;
3720     player->GfxPos = 0;
3721     player->GfxDir = initial_move_dir;
3722     player->GfxAction = ACTION_DEFAULT;
3723     player->Frame = 0;
3724     player->StepFrame = 0;
3725
3726     player->initial_element = player->element_nr;
3727     player->artwork_element =
3728       (level.use_artwork_element[i] ? level.artwork_element[i] :
3729        player->element_nr);
3730     player->use_murphy = FALSE;
3731
3732     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3733     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3734
3735     player->gravity = level.initial_player_gravity[i];
3736
3737     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3738
3739     player->actual_frame_counter.count = 0;
3740     player->actual_frame_counter.value = 1;
3741
3742     player->step_counter = 0;
3743
3744     player->last_move_dir = initial_move_dir;
3745
3746     player->is_active = FALSE;
3747
3748     player->is_waiting = FALSE;
3749     player->is_moving = FALSE;
3750     player->is_auto_moving = FALSE;
3751     player->is_digging = FALSE;
3752     player->is_snapping = FALSE;
3753     player->is_collecting = FALSE;
3754     player->is_pushing = FALSE;
3755     player->is_switching = FALSE;
3756     player->is_dropping = FALSE;
3757     player->is_dropping_pressed = FALSE;
3758
3759     player->is_bored = FALSE;
3760     player->is_sleeping = FALSE;
3761
3762     player->was_waiting = TRUE;
3763     player->was_moving = FALSE;
3764     player->was_snapping = FALSE;
3765     player->was_dropping = FALSE;
3766
3767     player->force_dropping = FALSE;
3768
3769     player->frame_counter_bored = -1;
3770     player->frame_counter_sleeping = -1;
3771
3772     player->anim_delay_counter = 0;
3773     player->post_delay_counter = 0;
3774
3775     player->dir_waiting = initial_move_dir;
3776     player->action_waiting = ACTION_DEFAULT;
3777     player->last_action_waiting = ACTION_DEFAULT;
3778     player->special_action_bored = ACTION_DEFAULT;
3779     player->special_action_sleeping = ACTION_DEFAULT;
3780
3781     player->switch_x = -1;
3782     player->switch_y = -1;
3783
3784     player->drop_x = -1;
3785     player->drop_y = -1;
3786
3787     player->show_envelope = 0;
3788
3789     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3790
3791     player->push_delay       = -1;      // initialized when pushing starts
3792     player->push_delay_value = game.initial_push_delay_value;
3793
3794     player->drop_delay = 0;
3795     player->drop_pressed_delay = 0;
3796
3797     player->last_jx = -1;
3798     player->last_jy = -1;
3799     player->jx = -1;
3800     player->jy = -1;
3801
3802     player->shield_normal_time_left = 0;
3803     player->shield_deadly_time_left = 0;
3804
3805     player->last_removed_element = EL_UNDEFINED;
3806
3807     player->inventory_infinite_element = EL_UNDEFINED;
3808     player->inventory_size = 0;
3809
3810     if (level.use_initial_inventory[i])
3811     {
3812       for (j = 0; j < level.initial_inventory_size[i]; j++)
3813       {
3814         int element = level.initial_inventory_content[i][j];
3815         int collect_count = element_info[element].collect_count_initial;
3816         int k;
3817
3818         if (!IS_CUSTOM_ELEMENT(element))
3819           collect_count = 1;
3820
3821         if (collect_count == 0)
3822           player->inventory_infinite_element = element;
3823         else
3824           for (k = 0; k < collect_count; k++)
3825             if (player->inventory_size < MAX_INVENTORY_SIZE)
3826               player->inventory_element[player->inventory_size++] = element;
3827       }
3828     }
3829
3830     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3831     SnapField(player, 0, 0);
3832
3833     map_player_action[i] = i;
3834   }
3835
3836   network_player_action_received = FALSE;
3837
3838   // initial null action
3839   if (network_playing)
3840     SendToServer_MovePlayer(MV_NONE);
3841
3842   FrameCounter = 0;
3843   TimeFrames = 0;
3844   TimePlayed = 0;
3845   TimeLeft = level.time;
3846
3847   TapeTimeFrames = 0;
3848   TapeTime = 0;
3849
3850   ScreenMovDir = MV_NONE;
3851   ScreenMovPos = 0;
3852   ScreenGfxPos = 0;
3853
3854   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3855
3856   game.robot_wheel_x = -1;
3857   game.robot_wheel_y = -1;
3858
3859   game.exit_x = -1;
3860   game.exit_y = -1;
3861
3862   game.all_players_gone = FALSE;
3863
3864   game.LevelSolved = FALSE;
3865   game.GameOver = FALSE;
3866
3867   game.GamePlayed = !tape.playing;
3868
3869   game.LevelSolved_GameWon = FALSE;
3870   game.LevelSolved_GameEnd = FALSE;
3871   game.LevelSolved_SaveTape = FALSE;
3872   game.LevelSolved_SaveScore = FALSE;
3873
3874   game.LevelSolved_CountingTime = 0;
3875   game.LevelSolved_CountingScore = 0;
3876   game.LevelSolved_CountingHealth = 0;
3877
3878   game.RestartGameRequested = FALSE;
3879
3880   game.panel.active = TRUE;
3881
3882   game.no_level_time_limit = (level.time == 0);
3883   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3884
3885   game.yamyam_content_nr = 0;
3886   game.robot_wheel_active = FALSE;
3887   game.magic_wall_active = FALSE;
3888   game.magic_wall_time_left = 0;
3889   game.light_time_left = 0;
3890   game.timegate_time_left = 0;
3891   game.switchgate_pos = 0;
3892   game.wind_direction = level.wind_direction_initial;
3893
3894   game.time_final = 0;
3895   game.score_time_final = 0;
3896
3897   game.score = 0;
3898   game.score_final = 0;
3899
3900   game.health = MAX_HEALTH;
3901   game.health_final = MAX_HEALTH;
3902
3903   game.gems_still_needed = level.gems_needed;
3904   game.sokoban_fields_still_needed = 0;
3905   game.sokoban_objects_still_needed = 0;
3906   game.lights_still_needed = 0;
3907   game.players_still_needed = 0;
3908   game.friends_still_needed = 0;
3909
3910   game.lenses_time_left = 0;
3911   game.magnify_time_left = 0;
3912
3913   game.ball_active = level.ball_active_initial;
3914   game.ball_content_nr = 0;
3915
3916   game.explosions_delayed = TRUE;
3917
3918   game.envelope_active = FALSE;
3919
3920   // special case: set custom artwork setting to initial value
3921   game.use_masked_elements = game.use_masked_elements_initial;
3922
3923   for (i = 0; i < NUM_BELTS; i++)
3924   {
3925     game.belt_dir[i] = MV_NONE;
3926     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3927   }
3928
3929   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3930     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3931
3932 #if DEBUG_INIT_PLAYER
3933   DebugPrintPlayerStatus("Player status at level initialization");
3934 #endif
3935
3936   SCAN_PLAYFIELD(x, y)
3937   {
3938     Tile[x][y] = Last[x][y] = level.field[x][y];
3939     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3940     ChangeDelay[x][y] = 0;
3941     ChangePage[x][y] = -1;
3942     CustomValue[x][y] = 0;              // initialized in InitField()
3943     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3944     AmoebaNr[x][y] = 0;
3945     WasJustMoving[x][y] = 0;
3946     WasJustFalling[x][y] = 0;
3947     CheckCollision[x][y] = 0;
3948     CheckImpact[x][y] = 0;
3949     Stop[x][y] = FALSE;
3950     Pushed[x][y] = FALSE;
3951
3952     ChangeCount[x][y] = 0;
3953     ChangeEvent[x][y] = -1;
3954
3955     ExplodePhase[x][y] = 0;
3956     ExplodeDelay[x][y] = 0;
3957     ExplodeField[x][y] = EX_TYPE_NONE;
3958
3959     RunnerVisit[x][y] = 0;
3960     PlayerVisit[x][y] = 0;
3961
3962     GfxFrame[x][y] = 0;
3963     GfxRandom[x][y] = INIT_GFX_RANDOM();
3964     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3965     GfxElement[x][y] = EL_UNDEFINED;
3966     GfxElementEmpty[x][y] = EL_EMPTY;
3967     GfxAction[x][y] = ACTION_DEFAULT;
3968     GfxDir[x][y] = MV_NONE;
3969     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3970   }
3971
3972   SCAN_PLAYFIELD(x, y)
3973   {
3974     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3975       emulate_bd = FALSE;
3976     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3977       emulate_sp = FALSE;
3978
3979     InitField(x, y, TRUE);
3980
3981     ResetGfxAnimation(x, y);
3982   }
3983
3984   InitBeltMovement();
3985
3986   // required if level does not contain any "empty space" element
3987   if (element_info[EL_EMPTY].use_gfx_element)
3988     game.use_masked_elements = TRUE;
3989
3990   for (i = 0; i < MAX_PLAYERS; i++)
3991   {
3992     struct PlayerInfo *player = &stored_player[i];
3993
3994     // set number of special actions for bored and sleeping animation
3995     player->num_special_action_bored =
3996       get_num_special_action(player->artwork_element,
3997                              ACTION_BORING_1, ACTION_BORING_LAST);
3998     player->num_special_action_sleeping =
3999       get_num_special_action(player->artwork_element,
4000                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4001   }
4002
4003   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4004                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4005
4006   // initialize type of slippery elements
4007   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4008   {
4009     if (!IS_CUSTOM_ELEMENT(i))
4010     {
4011       // default: elements slip down either to the left or right randomly
4012       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4013
4014       // SP style elements prefer to slip down on the left side
4015       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4016         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4017
4018       // BD style elements prefer to slip down on the left side
4019       if (game.emulation == EMU_BOULDERDASH)
4020         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4021     }
4022   }
4023
4024   // initialize explosion and ignition delay
4025   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4026   {
4027     if (!IS_CUSTOM_ELEMENT(i))
4028     {
4029       int num_phase = 8;
4030       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4031                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4032                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4033       int last_phase = (num_phase + 1) * delay;
4034       int half_phase = (num_phase / 2) * delay;
4035
4036       element_info[i].explosion_delay = last_phase - 1;
4037       element_info[i].ignition_delay = half_phase;
4038
4039       if (i == EL_BLACK_ORB)
4040         element_info[i].ignition_delay = 1;
4041     }
4042   }
4043
4044   // correct non-moving belts to start moving left
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   // use preferred player also in local single-player mode
4051   if (!network.enabled && !game.team_mode)
4052   {
4053     int new_index_nr = setup.network_player_nr;
4054
4055     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4056     {
4057       for (i = 0; i < MAX_PLAYERS; i++)
4058         stored_player[i].connected_locally = FALSE;
4059
4060       stored_player[new_index_nr].connected_locally = TRUE;
4061     }
4062   }
4063
4064   for (i = 0; i < MAX_PLAYERS; i++)
4065   {
4066     stored_player[i].connected = FALSE;
4067
4068     // in network game mode, the local player might not be the first player
4069     if (stored_player[i].connected_locally)
4070       local_player = &stored_player[i];
4071   }
4072
4073   if (!network.enabled)
4074     local_player->connected = TRUE;
4075
4076   if (tape.playing)
4077   {
4078     for (i = 0; i < MAX_PLAYERS; i++)
4079       stored_player[i].connected = tape.player_participates[i];
4080   }
4081   else if (network.enabled)
4082   {
4083     // add team mode players connected over the network (needed for correct
4084     // assignment of player figures from level to locally playing players)
4085
4086     for (i = 0; i < MAX_PLAYERS; i++)
4087       if (stored_player[i].connected_network)
4088         stored_player[i].connected = TRUE;
4089   }
4090   else if (game.team_mode)
4091   {
4092     // try to guess locally connected team mode players (needed for correct
4093     // assignment of player figures from level to locally playing players)
4094
4095     for (i = 0; i < MAX_PLAYERS; i++)
4096       if (setup.input[i].use_joystick ||
4097           setup.input[i].key.left != KSYM_UNDEFINED)
4098         stored_player[i].connected = TRUE;
4099   }
4100
4101 #if DEBUG_INIT_PLAYER
4102   DebugPrintPlayerStatus("Player status after level initialization");
4103 #endif
4104
4105 #if DEBUG_INIT_PLAYER
4106   Debug("game:init:player", "Reassigning players ...");
4107 #endif
4108
4109   // check if any connected player was not found in playfield
4110   for (i = 0; i < MAX_PLAYERS; i++)
4111   {
4112     struct PlayerInfo *player = &stored_player[i];
4113
4114     if (player->connected && !player->present)
4115     {
4116       struct PlayerInfo *field_player = NULL;
4117
4118 #if DEBUG_INIT_PLAYER
4119       Debug("game:init:player",
4120             "- looking for field player for player %d ...", i + 1);
4121 #endif
4122
4123       // assign first free player found that is present in the playfield
4124
4125       // first try: look for unmapped playfield player that is not connected
4126       for (j = 0; j < MAX_PLAYERS; j++)
4127         if (field_player == NULL &&
4128             stored_player[j].present &&
4129             !stored_player[j].mapped &&
4130             !stored_player[j].connected)
4131           field_player = &stored_player[j];
4132
4133       // second try: look for *any* unmapped playfield player
4134       for (j = 0; j < MAX_PLAYERS; j++)
4135         if (field_player == NULL &&
4136             stored_player[j].present &&
4137             !stored_player[j].mapped)
4138           field_player = &stored_player[j];
4139
4140       if (field_player != NULL)
4141       {
4142         int jx = field_player->jx, jy = field_player->jy;
4143
4144 #if DEBUG_INIT_PLAYER
4145         Debug("game:init:player", "- found player %d",
4146               field_player->index_nr + 1);
4147 #endif
4148
4149         player->present = FALSE;
4150         player->active = FALSE;
4151
4152         field_player->present = TRUE;
4153         field_player->active = TRUE;
4154
4155         /*
4156         player->initial_element = field_player->initial_element;
4157         player->artwork_element = field_player->artwork_element;
4158
4159         player->block_last_field       = field_player->block_last_field;
4160         player->block_delay_adjustment = field_player->block_delay_adjustment;
4161         */
4162
4163         StorePlayer[jx][jy] = field_player->element_nr;
4164
4165         field_player->jx = field_player->last_jx = jx;
4166         field_player->jy = field_player->last_jy = jy;
4167
4168         if (local_player == player)
4169           local_player = field_player;
4170
4171         map_player_action[field_player->index_nr] = i;
4172
4173         field_player->mapped = TRUE;
4174
4175 #if DEBUG_INIT_PLAYER
4176         Debug("game:init:player", "- map_player_action[%d] == %d",
4177               field_player->index_nr + 1, i + 1);
4178 #endif
4179       }
4180     }
4181
4182     if (player->connected && player->present)
4183       player->mapped = TRUE;
4184   }
4185
4186 #if DEBUG_INIT_PLAYER
4187   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4188 #endif
4189
4190 #else
4191
4192   // check if any connected player was not found in playfield
4193   for (i = 0; i < MAX_PLAYERS; i++)
4194   {
4195     struct PlayerInfo *player = &stored_player[i];
4196
4197     if (player->connected && !player->present)
4198     {
4199       for (j = 0; j < MAX_PLAYERS; j++)
4200       {
4201         struct PlayerInfo *field_player = &stored_player[j];
4202         int jx = field_player->jx, jy = field_player->jy;
4203
4204         // assign first free player found that is present in the playfield
4205         if (field_player->present && !field_player->connected)
4206         {
4207           player->present = TRUE;
4208           player->active = TRUE;
4209
4210           field_player->present = FALSE;
4211           field_player->active = FALSE;
4212
4213           player->initial_element = field_player->initial_element;
4214           player->artwork_element = field_player->artwork_element;
4215
4216           player->block_last_field       = field_player->block_last_field;
4217           player->block_delay_adjustment = field_player->block_delay_adjustment;
4218
4219           StorePlayer[jx][jy] = player->element_nr;
4220
4221           player->jx = player->last_jx = jx;
4222           player->jy = player->last_jy = jy;
4223
4224           break;
4225         }
4226       }
4227     }
4228   }
4229 #endif
4230
4231 #if 0
4232   Debug("game:init:player", "local_player->present == %d",
4233         local_player->present);
4234 #endif
4235
4236   // set focus to local player for network games, else to all players
4237   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4238   game.centered_player_nr_next = game.centered_player_nr;
4239   game.set_centered_player = FALSE;
4240   game.set_centered_player_wrap = FALSE;
4241
4242   if (network_playing && tape.recording)
4243   {
4244     // store client dependent player focus when recording network games
4245     tape.centered_player_nr_next = game.centered_player_nr_next;
4246     tape.set_centered_player = TRUE;
4247   }
4248
4249   if (tape.playing)
4250   {
4251     // when playing a tape, eliminate all players who do not participate
4252
4253 #if USE_NEW_PLAYER_ASSIGNMENTS
4254
4255     if (!game.team_mode)
4256     {
4257       for (i = 0; i < MAX_PLAYERS; i++)
4258       {
4259         if (stored_player[i].active &&
4260             !tape.player_participates[map_player_action[i]])
4261         {
4262           struct PlayerInfo *player = &stored_player[i];
4263           int jx = player->jx, jy = player->jy;
4264
4265 #if DEBUG_INIT_PLAYER
4266           Debug("game:init:player", "Removing player %d at (%d, %d)",
4267                 i + 1, jx, jy);
4268 #endif
4269
4270           player->active = FALSE;
4271           StorePlayer[jx][jy] = 0;
4272           Tile[jx][jy] = EL_EMPTY;
4273         }
4274       }
4275     }
4276
4277 #else
4278
4279     for (i = 0; i < MAX_PLAYERS; i++)
4280     {
4281       if (stored_player[i].active &&
4282           !tape.player_participates[i])
4283       {
4284         struct PlayerInfo *player = &stored_player[i];
4285         int jx = player->jx, jy = player->jy;
4286
4287         player->active = FALSE;
4288         StorePlayer[jx][jy] = 0;
4289         Tile[jx][jy] = EL_EMPTY;
4290       }
4291     }
4292 #endif
4293   }
4294   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4295   {
4296     // when in single player mode, eliminate all but the local player
4297
4298     for (i = 0; i < MAX_PLAYERS; i++)
4299     {
4300       struct PlayerInfo *player = &stored_player[i];
4301
4302       if (player->active && player != local_player)
4303       {
4304         int jx = player->jx, jy = player->jy;
4305
4306         player->active = FALSE;
4307         player->present = FALSE;
4308
4309         StorePlayer[jx][jy] = 0;
4310         Tile[jx][jy] = EL_EMPTY;
4311       }
4312     }
4313   }
4314
4315   for (i = 0; i < MAX_PLAYERS; i++)
4316     if (stored_player[i].active)
4317       game.players_still_needed++;
4318
4319   if (level.solved_by_one_player)
4320     game.players_still_needed = 1;
4321
4322   // when recording the game, store which players take part in the game
4323   if (tape.recording)
4324   {
4325 #if USE_NEW_PLAYER_ASSIGNMENTS
4326     for (i = 0; i < MAX_PLAYERS; i++)
4327       if (stored_player[i].connected)
4328         tape.player_participates[i] = TRUE;
4329 #else
4330     for (i = 0; i < MAX_PLAYERS; i++)
4331       if (stored_player[i].active)
4332         tape.player_participates[i] = TRUE;
4333 #endif
4334   }
4335
4336 #if DEBUG_INIT_PLAYER
4337   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4338 #endif
4339
4340   if (BorderElement == EL_EMPTY)
4341   {
4342     SBX_Left = 0;
4343     SBX_Right = lev_fieldx - SCR_FIELDX;
4344     SBY_Upper = 0;
4345     SBY_Lower = lev_fieldy - SCR_FIELDY;
4346   }
4347   else
4348   {
4349     SBX_Left = -1;
4350     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4351     SBY_Upper = -1;
4352     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4353   }
4354
4355   if (full_lev_fieldx <= SCR_FIELDX)
4356     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4357   if (full_lev_fieldy <= SCR_FIELDY)
4358     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4359
4360   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4361     SBX_Left--;
4362   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4363     SBY_Upper--;
4364
4365   // if local player not found, look for custom element that might create
4366   // the player (make some assumptions about the right custom element)
4367   if (!local_player->present)
4368   {
4369     int start_x = 0, start_y = 0;
4370     int found_rating = 0;
4371     int found_element = EL_UNDEFINED;
4372     int player_nr = local_player->index_nr;
4373
4374     SCAN_PLAYFIELD(x, y)
4375     {
4376       int element = Tile[x][y];
4377       int content;
4378       int xx, yy;
4379       boolean is_player;
4380
4381       if (level.use_start_element[player_nr] &&
4382           level.start_element[player_nr] == element &&
4383           found_rating < 4)
4384       {
4385         start_x = x;
4386         start_y = y;
4387
4388         found_rating = 4;
4389         found_element = element;
4390       }
4391
4392       if (!IS_CUSTOM_ELEMENT(element))
4393         continue;
4394
4395       if (CAN_CHANGE(element))
4396       {
4397         for (i = 0; i < element_info[element].num_change_pages; i++)
4398         {
4399           // check for player created from custom element as single target
4400           content = element_info[element].change_page[i].target_element;
4401           is_player = IS_PLAYER_ELEMENT(content);
4402
4403           if (is_player && (found_rating < 3 ||
4404                             (found_rating == 3 && element < found_element)))
4405           {
4406             start_x = x;
4407             start_y = y;
4408
4409             found_rating = 3;
4410             found_element = element;
4411           }
4412         }
4413       }
4414
4415       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4416       {
4417         // check for player created from custom element as explosion content
4418         content = element_info[element].content.e[xx][yy];
4419         is_player = IS_PLAYER_ELEMENT(content);
4420
4421         if (is_player && (found_rating < 2 ||
4422                           (found_rating == 2 && element < found_element)))
4423         {
4424           start_x = x + xx - 1;
4425           start_y = y + yy - 1;
4426
4427           found_rating = 2;
4428           found_element = element;
4429         }
4430
4431         if (!CAN_CHANGE(element))
4432           continue;
4433
4434         for (i = 0; i < element_info[element].num_change_pages; i++)
4435         {
4436           // check for player created from custom element as extended target
4437           content =
4438             element_info[element].change_page[i].target_content.e[xx][yy];
4439
4440           is_player = IS_PLAYER_ELEMENT(content);
4441
4442           if (is_player && (found_rating < 1 ||
4443                             (found_rating == 1 && element < found_element)))
4444           {
4445             start_x = x + xx - 1;
4446             start_y = y + yy - 1;
4447
4448             found_rating = 1;
4449             found_element = element;
4450           }
4451         }
4452       }
4453     }
4454
4455     scroll_x = SCROLL_POSITION_X(start_x);
4456     scroll_y = SCROLL_POSITION_Y(start_y);
4457   }
4458   else
4459   {
4460     scroll_x = SCROLL_POSITION_X(local_player->jx);
4461     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4462   }
4463
4464   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4465     scroll_x = game.forced_scroll_x;
4466   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4467     scroll_y = game.forced_scroll_y;
4468
4469   // !!! FIX THIS (START) !!!
4470   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4471   {
4472     InitGameEngine_EM();
4473   }
4474   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4475   {
4476     InitGameEngine_SP();
4477   }
4478   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4479   {
4480     InitGameEngine_MM();
4481   }
4482   else
4483   {
4484     DrawLevel(REDRAW_FIELD);
4485     DrawAllPlayers();
4486
4487     // after drawing the level, correct some elements
4488     if (game.timegate_time_left == 0)
4489       CloseAllOpenTimegates();
4490   }
4491
4492   // blit playfield from scroll buffer to normal back buffer for fading in
4493   BlitScreenToBitmap(backbuffer);
4494   // !!! FIX THIS (END) !!!
4495
4496   DrawMaskedBorder(fade_mask);
4497
4498   FadeIn(fade_mask);
4499
4500 #if 1
4501   // full screen redraw is required at this point in the following cases:
4502   // - special editor door undrawn when game was started from level editor
4503   // - drawing area (playfield) was changed and has to be removed completely
4504   redraw_mask = REDRAW_ALL;
4505   BackToFront();
4506 #endif
4507
4508   if (!game.restart_level)
4509   {
4510     // copy default game door content to main double buffer
4511
4512     // !!! CHECK AGAIN !!!
4513     SetPanelBackground();
4514     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4515     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4516   }
4517
4518   SetPanelBackground();
4519   SetDrawBackgroundMask(REDRAW_DOOR_1);
4520
4521   UpdateAndDisplayGameControlValues();
4522
4523   if (!game.restart_level)
4524   {
4525     UnmapGameButtons();
4526     UnmapTapeButtons();
4527
4528     FreeGameButtons();
4529     CreateGameButtons();
4530
4531     MapGameButtons();
4532     MapTapeButtons();
4533
4534     // copy actual game door content to door double buffer for OpenDoor()
4535     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4536
4537     OpenDoor(DOOR_OPEN_ALL);
4538
4539     KeyboardAutoRepeatOffUnlessAutoplay();
4540
4541 #if DEBUG_INIT_PLAYER
4542     DebugPrintPlayerStatus("Player status (final)");
4543 #endif
4544   }
4545
4546   UnmapAllGadgets();
4547
4548   MapGameButtons();
4549   MapTapeButtons();
4550
4551   if (!game.restart_level && !tape.playing)
4552   {
4553     LevelStats_incPlayed(level_nr);
4554
4555     SaveLevelSetup_SeriesInfo();
4556   }
4557
4558   game.restart_level = FALSE;
4559   game.request_active = FALSE;
4560
4561   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4562     InitGameActions_MM();
4563
4564   SaveEngineSnapshotToListInitial();
4565
4566   if (!game.restart_level)
4567   {
4568     PlaySound(SND_GAME_STARTING);
4569
4570     if (setup.sound_music)
4571       PlayLevelMusic();
4572   }
4573
4574   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4575 }
4576
4577 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4578                         int actual_player_x, int actual_player_y)
4579 {
4580   // this is used for non-R'n'D game engines to update certain engine values
4581
4582   // needed to determine if sounds are played within the visible screen area
4583   scroll_x = actual_scroll_x;
4584   scroll_y = actual_scroll_y;
4585
4586   // needed to get player position for "follow finger" playing input method
4587   local_player->jx = actual_player_x;
4588   local_player->jy = actual_player_y;
4589 }
4590
4591 void InitMovDir(int x, int y)
4592 {
4593   int i, element = Tile[x][y];
4594   static int xy[4][2] =
4595   {
4596     {  0, +1 },
4597     { +1,  0 },
4598     {  0, -1 },
4599     { -1,  0 }
4600   };
4601   static int direction[3][4] =
4602   {
4603     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4604     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4605     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4606   };
4607
4608   switch (element)
4609   {
4610     case EL_BUG_RIGHT:
4611     case EL_BUG_UP:
4612     case EL_BUG_LEFT:
4613     case EL_BUG_DOWN:
4614       Tile[x][y] = EL_BUG;
4615       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4616       break;
4617
4618     case EL_SPACESHIP_RIGHT:
4619     case EL_SPACESHIP_UP:
4620     case EL_SPACESHIP_LEFT:
4621     case EL_SPACESHIP_DOWN:
4622       Tile[x][y] = EL_SPACESHIP;
4623       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4624       break;
4625
4626     case EL_BD_BUTTERFLY_RIGHT:
4627     case EL_BD_BUTTERFLY_UP:
4628     case EL_BD_BUTTERFLY_LEFT:
4629     case EL_BD_BUTTERFLY_DOWN:
4630       Tile[x][y] = EL_BD_BUTTERFLY;
4631       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4632       break;
4633
4634     case EL_BD_FIREFLY_RIGHT:
4635     case EL_BD_FIREFLY_UP:
4636     case EL_BD_FIREFLY_LEFT:
4637     case EL_BD_FIREFLY_DOWN:
4638       Tile[x][y] = EL_BD_FIREFLY;
4639       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4640       break;
4641
4642     case EL_PACMAN_RIGHT:
4643     case EL_PACMAN_UP:
4644     case EL_PACMAN_LEFT:
4645     case EL_PACMAN_DOWN:
4646       Tile[x][y] = EL_PACMAN;
4647       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4648       break;
4649
4650     case EL_YAMYAM_LEFT:
4651     case EL_YAMYAM_RIGHT:
4652     case EL_YAMYAM_UP:
4653     case EL_YAMYAM_DOWN:
4654       Tile[x][y] = EL_YAMYAM;
4655       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4656       break;
4657
4658     case EL_SP_SNIKSNAK:
4659       MovDir[x][y] = MV_UP;
4660       break;
4661
4662     case EL_SP_ELECTRON:
4663       MovDir[x][y] = MV_LEFT;
4664       break;
4665
4666     case EL_MOLE_LEFT:
4667     case EL_MOLE_RIGHT:
4668     case EL_MOLE_UP:
4669     case EL_MOLE_DOWN:
4670       Tile[x][y] = EL_MOLE;
4671       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4672       break;
4673
4674     case EL_SPRING_LEFT:
4675     case EL_SPRING_RIGHT:
4676       Tile[x][y] = EL_SPRING;
4677       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4678       break;
4679
4680     default:
4681       if (IS_CUSTOM_ELEMENT(element))
4682       {
4683         struct ElementInfo *ei = &element_info[element];
4684         int move_direction_initial = ei->move_direction_initial;
4685         int move_pattern = ei->move_pattern;
4686
4687         if (move_direction_initial == MV_START_PREVIOUS)
4688         {
4689           if (MovDir[x][y] != MV_NONE)
4690             return;
4691
4692           move_direction_initial = MV_START_AUTOMATIC;
4693         }
4694
4695         if (move_direction_initial == MV_START_RANDOM)
4696           MovDir[x][y] = 1 << RND(4);
4697         else if (move_direction_initial & MV_ANY_DIRECTION)
4698           MovDir[x][y] = move_direction_initial;
4699         else if (move_pattern == MV_ALL_DIRECTIONS ||
4700                  move_pattern == MV_TURNING_LEFT ||
4701                  move_pattern == MV_TURNING_RIGHT ||
4702                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4703                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4704                  move_pattern == MV_TURNING_RANDOM)
4705           MovDir[x][y] = 1 << RND(4);
4706         else if (move_pattern == MV_HORIZONTAL)
4707           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4708         else if (move_pattern == MV_VERTICAL)
4709           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4710         else if (move_pattern & MV_ANY_DIRECTION)
4711           MovDir[x][y] = element_info[element].move_pattern;
4712         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4713                  move_pattern == MV_ALONG_RIGHT_SIDE)
4714         {
4715           // use random direction as default start direction
4716           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4717             MovDir[x][y] = 1 << RND(4);
4718
4719           for (i = 0; i < NUM_DIRECTIONS; i++)
4720           {
4721             int x1 = x + xy[i][0];
4722             int y1 = y + xy[i][1];
4723
4724             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4725             {
4726               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4727                 MovDir[x][y] = direction[0][i];
4728               else
4729                 MovDir[x][y] = direction[1][i];
4730
4731               break;
4732             }
4733           }
4734         }                
4735       }
4736       else
4737       {
4738         MovDir[x][y] = 1 << RND(4);
4739
4740         if (element != EL_BUG &&
4741             element != EL_SPACESHIP &&
4742             element != EL_BD_BUTTERFLY &&
4743             element != EL_BD_FIREFLY)
4744           break;
4745
4746         for (i = 0; i < NUM_DIRECTIONS; i++)
4747         {
4748           int x1 = x + xy[i][0];
4749           int y1 = y + xy[i][1];
4750
4751           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4752           {
4753             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4754             {
4755               MovDir[x][y] = direction[0][i];
4756               break;
4757             }
4758             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4759                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4760             {
4761               MovDir[x][y] = direction[1][i];
4762               break;
4763             }
4764           }
4765         }
4766       }
4767       break;
4768   }
4769
4770   GfxDir[x][y] = MovDir[x][y];
4771 }
4772
4773 void InitAmoebaNr(int x, int y)
4774 {
4775   int i;
4776   int group_nr = AmoebaNeighbourNr(x, y);
4777
4778   if (group_nr == 0)
4779   {
4780     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4781     {
4782       if (AmoebaCnt[i] == 0)
4783       {
4784         group_nr = i;
4785         break;
4786       }
4787     }
4788   }
4789
4790   AmoebaNr[x][y] = group_nr;
4791   AmoebaCnt[group_nr]++;
4792   AmoebaCnt2[group_nr]++;
4793 }
4794
4795 static void LevelSolved_SetFinalGameValues(void)
4796 {
4797   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4798   game.score_time_final = (level.use_step_counter ? TimePlayed :
4799                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4800
4801   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4802                       game_em.lev->score :
4803                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4804                       game_mm.score :
4805                       game.score);
4806
4807   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4808                        MM_HEALTH(game_mm.laser_overload_value) :
4809                        game.health);
4810
4811   game.LevelSolved_CountingTime = game.time_final;
4812   game.LevelSolved_CountingScore = game.score_final;
4813   game.LevelSolved_CountingHealth = game.health_final;
4814 }
4815
4816 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4817 {
4818   game.LevelSolved_CountingTime = time;
4819   game.LevelSolved_CountingScore = score;
4820   game.LevelSolved_CountingHealth = health;
4821
4822   game_panel_controls[GAME_PANEL_TIME].value = time;
4823   game_panel_controls[GAME_PANEL_SCORE].value = score;
4824   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4825
4826   DisplayGameControlValues();
4827 }
4828
4829 static void LevelSolved(void)
4830 {
4831   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4832       game.players_still_needed > 0)
4833     return;
4834
4835   game.LevelSolved = TRUE;
4836   game.GameOver = TRUE;
4837
4838   tape.solved = TRUE;
4839
4840   // needed here to display correct panel values while player walks into exit
4841   LevelSolved_SetFinalGameValues();
4842 }
4843
4844 void GameWon(void)
4845 {
4846   static int time_count_steps;
4847   static int time, time_final;
4848   static float score, score_final; // needed for time score < 10 for 10 seconds
4849   static int health, health_final;
4850   static int game_over_delay_1 = 0;
4851   static int game_over_delay_2 = 0;
4852   static int game_over_delay_3 = 0;
4853   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4854   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4855
4856   if (!game.LevelSolved_GameWon)
4857   {
4858     int i;
4859
4860     // do not start end game actions before the player stops moving (to exit)
4861     if (local_player->active && local_player->MovPos)
4862       return;
4863
4864     // calculate final game values after player finished walking into exit
4865     LevelSolved_SetFinalGameValues();
4866
4867     game.LevelSolved_GameWon = TRUE;
4868     game.LevelSolved_SaveTape = tape.recording;
4869     game.LevelSolved_SaveScore = !tape.playing;
4870
4871     if (!tape.playing)
4872     {
4873       LevelStats_incSolved(level_nr);
4874
4875       SaveLevelSetup_SeriesInfo();
4876     }
4877
4878     if (tape.auto_play)         // tape might already be stopped here
4879       tape.auto_play_level_solved = TRUE;
4880
4881     TapeStop();
4882
4883     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4884     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4885     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4886
4887     time = time_final = game.time_final;
4888     score = score_final = game.score_final;
4889     health = health_final = game.health_final;
4890
4891     // update game panel values before (delayed) counting of score (if any)
4892     LevelSolved_DisplayFinalGameValues(time, score, health);
4893
4894     // if level has time score defined, calculate new final game values
4895     if (time_score > 0)
4896     {
4897       int time_final_max = 999;
4898       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4899       int time_frames = 0;
4900       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4901       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4902
4903       if (TimeLeft > 0)
4904       {
4905         time_final = 0;
4906         time_frames = time_frames_left;
4907       }
4908       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4909       {
4910         time_final = time_final_max;
4911         time_frames = time_frames_final_max - time_frames_played;
4912       }
4913
4914       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4915
4916       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4917
4918       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4919       {
4920         health_final = 0;
4921         score_final += health * time_score;
4922       }
4923
4924       game.score_final = score_final;
4925       game.health_final = health_final;
4926     }
4927
4928     // if not counting score after game, immediately update game panel values
4929     if (level_editor_test_game || !setup.count_score_after_game)
4930     {
4931       time = time_final;
4932       score = score_final;
4933
4934       LevelSolved_DisplayFinalGameValues(time, score, health);
4935     }
4936
4937     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4938     {
4939       // check if last player has left the level
4940       if (game.exit_x >= 0 &&
4941           game.exit_y >= 0)
4942       {
4943         int x = game.exit_x;
4944         int y = game.exit_y;
4945         int element = Tile[x][y];
4946
4947         // close exit door after last player
4948         if ((game.all_players_gone &&
4949              (element == EL_EXIT_OPEN ||
4950               element == EL_SP_EXIT_OPEN ||
4951               element == EL_STEEL_EXIT_OPEN)) ||
4952             element == EL_EM_EXIT_OPEN ||
4953             element == EL_EM_STEEL_EXIT_OPEN)
4954         {
4955
4956           Tile[x][y] =
4957             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4958              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4959              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4960              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4961              EL_EM_STEEL_EXIT_CLOSING);
4962
4963           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4964         }
4965
4966         // player disappears
4967         DrawLevelField(x, y);
4968       }
4969
4970       for (i = 0; i < MAX_PLAYERS; i++)
4971       {
4972         struct PlayerInfo *player = &stored_player[i];
4973
4974         if (player->present)
4975         {
4976           RemovePlayer(player);
4977
4978           // player disappears
4979           DrawLevelField(player->jx, player->jy);
4980         }
4981       }
4982     }
4983
4984     PlaySound(SND_GAME_WINNING);
4985   }
4986
4987   if (setup.count_score_after_game)
4988   {
4989     if (time != time_final)
4990     {
4991       if (game_over_delay_1 > 0)
4992       {
4993         game_over_delay_1--;
4994
4995         return;
4996       }
4997
4998       int time_to_go = ABS(time_final - time);
4999       int time_count_dir = (time < time_final ? +1 : -1);
5000
5001       if (time_to_go < time_count_steps)
5002         time_count_steps = 1;
5003
5004       time  += time_count_steps * time_count_dir;
5005       score += time_count_steps * time_score;
5006
5007       // set final score to correct rounding differences after counting score
5008       if (time == time_final)
5009         score = score_final;
5010
5011       LevelSolved_DisplayFinalGameValues(time, score, health);
5012
5013       if (time == time_final)
5014         StopSound(SND_GAME_LEVELTIME_BONUS);
5015       else if (setup.sound_loops)
5016         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5017       else
5018         PlaySound(SND_GAME_LEVELTIME_BONUS);
5019
5020       return;
5021     }
5022
5023     if (health != health_final)
5024     {
5025       if (game_over_delay_2 > 0)
5026       {
5027         game_over_delay_2--;
5028
5029         return;
5030       }
5031
5032       int health_count_dir = (health < health_final ? +1 : -1);
5033
5034       health += health_count_dir;
5035       score  += time_score;
5036
5037       LevelSolved_DisplayFinalGameValues(time, score, health);
5038
5039       if (health == health_final)
5040         StopSound(SND_GAME_LEVELTIME_BONUS);
5041       else if (setup.sound_loops)
5042         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5043       else
5044         PlaySound(SND_GAME_LEVELTIME_BONUS);
5045
5046       return;
5047     }
5048   }
5049
5050   game.panel.active = FALSE;
5051
5052   if (game_over_delay_3 > 0)
5053   {
5054     game_over_delay_3--;
5055
5056     return;
5057   }
5058
5059   GameEnd();
5060 }
5061
5062 void GameEnd(void)
5063 {
5064   // used instead of "level_nr" (needed for network games)
5065   int last_level_nr = levelset.level_nr;
5066   boolean tape_saved = FALSE;
5067
5068   game.LevelSolved_GameEnd = TRUE;
5069
5070   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5071   {
5072     // make sure that request dialog to save tape does not open door again
5073     if (!global.use_envelope_request)
5074       CloseDoor(DOOR_CLOSE_1);
5075
5076     // ask to save tape
5077     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5078
5079     // set unique basename for score tape (also saved in high score table)
5080     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5081   }
5082
5083   // if no tape is to be saved, close both doors simultaneously
5084   CloseDoor(DOOR_CLOSE_ALL);
5085
5086   if (level_editor_test_game || score_info_tape_play)
5087   {
5088     SetGameStatus(GAME_MODE_MAIN);
5089
5090     DrawMainMenu();
5091
5092     return;
5093   }
5094
5095   if (!game.LevelSolved_SaveScore)
5096   {
5097     SetGameStatus(GAME_MODE_MAIN);
5098
5099     DrawMainMenu();
5100
5101     return;
5102   }
5103
5104   if (level_nr == leveldir_current->handicap_level)
5105   {
5106     leveldir_current->handicap_level++;
5107
5108     SaveLevelSetup_SeriesInfo();
5109   }
5110
5111   // save score and score tape before potentially erasing tape below
5112   NewHighScore(last_level_nr, tape_saved);
5113
5114   if (setup.increment_levels &&
5115       level_nr < leveldir_current->last_level &&
5116       !network_playing)
5117   {
5118     level_nr++;         // advance to next level
5119     TapeErase();        // start with empty tape
5120
5121     if (setup.auto_play_next_level)
5122     {
5123       scores.continue_playing = TRUE;
5124       scores.next_level_nr = level_nr;
5125
5126       LoadLevel(level_nr);
5127
5128       SaveLevelSetup_SeriesInfo();
5129     }
5130   }
5131
5132   if (scores.last_added >= 0 && setup.show_scores_after_game)
5133   {
5134     SetGameStatus(GAME_MODE_SCORES);
5135
5136     DrawHallOfFame(last_level_nr);
5137   }
5138   else if (scores.continue_playing)
5139   {
5140     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5141   }
5142   else
5143   {
5144     SetGameStatus(GAME_MODE_MAIN);
5145
5146     DrawMainMenu();
5147   }
5148 }
5149
5150 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5151                          boolean one_score_entry_per_name)
5152 {
5153   int i;
5154
5155   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5156     return -1;
5157
5158   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5159   {
5160     struct ScoreEntry *entry = &list->entry[i];
5161     boolean score_is_better = (new_entry->score >  entry->score);
5162     boolean score_is_equal  = (new_entry->score == entry->score);
5163     boolean time_is_better  = (new_entry->time  <  entry->time);
5164     boolean time_is_equal   = (new_entry->time  == entry->time);
5165     boolean better_by_score = (score_is_better ||
5166                                (score_is_equal && time_is_better));
5167     boolean better_by_time  = (time_is_better ||
5168                                (time_is_equal && score_is_better));
5169     boolean is_better = (level.rate_time_over_score ? better_by_time :
5170                          better_by_score);
5171     boolean entry_is_empty = (entry->score == 0 &&
5172                               entry->time == 0);
5173
5174     // prevent adding server score entries if also existing in local score file
5175     // (special case: historic score entries have an empty tape basename entry)
5176     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5177         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5178     {
5179       // add fields from server score entry not stored in local score entry
5180       // (currently, this means setting platform, version and country fields;
5181       // in rare cases, this may also correct an invalid score value, as
5182       // historic scores might have been truncated to 16-bit values locally)
5183       *entry = *new_entry;
5184
5185       return -1;
5186     }
5187
5188     if (is_better || entry_is_empty)
5189     {
5190       // player has made it to the hall of fame
5191
5192       if (i < MAX_SCORE_ENTRIES - 1)
5193       {
5194         int m = MAX_SCORE_ENTRIES - 1;
5195         int l;
5196
5197         if (one_score_entry_per_name)
5198         {
5199           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5200             if (strEqual(list->entry[l].name, new_entry->name))
5201               m = l;
5202
5203           if (m == i)   // player's new highscore overwrites his old one
5204             goto put_into_list;
5205         }
5206
5207         for (l = m; l > i; l--)
5208           list->entry[l] = list->entry[l - 1];
5209       }
5210
5211       put_into_list:
5212
5213       *entry = *new_entry;
5214
5215       return i;
5216     }
5217     else if (one_score_entry_per_name &&
5218              strEqual(entry->name, new_entry->name))
5219     {
5220       // player already in high score list with better score or time
5221
5222       return -1;
5223     }
5224   }
5225
5226   // special case: new score is beyond the last high score list position
5227   return MAX_SCORE_ENTRIES;
5228 }
5229
5230 void NewHighScore(int level_nr, boolean tape_saved)
5231 {
5232   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5233   boolean one_per_name = FALSE;
5234
5235   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5236   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5237
5238   new_entry.score = game.score_final;
5239   new_entry.time = game.score_time_final;
5240
5241   LoadScore(level_nr);
5242
5243   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5244
5245   if (scores.last_added >= MAX_SCORE_ENTRIES)
5246   {
5247     scores.last_added = MAX_SCORE_ENTRIES - 1;
5248     scores.force_last_added = TRUE;
5249
5250     scores.entry[scores.last_added] = new_entry;
5251
5252     // store last added local score entry (before merging server scores)
5253     scores.last_added_local = scores.last_added;
5254
5255     return;
5256   }
5257
5258   if (scores.last_added < 0)
5259     return;
5260
5261   SaveScore(level_nr);
5262
5263   // store last added local score entry (before merging server scores)
5264   scores.last_added_local = scores.last_added;
5265
5266   if (!game.LevelSolved_SaveTape)
5267     return;
5268
5269   SaveScoreTape(level_nr);
5270
5271   if (setup.ask_for_using_api_server)
5272   {
5273     setup.use_api_server =
5274       Request("Upload your score and tape to the high score server?", REQ_ASK);
5275
5276     if (!setup.use_api_server)
5277       Request("Not using high score server! Use setup menu to enable again!",
5278               REQ_CONFIRM);
5279
5280     runtime.use_api_server = setup.use_api_server;
5281
5282     // after asking for using API server once, do not ask again
5283     setup.ask_for_using_api_server = FALSE;
5284
5285     SaveSetup_ServerSetup();
5286   }
5287
5288   SaveServerScore(level_nr, tape_saved);
5289 }
5290
5291 void MergeServerScore(void)
5292 {
5293   struct ScoreEntry last_added_entry;
5294   boolean one_per_name = FALSE;
5295   int i;
5296
5297   if (scores.last_added >= 0)
5298     last_added_entry = scores.entry[scores.last_added];
5299
5300   for (i = 0; i < server_scores.num_entries; i++)
5301   {
5302     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5303
5304     if (pos >= 0 && pos <= scores.last_added)
5305       scores.last_added++;
5306   }
5307
5308   if (scores.last_added >= MAX_SCORE_ENTRIES)
5309   {
5310     scores.last_added = MAX_SCORE_ENTRIES - 1;
5311     scores.force_last_added = TRUE;
5312
5313     scores.entry[scores.last_added] = last_added_entry;
5314   }
5315 }
5316
5317 static int getElementMoveStepsizeExt(int x, int y, int direction)
5318 {
5319   int element = Tile[x][y];
5320   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5321   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5322   int horiz_move = (dx != 0);
5323   int sign = (horiz_move ? dx : dy);
5324   int step = sign * element_info[element].move_stepsize;
5325
5326   // special values for move stepsize for spring and things on conveyor belt
5327   if (horiz_move)
5328   {
5329     if (CAN_FALL(element) &&
5330         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5331       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5332     else if (element == EL_SPRING)
5333       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5334   }
5335
5336   return step;
5337 }
5338
5339 static int getElementMoveStepsize(int x, int y)
5340 {
5341   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5342 }
5343
5344 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5345 {
5346   if (player->GfxAction != action || player->GfxDir != dir)
5347   {
5348     player->GfxAction = action;
5349     player->GfxDir = dir;
5350     player->Frame = 0;
5351     player->StepFrame = 0;
5352   }
5353 }
5354
5355 static void ResetGfxFrame(int x, int y)
5356 {
5357   // profiling showed that "autotest" spends 10~20% of its time in this function
5358   if (DrawingDeactivatedField())
5359     return;
5360
5361   int element = Tile[x][y];
5362   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5363
5364   if (graphic_info[graphic].anim_global_sync)
5365     GfxFrame[x][y] = FrameCounter;
5366   else if (graphic_info[graphic].anim_global_anim_sync)
5367     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5368   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5369     GfxFrame[x][y] = CustomValue[x][y];
5370   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5371     GfxFrame[x][y] = element_info[element].collect_score;
5372   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5373     GfxFrame[x][y] = ChangeDelay[x][y];
5374 }
5375
5376 static void ResetGfxAnimation(int x, int y)
5377 {
5378   GfxAction[x][y] = ACTION_DEFAULT;
5379   GfxDir[x][y] = MovDir[x][y];
5380   GfxFrame[x][y] = 0;
5381
5382   ResetGfxFrame(x, y);
5383 }
5384
5385 static void ResetRandomAnimationValue(int x, int y)
5386 {
5387   GfxRandom[x][y] = INIT_GFX_RANDOM();
5388 }
5389
5390 static void InitMovingField(int x, int y, int direction)
5391 {
5392   int element = Tile[x][y];
5393   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5394   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5395   int newx = x + dx;
5396   int newy = y + dy;
5397   boolean is_moving_before, is_moving_after;
5398
5399   // check if element was/is moving or being moved before/after mode change
5400   is_moving_before = (WasJustMoving[x][y] != 0);
5401   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5402
5403   // reset animation only for moving elements which change direction of moving
5404   // or which just started or stopped moving
5405   // (else CEs with property "can move" / "not moving" are reset each frame)
5406   if (is_moving_before != is_moving_after ||
5407       direction != MovDir[x][y])
5408     ResetGfxAnimation(x, y);
5409
5410   MovDir[x][y] = direction;
5411   GfxDir[x][y] = direction;
5412
5413   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5414                      direction == MV_DOWN && CAN_FALL(element) ?
5415                      ACTION_FALLING : ACTION_MOVING);
5416
5417   // this is needed for CEs with property "can move" / "not moving"
5418
5419   if (is_moving_after)
5420   {
5421     if (Tile[newx][newy] == EL_EMPTY)
5422       Tile[newx][newy] = EL_BLOCKED;
5423
5424     MovDir[newx][newy] = MovDir[x][y];
5425
5426     CustomValue[newx][newy] = CustomValue[x][y];
5427
5428     GfxFrame[newx][newy] = GfxFrame[x][y];
5429     GfxRandom[newx][newy] = GfxRandom[x][y];
5430     GfxAction[newx][newy] = GfxAction[x][y];
5431     GfxDir[newx][newy] = GfxDir[x][y];
5432   }
5433 }
5434
5435 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5436 {
5437   int direction = MovDir[x][y];
5438   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5439   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5440
5441   *goes_to_x = newx;
5442   *goes_to_y = newy;
5443 }
5444
5445 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5446 {
5447   int direction = MovDir[x][y];
5448   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5449   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5450
5451   *comes_from_x = oldx;
5452   *comes_from_y = oldy;
5453 }
5454
5455 static int MovingOrBlocked2Element(int x, int y)
5456 {
5457   int element = Tile[x][y];
5458
5459   if (element == EL_BLOCKED)
5460   {
5461     int oldx, oldy;
5462
5463     Blocked2Moving(x, y, &oldx, &oldy);
5464
5465     return Tile[oldx][oldy];
5466   }
5467
5468   return element;
5469 }
5470
5471 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5472 {
5473   // like MovingOrBlocked2Element(), but if element is moving
5474   // and (x, y) is the field the moving element is just leaving,
5475   // return EL_BLOCKED instead of the element value
5476   int element = Tile[x][y];
5477
5478   if (IS_MOVING(x, y))
5479   {
5480     if (element == EL_BLOCKED)
5481     {
5482       int oldx, oldy;
5483
5484       Blocked2Moving(x, y, &oldx, &oldy);
5485       return Tile[oldx][oldy];
5486     }
5487     else
5488       return EL_BLOCKED;
5489   }
5490   else
5491     return element;
5492 }
5493
5494 static void RemoveField(int x, int y)
5495 {
5496   Tile[x][y] = EL_EMPTY;
5497
5498   MovPos[x][y] = 0;
5499   MovDir[x][y] = 0;
5500   MovDelay[x][y] = 0;
5501
5502   CustomValue[x][y] = 0;
5503
5504   AmoebaNr[x][y] = 0;
5505   ChangeDelay[x][y] = 0;
5506   ChangePage[x][y] = -1;
5507   Pushed[x][y] = FALSE;
5508
5509   GfxElement[x][y] = EL_UNDEFINED;
5510   GfxAction[x][y] = ACTION_DEFAULT;
5511   GfxDir[x][y] = MV_NONE;
5512 }
5513
5514 static void RemoveMovingField(int x, int y)
5515 {
5516   int oldx = x, oldy = y, newx = x, newy = y;
5517   int element = Tile[x][y];
5518   int next_element = EL_UNDEFINED;
5519
5520   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5521     return;
5522
5523   if (IS_MOVING(x, y))
5524   {
5525     Moving2Blocked(x, y, &newx, &newy);
5526
5527     if (Tile[newx][newy] != EL_BLOCKED)
5528     {
5529       // element is moving, but target field is not free (blocked), but
5530       // already occupied by something different (example: acid pool);
5531       // in this case, only remove the moving field, but not the target
5532
5533       RemoveField(oldx, oldy);
5534
5535       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5536
5537       TEST_DrawLevelField(oldx, oldy);
5538
5539       return;
5540     }
5541   }
5542   else if (element == EL_BLOCKED)
5543   {
5544     Blocked2Moving(x, y, &oldx, &oldy);
5545     if (!IS_MOVING(oldx, oldy))
5546       return;
5547   }
5548
5549   if (element == EL_BLOCKED &&
5550       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5551        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5552        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5553        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5554        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5555        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5556     next_element = get_next_element(Tile[oldx][oldy]);
5557
5558   RemoveField(oldx, oldy);
5559   RemoveField(newx, newy);
5560
5561   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5562
5563   if (next_element != EL_UNDEFINED)
5564     Tile[oldx][oldy] = next_element;
5565
5566   TEST_DrawLevelField(oldx, oldy);
5567   TEST_DrawLevelField(newx, newy);
5568 }
5569
5570 void DrawDynamite(int x, int y)
5571 {
5572   int sx = SCREENX(x), sy = SCREENY(y);
5573   int graphic = el2img(Tile[x][y]);
5574   int frame;
5575
5576   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5577     return;
5578
5579   if (IS_WALKABLE_INSIDE(Back[x][y]))
5580     return;
5581
5582   if (Back[x][y])
5583     DrawLevelElement(x, y, Back[x][y]);
5584   else if (Store[x][y])
5585     DrawLevelElement(x, y, Store[x][y]);
5586   else if (game.use_masked_elements)
5587     DrawLevelElement(x, y, EL_EMPTY);
5588
5589   frame = getGraphicAnimationFrameXY(graphic, x, y);
5590
5591   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5592     DrawGraphicThruMask(sx, sy, graphic, frame);
5593   else
5594     DrawGraphic(sx, sy, graphic, frame);
5595 }
5596
5597 static void CheckDynamite(int x, int y)
5598 {
5599   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5600   {
5601     MovDelay[x][y]--;
5602
5603     if (MovDelay[x][y] != 0)
5604     {
5605       DrawDynamite(x, y);
5606       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5607
5608       return;
5609     }
5610   }
5611
5612   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5613
5614   Bang(x, y);
5615 }
5616
5617 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5618 {
5619   boolean num_checked_players = 0;
5620   int i;
5621
5622   for (i = 0; i < MAX_PLAYERS; i++)
5623   {
5624     if (stored_player[i].active)
5625     {
5626       int sx = stored_player[i].jx;
5627       int sy = stored_player[i].jy;
5628
5629       if (num_checked_players == 0)
5630       {
5631         *sx1 = *sx2 = sx;
5632         *sy1 = *sy2 = sy;
5633       }
5634       else
5635       {
5636         *sx1 = MIN(*sx1, sx);
5637         *sy1 = MIN(*sy1, sy);
5638         *sx2 = MAX(*sx2, sx);
5639         *sy2 = MAX(*sy2, sy);
5640       }
5641
5642       num_checked_players++;
5643     }
5644   }
5645 }
5646
5647 static boolean checkIfAllPlayersFitToScreen_RND(void)
5648 {
5649   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5650
5651   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5652
5653   return (sx2 - sx1 < SCR_FIELDX &&
5654           sy2 - sy1 < SCR_FIELDY);
5655 }
5656
5657 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5658 {
5659   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5660
5661   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5662
5663   *sx = (sx1 + sx2) / 2;
5664   *sy = (sy1 + sy2) / 2;
5665 }
5666
5667 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5668                                boolean center_screen, boolean quick_relocation)
5669 {
5670   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5671   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5672   boolean no_delay = (tape.warp_forward);
5673   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5674   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5675   int new_scroll_x, new_scroll_y;
5676
5677   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5678   {
5679     // case 1: quick relocation inside visible screen (without scrolling)
5680
5681     RedrawPlayfield();
5682
5683     return;
5684   }
5685
5686   if (!level.shifted_relocation || center_screen)
5687   {
5688     // relocation _with_ centering of screen
5689
5690     new_scroll_x = SCROLL_POSITION_X(x);
5691     new_scroll_y = SCROLL_POSITION_Y(y);
5692   }
5693   else
5694   {
5695     // relocation _without_ centering of screen
5696
5697     // apply distance between old and new player position to scroll position
5698     int shifted_scroll_x = scroll_x + (x - old_x);
5699     int shifted_scroll_y = scroll_y + (y - old_y);
5700
5701     // make sure that shifted scroll position does not scroll beyond screen
5702     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5703     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5704
5705     // special case for teleporting from one end of the playfield to the other
5706     // (this kludge prevents the destination area to be shifted by half a tile
5707     // against the source destination for even screen width or screen height;
5708     // probably most useful when used with high "game.forced_scroll_delay_value"
5709     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5710     if (quick_relocation)
5711     {
5712       if (EVEN(SCR_FIELDX))
5713       {
5714         // relocate (teleport) between left and right border (half or full)
5715         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5716           new_scroll_x = SBX_Right;
5717         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5718           new_scroll_x = SBX_Right - 1;
5719         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5720           new_scroll_x = SBX_Left;
5721         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5722           new_scroll_x = SBX_Left + 1;
5723       }
5724
5725       if (EVEN(SCR_FIELDY))
5726       {
5727         // relocate (teleport) between top and bottom border (half or full)
5728         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5729           new_scroll_y = SBY_Lower;
5730         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5731           new_scroll_y = SBY_Lower - 1;
5732         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5733           new_scroll_y = SBY_Upper;
5734         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5735           new_scroll_y = SBY_Upper + 1;
5736       }
5737     }
5738   }
5739
5740   if (quick_relocation)
5741   {
5742     // case 2: quick relocation (redraw without visible scrolling)
5743
5744     scroll_x = new_scroll_x;
5745     scroll_y = new_scroll_y;
5746
5747     RedrawPlayfield();
5748
5749     return;
5750   }
5751
5752   // case 3: visible relocation (with scrolling to new position)
5753
5754   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5755
5756   SetVideoFrameDelay(wait_delay_value);
5757
5758   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5759   {
5760     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5761     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5762
5763     if (dx == 0 && dy == 0)             // no scrolling needed at all
5764       break;
5765
5766     scroll_x -= dx;
5767     scroll_y -= dy;
5768
5769     // set values for horizontal/vertical screen scrolling (half tile size)
5770     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5771     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5772     int pos_x = dx * TILEX / 2;
5773     int pos_y = dy * TILEY / 2;
5774     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5775     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5776
5777     ScrollLevel(dx, dy);
5778     DrawAllPlayers();
5779
5780     // scroll in two steps of half tile size to make things smoother
5781     BlitScreenToBitmapExt_RND(window, fx, fy);
5782
5783     // scroll second step to align at full tile size
5784     BlitScreenToBitmap(window);
5785   }
5786
5787   DrawAllPlayers();
5788   BackToFront();
5789
5790   SetVideoFrameDelay(frame_delay_value_old);
5791 }
5792
5793 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5794 {
5795   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5796   int player_nr = GET_PLAYER_NR(el_player);
5797   struct PlayerInfo *player = &stored_player[player_nr];
5798   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5799   boolean no_delay = (tape.warp_forward);
5800   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5801   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5802   int old_jx = player->jx;
5803   int old_jy = player->jy;
5804   int old_element = Tile[old_jx][old_jy];
5805   int element = Tile[jx][jy];
5806   boolean player_relocated = (old_jx != jx || old_jy != jy);
5807
5808   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5809   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5810   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5811   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5812   int leave_side_horiz = move_dir_horiz;
5813   int leave_side_vert  = move_dir_vert;
5814   int enter_side = enter_side_horiz | enter_side_vert;
5815   int leave_side = leave_side_horiz | leave_side_vert;
5816
5817   if (player->buried)           // do not reanimate dead player
5818     return;
5819
5820   if (!player_relocated)        // no need to relocate the player
5821     return;
5822
5823   if (IS_PLAYER(jx, jy))        // player already placed at new position
5824   {
5825     RemoveField(jx, jy);        // temporarily remove newly placed player
5826     DrawLevelField(jx, jy);
5827   }
5828
5829   if (player->present)
5830   {
5831     while (player->MovPos)
5832     {
5833       ScrollPlayer(player, SCROLL_GO_ON);
5834       ScrollScreen(NULL, SCROLL_GO_ON);
5835
5836       AdvanceFrameAndPlayerCounters(player->index_nr);
5837
5838       DrawPlayer(player);
5839
5840       BackToFront_WithFrameDelay(wait_delay_value);
5841     }
5842
5843     DrawPlayer(player);         // needed here only to cleanup last field
5844     DrawLevelField(player->jx, player->jy);     // remove player graphic
5845
5846     player->is_moving = FALSE;
5847   }
5848
5849   if (IS_CUSTOM_ELEMENT(old_element))
5850     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5851                                CE_LEFT_BY_PLAYER,
5852                                player->index_bit, leave_side);
5853
5854   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5855                                       CE_PLAYER_LEAVES_X,
5856                                       player->index_bit, leave_side);
5857
5858   Tile[jx][jy] = el_player;
5859   InitPlayerField(jx, jy, el_player, TRUE);
5860
5861   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5862      possible that the relocation target field did not contain a player element,
5863      but a walkable element, to which the new player was relocated -- in this
5864      case, restore that (already initialized!) element on the player field */
5865   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5866   {
5867     Tile[jx][jy] = element;     // restore previously existing element
5868   }
5869
5870   // only visually relocate centered player
5871   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5872                      FALSE, level.instant_relocation);
5873
5874   TestIfPlayerTouchesBadThing(jx, jy);
5875   TestIfPlayerTouchesCustomElement(jx, jy);
5876
5877   if (IS_CUSTOM_ELEMENT(element))
5878     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5879                                player->index_bit, enter_side);
5880
5881   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5882                                       player->index_bit, enter_side);
5883
5884   if (player->is_switching)
5885   {
5886     /* ensure that relocation while still switching an element does not cause
5887        a new element to be treated as also switched directly after relocation
5888        (this is important for teleporter switches that teleport the player to
5889        a place where another teleporter switch is in the same direction, which
5890        would then incorrectly be treated as immediately switched before the
5891        direction key that caused the switch was released) */
5892
5893     player->switch_x += jx - old_jx;
5894     player->switch_y += jy - old_jy;
5895   }
5896 }
5897
5898 static void Explode(int ex, int ey, int phase, int mode)
5899 {
5900   int x, y;
5901   int last_phase;
5902   int border_element;
5903
5904   if (game.explosions_delayed)
5905   {
5906     ExplodeField[ex][ey] = mode;
5907     return;
5908   }
5909
5910   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5911   {
5912     int center_element = Tile[ex][ey];
5913     int ce_value = CustomValue[ex][ey];
5914     int ce_score = element_info[center_element].collect_score;
5915     int artwork_element, explosion_element;     // set these values later
5916
5917     // remove things displayed in background while burning dynamite
5918     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5919       Back[ex][ey] = 0;
5920
5921     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5922     {
5923       // put moving element to center field (and let it explode there)
5924       center_element = MovingOrBlocked2Element(ex, ey);
5925       RemoveMovingField(ex, ey);
5926       Tile[ex][ey] = center_element;
5927     }
5928
5929     // now "center_element" is finally determined -- set related values now
5930     artwork_element = center_element;           // for custom player artwork
5931     explosion_element = center_element;         // for custom player artwork
5932
5933     if (IS_PLAYER(ex, ey))
5934     {
5935       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5936
5937       artwork_element = stored_player[player_nr].artwork_element;
5938
5939       if (level.use_explosion_element[player_nr])
5940       {
5941         explosion_element = level.explosion_element[player_nr];
5942         artwork_element = explosion_element;
5943       }
5944     }
5945
5946     if (mode == EX_TYPE_NORMAL ||
5947         mode == EX_TYPE_CENTER ||
5948         mode == EX_TYPE_CROSS)
5949       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5950
5951     last_phase = element_info[explosion_element].explosion_delay + 1;
5952
5953     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5954     {
5955       int xx = x - ex + 1;
5956       int yy = y - ey + 1;
5957       int element;
5958
5959       if (!IN_LEV_FIELD(x, y) ||
5960           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5961           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5962         continue;
5963
5964       element = Tile[x][y];
5965
5966       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5967       {
5968         element = MovingOrBlocked2Element(x, y);
5969
5970         if (!IS_EXPLOSION_PROOF(element))
5971           RemoveMovingField(x, y);
5972       }
5973
5974       // indestructible elements can only explode in center (but not flames)
5975       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5976                                            mode == EX_TYPE_BORDER)) ||
5977           element == EL_FLAMES)
5978         continue;
5979
5980       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5981          behaviour, for example when touching a yamyam that explodes to rocks
5982          with active deadly shield, a rock is created under the player !!! */
5983       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5984 #if 0
5985       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5986           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5987            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5988 #else
5989       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5990 #endif
5991       {
5992         if (IS_ACTIVE_BOMB(element))
5993         {
5994           // re-activate things under the bomb like gate or penguin
5995           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5996           Back[x][y] = 0;
5997         }
5998
5999         continue;
6000       }
6001
6002       // save walkable background elements while explosion on same tile
6003       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6004           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6005         Back[x][y] = element;
6006
6007       // ignite explodable elements reached by other explosion
6008       if (element == EL_EXPLOSION)
6009         element = Store2[x][y];
6010
6011       if (AmoebaNr[x][y] &&
6012           (element == EL_AMOEBA_FULL ||
6013            element == EL_BD_AMOEBA ||
6014            element == EL_AMOEBA_GROWING))
6015       {
6016         AmoebaCnt[AmoebaNr[x][y]]--;
6017         AmoebaCnt2[AmoebaNr[x][y]]--;
6018       }
6019
6020       RemoveField(x, y);
6021
6022       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6023       {
6024         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6025
6026         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6027
6028         if (PLAYERINFO(ex, ey)->use_murphy)
6029           Store[x][y] = EL_EMPTY;
6030       }
6031
6032       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6033       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6034       else if (IS_PLAYER_ELEMENT(center_element))
6035         Store[x][y] = EL_EMPTY;
6036       else if (center_element == EL_YAMYAM)
6037         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6038       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6039         Store[x][y] = element_info[center_element].content.e[xx][yy];
6040 #if 1
6041       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6042       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6043       // otherwise) -- FIX THIS !!!
6044       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6045         Store[x][y] = element_info[element].content.e[1][1];
6046 #else
6047       else if (!CAN_EXPLODE(element))
6048         Store[x][y] = element_info[element].content.e[1][1];
6049 #endif
6050       else
6051         Store[x][y] = EL_EMPTY;
6052
6053       if (IS_CUSTOM_ELEMENT(center_element))
6054         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6055                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6056                        Store[x][y] >= EL_PREV_CE_8 &&
6057                        Store[x][y] <= EL_NEXT_CE_8 ?
6058                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6059                        Store[x][y]);
6060
6061       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6062           center_element == EL_AMOEBA_TO_DIAMOND)
6063         Store2[x][y] = element;
6064
6065       Tile[x][y] = EL_EXPLOSION;
6066       GfxElement[x][y] = artwork_element;
6067
6068       ExplodePhase[x][y] = 1;
6069       ExplodeDelay[x][y] = last_phase;
6070
6071       Stop[x][y] = TRUE;
6072     }
6073
6074     if (center_element == EL_YAMYAM)
6075       game.yamyam_content_nr =
6076         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6077
6078     return;
6079   }
6080
6081   if (Stop[ex][ey])
6082     return;
6083
6084   x = ex;
6085   y = ey;
6086
6087   if (phase == 1)
6088     GfxFrame[x][y] = 0;         // restart explosion animation
6089
6090   last_phase = ExplodeDelay[x][y];
6091
6092   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6093
6094   // this can happen if the player leaves an explosion just in time
6095   if (GfxElement[x][y] == EL_UNDEFINED)
6096     GfxElement[x][y] = EL_EMPTY;
6097
6098   border_element = Store2[x][y];
6099   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6100     border_element = StorePlayer[x][y];
6101
6102   if (phase == element_info[border_element].ignition_delay ||
6103       phase == last_phase)
6104   {
6105     boolean border_explosion = FALSE;
6106
6107     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6108         !PLAYER_EXPLOSION_PROTECTED(x, y))
6109     {
6110       KillPlayerUnlessExplosionProtected(x, y);
6111       border_explosion = TRUE;
6112     }
6113     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6114     {
6115       Tile[x][y] = Store2[x][y];
6116       Store2[x][y] = 0;
6117       Bang(x, y);
6118       border_explosion = TRUE;
6119     }
6120     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6121     {
6122       AmoebaToDiamond(x, y);
6123       Store2[x][y] = 0;
6124       border_explosion = TRUE;
6125     }
6126
6127     // if an element just explodes due to another explosion (chain-reaction),
6128     // do not immediately end the new explosion when it was the last frame of
6129     // the explosion (as it would be done in the following "if"-statement!)
6130     if (border_explosion && phase == last_phase)
6131       return;
6132   }
6133
6134   // this can happen if the player was just killed by an explosion
6135   if (GfxElement[x][y] == EL_UNDEFINED)
6136     GfxElement[x][y] = EL_EMPTY;
6137
6138   if (phase == last_phase)
6139   {
6140     int element;
6141
6142     element = Tile[x][y] = Store[x][y];
6143     Store[x][y] = Store2[x][y] = 0;
6144     GfxElement[x][y] = EL_UNDEFINED;
6145
6146     // player can escape from explosions and might therefore be still alive
6147     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6148         element <= EL_PLAYER_IS_EXPLODING_4)
6149     {
6150       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6151       int explosion_element = EL_PLAYER_1 + player_nr;
6152       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6153       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6154
6155       if (level.use_explosion_element[player_nr])
6156         explosion_element = level.explosion_element[player_nr];
6157
6158       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6159                     element_info[explosion_element].content.e[xx][yy]);
6160     }
6161
6162     // restore probably existing indestructible background element
6163     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6164       element = Tile[x][y] = Back[x][y];
6165     Back[x][y] = 0;
6166
6167     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6168     GfxDir[x][y] = MV_NONE;
6169     ChangeDelay[x][y] = 0;
6170     ChangePage[x][y] = -1;
6171
6172     CustomValue[x][y] = 0;
6173
6174     InitField_WithBug2(x, y, FALSE);
6175
6176     TEST_DrawLevelField(x, y);
6177
6178     TestIfElementTouchesCustomElement(x, y);
6179
6180     if (GFX_CRUMBLED(element))
6181       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6182
6183     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6184       StorePlayer[x][y] = 0;
6185
6186     if (IS_PLAYER_ELEMENT(element))
6187       RelocatePlayer(x, y, element);
6188   }
6189   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6190   {
6191     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6192     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6193
6194     if (phase == 1)
6195       TEST_DrawLevelFieldCrumbled(x, y);
6196
6197     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6198     {
6199       DrawLevelElement(x, y, Back[x][y]);
6200       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6201     }
6202     else if (IS_WALKABLE_UNDER(Back[x][y]))
6203     {
6204       DrawLevelGraphic(x, y, graphic, frame);
6205       DrawLevelElementThruMask(x, y, Back[x][y]);
6206     }
6207     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6208       DrawLevelGraphic(x, y, graphic, frame);
6209   }
6210 }
6211
6212 static void DynaExplode(int ex, int ey)
6213 {
6214   int i, j;
6215   int dynabomb_element = Tile[ex][ey];
6216   int dynabomb_size = 1;
6217   boolean dynabomb_xl = FALSE;
6218   struct PlayerInfo *player;
6219   struct XY *xy = xy_topdown;
6220
6221   if (IS_ACTIVE_BOMB(dynabomb_element))
6222   {
6223     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6224     dynabomb_size = player->dynabomb_size;
6225     dynabomb_xl = player->dynabomb_xl;
6226     player->dynabombs_left++;
6227   }
6228
6229   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6230
6231   for (i = 0; i < NUM_DIRECTIONS; i++)
6232   {
6233     for (j = 1; j <= dynabomb_size; j++)
6234     {
6235       int x = ex + j * xy[i].x;
6236       int y = ey + j * xy[i].y;
6237       int element;
6238
6239       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6240         break;
6241
6242       element = Tile[x][y];
6243
6244       // do not restart explosions of fields with active bombs
6245       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6246         continue;
6247
6248       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6249
6250       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6251           !IS_DIGGABLE(element) && !dynabomb_xl)
6252         break;
6253     }
6254   }
6255 }
6256
6257 void Bang(int x, int y)
6258 {
6259   int element = MovingOrBlocked2Element(x, y);
6260   int explosion_type = EX_TYPE_NORMAL;
6261
6262   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6263   {
6264     struct PlayerInfo *player = PLAYERINFO(x, y);
6265
6266     element = Tile[x][y] = player->initial_element;
6267
6268     if (level.use_explosion_element[player->index_nr])
6269     {
6270       int explosion_element = level.explosion_element[player->index_nr];
6271
6272       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6273         explosion_type = EX_TYPE_CROSS;
6274       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6275         explosion_type = EX_TYPE_CENTER;
6276     }
6277   }
6278
6279   switch (element)
6280   {
6281     case EL_BUG:
6282     case EL_SPACESHIP:
6283     case EL_BD_BUTTERFLY:
6284     case EL_BD_FIREFLY:
6285     case EL_YAMYAM:
6286     case EL_DARK_YAMYAM:
6287     case EL_ROBOT:
6288     case EL_PACMAN:
6289     case EL_MOLE:
6290       RaiseScoreElement(element);
6291       break;
6292
6293     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6294     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6295     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6296     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6297     case EL_DYNABOMB_INCREASE_NUMBER:
6298     case EL_DYNABOMB_INCREASE_SIZE:
6299     case EL_DYNABOMB_INCREASE_POWER:
6300       explosion_type = EX_TYPE_DYNA;
6301       break;
6302
6303     case EL_DC_LANDMINE:
6304       explosion_type = EX_TYPE_CENTER;
6305       break;
6306
6307     case EL_PENGUIN:
6308     case EL_LAMP:
6309     case EL_LAMP_ACTIVE:
6310     case EL_AMOEBA_TO_DIAMOND:
6311       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6312         explosion_type = EX_TYPE_CENTER;
6313       break;
6314
6315     default:
6316       if (element_info[element].explosion_type == EXPLODES_CROSS)
6317         explosion_type = EX_TYPE_CROSS;
6318       else if (element_info[element].explosion_type == EXPLODES_1X1)
6319         explosion_type = EX_TYPE_CENTER;
6320       break;
6321   }
6322
6323   if (explosion_type == EX_TYPE_DYNA)
6324     DynaExplode(x, y);
6325   else
6326     Explode(x, y, EX_PHASE_START, explosion_type);
6327
6328   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6329 }
6330
6331 static void SplashAcid(int x, int y)
6332 {
6333   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6334       (!IN_LEV_FIELD(x - 1, y - 2) ||
6335        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6336     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6337
6338   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6339       (!IN_LEV_FIELD(x + 1, y - 2) ||
6340        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6341     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6342
6343   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6344 }
6345
6346 static void InitBeltMovement(void)
6347 {
6348   static int belt_base_element[4] =
6349   {
6350     EL_CONVEYOR_BELT_1_LEFT,
6351     EL_CONVEYOR_BELT_2_LEFT,
6352     EL_CONVEYOR_BELT_3_LEFT,
6353     EL_CONVEYOR_BELT_4_LEFT
6354   };
6355   static int belt_base_active_element[4] =
6356   {
6357     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6358     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6359     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6360     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6361   };
6362
6363   int x, y, i, j;
6364
6365   // set frame order for belt animation graphic according to belt direction
6366   for (i = 0; i < NUM_BELTS; i++)
6367   {
6368     int belt_nr = i;
6369
6370     for (j = 0; j < NUM_BELT_PARTS; j++)
6371     {
6372       int element = belt_base_active_element[belt_nr] + j;
6373       int graphic_1 = el2img(element);
6374       int graphic_2 = el2panelimg(element);
6375
6376       if (game.belt_dir[i] == MV_LEFT)
6377       {
6378         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6379         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6380       }
6381       else
6382       {
6383         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6384         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6385       }
6386     }
6387   }
6388
6389   SCAN_PLAYFIELD(x, y)
6390   {
6391     int element = Tile[x][y];
6392
6393     for (i = 0; i < NUM_BELTS; i++)
6394     {
6395       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6396       {
6397         int e_belt_nr = getBeltNrFromBeltElement(element);
6398         int belt_nr = i;
6399
6400         if (e_belt_nr == belt_nr)
6401         {
6402           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6403
6404           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6405         }
6406       }
6407     }
6408   }
6409 }
6410
6411 static void ToggleBeltSwitch(int x, int y)
6412 {
6413   static int belt_base_element[4] =
6414   {
6415     EL_CONVEYOR_BELT_1_LEFT,
6416     EL_CONVEYOR_BELT_2_LEFT,
6417     EL_CONVEYOR_BELT_3_LEFT,
6418     EL_CONVEYOR_BELT_4_LEFT
6419   };
6420   static int belt_base_active_element[4] =
6421   {
6422     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6423     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6424     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6425     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6426   };
6427   static int belt_base_switch_element[4] =
6428   {
6429     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6430     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6431     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6432     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6433   };
6434   static int belt_move_dir[4] =
6435   {
6436     MV_LEFT,
6437     MV_NONE,
6438     MV_RIGHT,
6439     MV_NONE,
6440   };
6441
6442   int element = Tile[x][y];
6443   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6444   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6445   int belt_dir = belt_move_dir[belt_dir_nr];
6446   int xx, yy, i;
6447
6448   if (!IS_BELT_SWITCH(element))
6449     return;
6450
6451   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6452   game.belt_dir[belt_nr] = belt_dir;
6453
6454   if (belt_dir_nr == 3)
6455     belt_dir_nr = 1;
6456
6457   // set frame order for belt animation graphic according to belt direction
6458   for (i = 0; i < NUM_BELT_PARTS; i++)
6459   {
6460     int element = belt_base_active_element[belt_nr] + i;
6461     int graphic_1 = el2img(element);
6462     int graphic_2 = el2panelimg(element);
6463
6464     if (belt_dir == MV_LEFT)
6465     {
6466       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6467       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6468     }
6469     else
6470     {
6471       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6472       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(xx, yy)
6477   {
6478     int element = Tile[xx][yy];
6479
6480     if (IS_BELT_SWITCH(element))
6481     {
6482       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6483
6484       if (e_belt_nr == belt_nr)
6485       {
6486         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6487         TEST_DrawLevelField(xx, yy);
6488       }
6489     }
6490     else if (IS_BELT(element) && belt_dir != MV_NONE)
6491     {
6492       int e_belt_nr = getBeltNrFromBeltElement(element);
6493
6494       if (e_belt_nr == belt_nr)
6495       {
6496         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6497
6498         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6499         TEST_DrawLevelField(xx, yy);
6500       }
6501     }
6502     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6503     {
6504       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6505
6506       if (e_belt_nr == belt_nr)
6507       {
6508         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6509
6510         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6511         TEST_DrawLevelField(xx, yy);
6512       }
6513     }
6514   }
6515 }
6516
6517 static void ToggleSwitchgateSwitch(void)
6518 {
6519   int xx, yy;
6520
6521   game.switchgate_pos = !game.switchgate_pos;
6522
6523   SCAN_PLAYFIELD(xx, yy)
6524   {
6525     int element = Tile[xx][yy];
6526
6527     if (element == EL_SWITCHGATE_SWITCH_UP)
6528     {
6529       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6530       TEST_DrawLevelField(xx, yy);
6531     }
6532     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6533     {
6534       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6535       TEST_DrawLevelField(xx, yy);
6536     }
6537     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6538     {
6539       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6540       TEST_DrawLevelField(xx, yy);
6541     }
6542     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6543     {
6544       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6545       TEST_DrawLevelField(xx, yy);
6546     }
6547     else if (element == EL_SWITCHGATE_OPEN ||
6548              element == EL_SWITCHGATE_OPENING)
6549     {
6550       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6551
6552       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6553     }
6554     else if (element == EL_SWITCHGATE_CLOSED ||
6555              element == EL_SWITCHGATE_CLOSING)
6556     {
6557       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6558
6559       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6560     }
6561   }
6562 }
6563
6564 static int getInvisibleActiveFromInvisibleElement(int element)
6565 {
6566   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6567           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6568           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6569           element);
6570 }
6571
6572 static int getInvisibleFromInvisibleActiveElement(int element)
6573 {
6574   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6575           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6576           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6577           element);
6578 }
6579
6580 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6581 {
6582   int x, y;
6583
6584   SCAN_PLAYFIELD(x, y)
6585   {
6586     int element = Tile[x][y];
6587
6588     if (element == EL_LIGHT_SWITCH &&
6589         game.light_time_left > 0)
6590     {
6591       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6592       TEST_DrawLevelField(x, y);
6593     }
6594     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6595              game.light_time_left == 0)
6596     {
6597       Tile[x][y] = EL_LIGHT_SWITCH;
6598       TEST_DrawLevelField(x, y);
6599     }
6600     else if (element == EL_EMC_DRIPPER &&
6601              game.light_time_left > 0)
6602     {
6603       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6604       TEST_DrawLevelField(x, y);
6605     }
6606     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6607              game.light_time_left == 0)
6608     {
6609       Tile[x][y] = EL_EMC_DRIPPER;
6610       TEST_DrawLevelField(x, y);
6611     }
6612     else if (element == EL_INVISIBLE_STEELWALL ||
6613              element == EL_INVISIBLE_WALL ||
6614              element == EL_INVISIBLE_SAND)
6615     {
6616       if (game.light_time_left > 0)
6617         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6618
6619       TEST_DrawLevelField(x, y);
6620
6621       // uncrumble neighbour fields, if needed
6622       if (element == EL_INVISIBLE_SAND)
6623         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6624     }
6625     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6626              element == EL_INVISIBLE_WALL_ACTIVE ||
6627              element == EL_INVISIBLE_SAND_ACTIVE)
6628     {
6629       if (game.light_time_left == 0)
6630         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6631
6632       TEST_DrawLevelField(x, y);
6633
6634       // re-crumble neighbour fields, if needed
6635       if (element == EL_INVISIBLE_SAND)
6636         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6637     }
6638   }
6639 }
6640
6641 static void RedrawAllInvisibleElementsForLenses(void)
6642 {
6643   int x, y;
6644
6645   SCAN_PLAYFIELD(x, y)
6646   {
6647     int element = Tile[x][y];
6648
6649     if (element == EL_EMC_DRIPPER &&
6650         game.lenses_time_left > 0)
6651     {
6652       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6653       TEST_DrawLevelField(x, y);
6654     }
6655     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6656              game.lenses_time_left == 0)
6657     {
6658       Tile[x][y] = EL_EMC_DRIPPER;
6659       TEST_DrawLevelField(x, y);
6660     }
6661     else if (element == EL_INVISIBLE_STEELWALL ||
6662              element == EL_INVISIBLE_WALL ||
6663              element == EL_INVISIBLE_SAND)
6664     {
6665       if (game.lenses_time_left > 0)
6666         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6667
6668       TEST_DrawLevelField(x, y);
6669
6670       // uncrumble neighbour fields, if needed
6671       if (element == EL_INVISIBLE_SAND)
6672         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6673     }
6674     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6675              element == EL_INVISIBLE_WALL_ACTIVE ||
6676              element == EL_INVISIBLE_SAND_ACTIVE)
6677     {
6678       if (game.lenses_time_left == 0)
6679         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6680
6681       TEST_DrawLevelField(x, y);
6682
6683       // re-crumble neighbour fields, if needed
6684       if (element == EL_INVISIBLE_SAND)
6685         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6686     }
6687   }
6688 }
6689
6690 static void RedrawAllInvisibleElementsForMagnifier(void)
6691 {
6692   int x, y;
6693
6694   SCAN_PLAYFIELD(x, y)
6695   {
6696     int element = Tile[x][y];
6697
6698     if (element == EL_EMC_FAKE_GRASS &&
6699         game.magnify_time_left > 0)
6700     {
6701       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6702       TEST_DrawLevelField(x, y);
6703     }
6704     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6705              game.magnify_time_left == 0)
6706     {
6707       Tile[x][y] = EL_EMC_FAKE_GRASS;
6708       TEST_DrawLevelField(x, y);
6709     }
6710     else if (IS_GATE_GRAY(element) &&
6711              game.magnify_time_left > 0)
6712     {
6713       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6714                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6715                     IS_EM_GATE_GRAY(element) ?
6716                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6717                     IS_EMC_GATE_GRAY(element) ?
6718                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6719                     IS_DC_GATE_GRAY(element) ?
6720                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6721                     element);
6722       TEST_DrawLevelField(x, y);
6723     }
6724     else if (IS_GATE_GRAY_ACTIVE(element) &&
6725              game.magnify_time_left == 0)
6726     {
6727       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6728                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6729                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6730                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6731                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6732                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6733                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6734                     EL_DC_GATE_WHITE_GRAY :
6735                     element);
6736       TEST_DrawLevelField(x, y);
6737     }
6738   }
6739 }
6740
6741 static void ToggleLightSwitch(int x, int y)
6742 {
6743   int element = Tile[x][y];
6744
6745   game.light_time_left =
6746     (element == EL_LIGHT_SWITCH ?
6747      level.time_light * FRAMES_PER_SECOND : 0);
6748
6749   RedrawAllLightSwitchesAndInvisibleElements();
6750 }
6751
6752 static void ActivateTimegateSwitch(int x, int y)
6753 {
6754   int xx, yy;
6755
6756   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6757
6758   SCAN_PLAYFIELD(xx, yy)
6759   {
6760     int element = Tile[xx][yy];
6761
6762     if (element == EL_TIMEGATE_CLOSED ||
6763         element == EL_TIMEGATE_CLOSING)
6764     {
6765       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6766       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6767     }
6768
6769     /*
6770     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6771     {
6772       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6773       TEST_DrawLevelField(xx, yy);
6774     }
6775     */
6776
6777   }
6778
6779   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6780                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6781 }
6782
6783 static void Impact(int x, int y)
6784 {
6785   boolean last_line = (y == lev_fieldy - 1);
6786   boolean object_hit = FALSE;
6787   boolean impact = (last_line || object_hit);
6788   int element = Tile[x][y];
6789   int smashed = EL_STEELWALL;
6790
6791   if (!last_line)       // check if element below was hit
6792   {
6793     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6794       return;
6795
6796     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6797                                          MovDir[x][y + 1] != MV_DOWN ||
6798                                          MovPos[x][y + 1] <= TILEY / 2));
6799
6800     // do not smash moving elements that left the smashed field in time
6801     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6802         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6803       object_hit = FALSE;
6804
6805 #if USE_QUICKSAND_IMPACT_BUGFIX
6806     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6807     {
6808       RemoveMovingField(x, y + 1);
6809       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6810       Tile[x][y + 2] = EL_ROCK;
6811       TEST_DrawLevelField(x, y + 2);
6812
6813       object_hit = TRUE;
6814     }
6815
6816     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6817     {
6818       RemoveMovingField(x, y + 1);
6819       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6820       Tile[x][y + 2] = EL_ROCK;
6821       TEST_DrawLevelField(x, y + 2);
6822
6823       object_hit = TRUE;
6824     }
6825 #endif
6826
6827     if (object_hit)
6828       smashed = MovingOrBlocked2Element(x, y + 1);
6829
6830     impact = (last_line || object_hit);
6831   }
6832
6833   if (!last_line && smashed == EL_ACID) // element falls into acid
6834   {
6835     SplashAcid(x, y + 1);
6836     return;
6837   }
6838
6839   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6840   // only reset graphic animation if graphic really changes after impact
6841   if (impact &&
6842       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6843   {
6844     ResetGfxAnimation(x, y);
6845     TEST_DrawLevelField(x, y);
6846   }
6847
6848   if (impact && CAN_EXPLODE_IMPACT(element))
6849   {
6850     Bang(x, y);
6851     return;
6852   }
6853   else if (impact && element == EL_PEARL &&
6854            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6855   {
6856     ResetGfxAnimation(x, y);
6857
6858     Tile[x][y] = EL_PEARL_BREAKING;
6859     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6860     return;
6861   }
6862   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6863   {
6864     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6865
6866     return;
6867   }
6868
6869   if (impact && element == EL_AMOEBA_DROP)
6870   {
6871     if (object_hit && IS_PLAYER(x, y + 1))
6872       KillPlayerUnlessEnemyProtected(x, y + 1);
6873     else if (object_hit && smashed == EL_PENGUIN)
6874       Bang(x, y + 1);
6875     else
6876     {
6877       Tile[x][y] = EL_AMOEBA_GROWING;
6878       Store[x][y] = EL_AMOEBA_WET;
6879
6880       ResetRandomAnimationValue(x, y);
6881     }
6882     return;
6883   }
6884
6885   if (object_hit)               // check which object was hit
6886   {
6887     if ((CAN_PASS_MAGIC_WALL(element) && 
6888          (smashed == EL_MAGIC_WALL ||
6889           smashed == EL_BD_MAGIC_WALL)) ||
6890         (CAN_PASS_DC_MAGIC_WALL(element) &&
6891          smashed == EL_DC_MAGIC_WALL))
6892     {
6893       int xx, yy;
6894       int activated_magic_wall =
6895         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6896          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6897          EL_DC_MAGIC_WALL_ACTIVE);
6898
6899       // activate magic wall / mill
6900       SCAN_PLAYFIELD(xx, yy)
6901       {
6902         if (Tile[xx][yy] == smashed)
6903           Tile[xx][yy] = activated_magic_wall;
6904       }
6905
6906       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6907       game.magic_wall_active = TRUE;
6908
6909       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6910                             SND_MAGIC_WALL_ACTIVATING :
6911                             smashed == EL_BD_MAGIC_WALL ?
6912                             SND_BD_MAGIC_WALL_ACTIVATING :
6913                             SND_DC_MAGIC_WALL_ACTIVATING));
6914     }
6915
6916     if (IS_PLAYER(x, y + 1))
6917     {
6918       if (CAN_SMASH_PLAYER(element))
6919       {
6920         KillPlayerUnlessEnemyProtected(x, y + 1);
6921         return;
6922       }
6923     }
6924     else if (smashed == EL_PENGUIN)
6925     {
6926       if (CAN_SMASH_PLAYER(element))
6927       {
6928         Bang(x, y + 1);
6929         return;
6930       }
6931     }
6932     else if (element == EL_BD_DIAMOND)
6933     {
6934       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6935       {
6936         Bang(x, y + 1);
6937         return;
6938       }
6939     }
6940     else if (((element == EL_SP_INFOTRON ||
6941                element == EL_SP_ZONK) &&
6942               (smashed == EL_SP_SNIKSNAK ||
6943                smashed == EL_SP_ELECTRON ||
6944                smashed == EL_SP_DISK_ORANGE)) ||
6945              (element == EL_SP_INFOTRON &&
6946               smashed == EL_SP_DISK_YELLOW))
6947     {
6948       Bang(x, y + 1);
6949       return;
6950     }
6951     else if (CAN_SMASH_EVERYTHING(element))
6952     {
6953       if (IS_CLASSIC_ENEMY(smashed) ||
6954           CAN_EXPLODE_SMASHED(smashed))
6955       {
6956         Bang(x, y + 1);
6957         return;
6958       }
6959       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6960       {
6961         if (smashed == EL_LAMP ||
6962             smashed == EL_LAMP_ACTIVE)
6963         {
6964           Bang(x, y + 1);
6965           return;
6966         }
6967         else if (smashed == EL_NUT)
6968         {
6969           Tile[x][y + 1] = EL_NUT_BREAKING;
6970           PlayLevelSound(x, y, SND_NUT_BREAKING);
6971           RaiseScoreElement(EL_NUT);
6972           return;
6973         }
6974         else if (smashed == EL_PEARL)
6975         {
6976           ResetGfxAnimation(x, y);
6977
6978           Tile[x][y + 1] = EL_PEARL_BREAKING;
6979           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6980           return;
6981         }
6982         else if (smashed == EL_DIAMOND)
6983         {
6984           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6985           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6986           return;
6987         }
6988         else if (IS_BELT_SWITCH(smashed))
6989         {
6990           ToggleBeltSwitch(x, y + 1);
6991         }
6992         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6993                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6994                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6995                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6996         {
6997           ToggleSwitchgateSwitch();
6998         }
6999         else if (smashed == EL_LIGHT_SWITCH ||
7000                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7001         {
7002           ToggleLightSwitch(x, y + 1);
7003         }
7004         else
7005         {
7006           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7007
7008           CheckElementChangeBySide(x, y + 1, smashed, element,
7009                                    CE_SWITCHED, CH_SIDE_TOP);
7010           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7011                                             CH_SIDE_TOP);
7012         }
7013       }
7014       else
7015       {
7016         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7017       }
7018     }
7019   }
7020
7021   // play sound of magic wall / mill
7022   if (!last_line &&
7023       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7024        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7025        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7026   {
7027     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7028       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7029     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7030       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7031     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7032       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7033
7034     return;
7035   }
7036
7037   // play sound of object that hits the ground
7038   if (last_line || object_hit)
7039     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7040 }
7041
7042 static void TurnRoundExt(int x, int y)
7043 {
7044   static struct
7045   {
7046     int dx, dy;
7047   } move_xy[] =
7048   {
7049     {  0,  0 },
7050     { -1,  0 },
7051     { +1,  0 },
7052     {  0,  0 },
7053     {  0, -1 },
7054     {  0,  0 }, { 0, 0 }, { 0, 0 },
7055     {  0, +1 }
7056   };
7057   static struct
7058   {
7059     int left, right, back;
7060   } turn[] =
7061   {
7062     { 0,        0,              0        },
7063     { MV_DOWN,  MV_UP,          MV_RIGHT },
7064     { MV_UP,    MV_DOWN,        MV_LEFT  },
7065     { 0,        0,              0        },
7066     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7067     { 0,        0,              0        },
7068     { 0,        0,              0        },
7069     { 0,        0,              0        },
7070     { MV_RIGHT, MV_LEFT,        MV_UP    }
7071   };
7072
7073   int element = Tile[x][y];
7074   int move_pattern = element_info[element].move_pattern;
7075
7076   int old_move_dir = MovDir[x][y];
7077   int left_dir  = turn[old_move_dir].left;
7078   int right_dir = turn[old_move_dir].right;
7079   int back_dir  = turn[old_move_dir].back;
7080
7081   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7082   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7083   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7084   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7085
7086   int left_x  = x + left_dx,  left_y  = y + left_dy;
7087   int right_x = x + right_dx, right_y = y + right_dy;
7088   int move_x  = x + move_dx,  move_y  = y + move_dy;
7089
7090   int xx, yy;
7091
7092   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7093   {
7094     TestIfBadThingTouchesOtherBadThing(x, y);
7095
7096     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7097       MovDir[x][y] = right_dir;
7098     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7099       MovDir[x][y] = left_dir;
7100
7101     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7102       MovDelay[x][y] = 9;
7103     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7104       MovDelay[x][y] = 1;
7105   }
7106   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7107   {
7108     TestIfBadThingTouchesOtherBadThing(x, y);
7109
7110     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7111       MovDir[x][y] = left_dir;
7112     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7113       MovDir[x][y] = right_dir;
7114
7115     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7116       MovDelay[x][y] = 9;
7117     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7118       MovDelay[x][y] = 1;
7119   }
7120   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7121   {
7122     TestIfBadThingTouchesOtherBadThing(x, y);
7123
7124     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7125       MovDir[x][y] = left_dir;
7126     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7127       MovDir[x][y] = right_dir;
7128
7129     if (MovDir[x][y] != old_move_dir)
7130       MovDelay[x][y] = 9;
7131   }
7132   else if (element == EL_YAMYAM)
7133   {
7134     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7135     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7136
7137     if (can_turn_left && can_turn_right)
7138       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7139     else if (can_turn_left)
7140       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7141     else if (can_turn_right)
7142       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7143     else
7144       MovDir[x][y] = back_dir;
7145
7146     MovDelay[x][y] = 16 + 16 * RND(3);
7147   }
7148   else if (element == EL_DARK_YAMYAM)
7149   {
7150     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7151                                                          left_x, left_y);
7152     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7153                                                          right_x, right_y);
7154
7155     if (can_turn_left && can_turn_right)
7156       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7157     else if (can_turn_left)
7158       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7159     else if (can_turn_right)
7160       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7161     else
7162       MovDir[x][y] = back_dir;
7163
7164     MovDelay[x][y] = 16 + 16 * RND(3);
7165   }
7166   else if (element == EL_PACMAN)
7167   {
7168     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7169     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7170
7171     if (can_turn_left && can_turn_right)
7172       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7173     else if (can_turn_left)
7174       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7175     else if (can_turn_right)
7176       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7177     else
7178       MovDir[x][y] = back_dir;
7179
7180     MovDelay[x][y] = 6 + RND(40);
7181   }
7182   else if (element == EL_PIG)
7183   {
7184     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7185     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7186     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7187     boolean should_turn_left, should_turn_right, should_move_on;
7188     int rnd_value = 24;
7189     int rnd = RND(rnd_value);
7190
7191     should_turn_left = (can_turn_left &&
7192                         (!can_move_on ||
7193                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7194                                                    y + back_dy + left_dy)));
7195     should_turn_right = (can_turn_right &&
7196                          (!can_move_on ||
7197                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7198                                                     y + back_dy + right_dy)));
7199     should_move_on = (can_move_on &&
7200                       (!can_turn_left ||
7201                        !can_turn_right ||
7202                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7203                                                  y + move_dy + left_dy) ||
7204                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7205                                                  y + move_dy + right_dy)));
7206
7207     if (should_turn_left || should_turn_right || should_move_on)
7208     {
7209       if (should_turn_left && should_turn_right && should_move_on)
7210         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7211                         rnd < 2 * rnd_value / 3 ? right_dir :
7212                         old_move_dir);
7213       else if (should_turn_left && should_turn_right)
7214         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7215       else if (should_turn_left && should_move_on)
7216         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7217       else if (should_turn_right && should_move_on)
7218         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7219       else if (should_turn_left)
7220         MovDir[x][y] = left_dir;
7221       else if (should_turn_right)
7222         MovDir[x][y] = right_dir;
7223       else if (should_move_on)
7224         MovDir[x][y] = old_move_dir;
7225     }
7226     else if (can_move_on && rnd > rnd_value / 8)
7227       MovDir[x][y] = old_move_dir;
7228     else if (can_turn_left && can_turn_right)
7229       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7230     else if (can_turn_left && rnd > rnd_value / 8)
7231       MovDir[x][y] = left_dir;
7232     else if (can_turn_right && rnd > rnd_value/8)
7233       MovDir[x][y] = right_dir;
7234     else
7235       MovDir[x][y] = back_dir;
7236
7237     xx = x + move_xy[MovDir[x][y]].dx;
7238     yy = y + move_xy[MovDir[x][y]].dy;
7239
7240     if (!IN_LEV_FIELD(xx, yy) ||
7241         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7242       MovDir[x][y] = old_move_dir;
7243
7244     MovDelay[x][y] = 0;
7245   }
7246   else if (element == EL_DRAGON)
7247   {
7248     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7249     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7250     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7251     int rnd_value = 24;
7252     int rnd = RND(rnd_value);
7253
7254     if (can_move_on && rnd > rnd_value / 8)
7255       MovDir[x][y] = old_move_dir;
7256     else if (can_turn_left && can_turn_right)
7257       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7258     else if (can_turn_left && rnd > rnd_value / 8)
7259       MovDir[x][y] = left_dir;
7260     else if (can_turn_right && rnd > rnd_value / 8)
7261       MovDir[x][y] = right_dir;
7262     else
7263       MovDir[x][y] = back_dir;
7264
7265     xx = x + move_xy[MovDir[x][y]].dx;
7266     yy = y + move_xy[MovDir[x][y]].dy;
7267
7268     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7269       MovDir[x][y] = old_move_dir;
7270
7271     MovDelay[x][y] = 0;
7272   }
7273   else if (element == EL_MOLE)
7274   {
7275     boolean can_move_on =
7276       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7277                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7278                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7279     if (!can_move_on)
7280     {
7281       boolean can_turn_left =
7282         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7283                               IS_AMOEBOID(Tile[left_x][left_y])));
7284
7285       boolean can_turn_right =
7286         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7287                               IS_AMOEBOID(Tile[right_x][right_y])));
7288
7289       if (can_turn_left && can_turn_right)
7290         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7291       else if (can_turn_left)
7292         MovDir[x][y] = left_dir;
7293       else
7294         MovDir[x][y] = right_dir;
7295     }
7296
7297     if (MovDir[x][y] != old_move_dir)
7298       MovDelay[x][y] = 9;
7299   }
7300   else if (element == EL_BALLOON)
7301   {
7302     MovDir[x][y] = game.wind_direction;
7303     MovDelay[x][y] = 0;
7304   }
7305   else if (element == EL_SPRING)
7306   {
7307     if (MovDir[x][y] & MV_HORIZONTAL)
7308     {
7309       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7310           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7311       {
7312         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7313         ResetGfxAnimation(move_x, move_y);
7314         TEST_DrawLevelField(move_x, move_y);
7315
7316         MovDir[x][y] = back_dir;
7317       }
7318       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7319                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7320         MovDir[x][y] = MV_NONE;
7321     }
7322
7323     MovDelay[x][y] = 0;
7324   }
7325   else if (element == EL_ROBOT ||
7326            element == EL_SATELLITE ||
7327            element == EL_PENGUIN ||
7328            element == EL_EMC_ANDROID)
7329   {
7330     int attr_x = -1, attr_y = -1;
7331
7332     if (game.all_players_gone)
7333     {
7334       attr_x = game.exit_x;
7335       attr_y = game.exit_y;
7336     }
7337     else
7338     {
7339       int i;
7340
7341       for (i = 0; i < MAX_PLAYERS; i++)
7342       {
7343         struct PlayerInfo *player = &stored_player[i];
7344         int jx = player->jx, jy = player->jy;
7345
7346         if (!player->active)
7347           continue;
7348
7349         if (attr_x == -1 ||
7350             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7351         {
7352           attr_x = jx;
7353           attr_y = jy;
7354         }
7355       }
7356     }
7357
7358     if (element == EL_ROBOT &&
7359         game.robot_wheel_x >= 0 &&
7360         game.robot_wheel_y >= 0 &&
7361         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7362          game.engine_version < VERSION_IDENT(3,1,0,0)))
7363     {
7364       attr_x = game.robot_wheel_x;
7365       attr_y = game.robot_wheel_y;
7366     }
7367
7368     if (element == EL_PENGUIN)
7369     {
7370       int i;
7371       struct XY *xy = xy_topdown;
7372
7373       for (i = 0; i < NUM_DIRECTIONS; i++)
7374       {
7375         int ex = x + xy[i].x;
7376         int ey = y + xy[i].y;
7377
7378         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7379                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7380                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7381                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7382         {
7383           attr_x = ex;
7384           attr_y = ey;
7385           break;
7386         }
7387       }
7388     }
7389
7390     MovDir[x][y] = MV_NONE;
7391     if (attr_x < x)
7392       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7393     else if (attr_x > x)
7394       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7395     if (attr_y < y)
7396       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7397     else if (attr_y > y)
7398       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7399
7400     if (element == EL_ROBOT)
7401     {
7402       int newx, newy;
7403
7404       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7405         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7406       Moving2Blocked(x, y, &newx, &newy);
7407
7408       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7409         MovDelay[x][y] = 8 + 8 * !RND(3);
7410       else
7411         MovDelay[x][y] = 16;
7412     }
7413     else if (element == EL_PENGUIN)
7414     {
7415       int newx, newy;
7416
7417       MovDelay[x][y] = 1;
7418
7419       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7420       {
7421         boolean first_horiz = RND(2);
7422         int new_move_dir = MovDir[x][y];
7423
7424         MovDir[x][y] =
7425           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7426         Moving2Blocked(x, y, &newx, &newy);
7427
7428         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7429           return;
7430
7431         MovDir[x][y] =
7432           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7433         Moving2Blocked(x, y, &newx, &newy);
7434
7435         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7436           return;
7437
7438         MovDir[x][y] = old_move_dir;
7439         return;
7440       }
7441     }
7442     else if (element == EL_SATELLITE)
7443     {
7444       int newx, newy;
7445
7446       MovDelay[x][y] = 1;
7447
7448       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7449       {
7450         boolean first_horiz = RND(2);
7451         int new_move_dir = MovDir[x][y];
7452
7453         MovDir[x][y] =
7454           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7455         Moving2Blocked(x, y, &newx, &newy);
7456
7457         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7458           return;
7459
7460         MovDir[x][y] =
7461           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7462         Moving2Blocked(x, y, &newx, &newy);
7463
7464         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7465           return;
7466
7467         MovDir[x][y] = old_move_dir;
7468         return;
7469       }
7470     }
7471     else if (element == EL_EMC_ANDROID)
7472     {
7473       static int check_pos[16] =
7474       {
7475         -1,             //  0 => (invalid)
7476         7,              //  1 => MV_LEFT
7477         3,              //  2 => MV_RIGHT
7478         -1,             //  3 => (invalid)
7479         1,              //  4 =>            MV_UP
7480         0,              //  5 => MV_LEFT  | MV_UP
7481         2,              //  6 => MV_RIGHT | MV_UP
7482         -1,             //  7 => (invalid)
7483         5,              //  8 =>            MV_DOWN
7484         6,              //  9 => MV_LEFT  | MV_DOWN
7485         4,              // 10 => MV_RIGHT | MV_DOWN
7486         -1,             // 11 => (invalid)
7487         -1,             // 12 => (invalid)
7488         -1,             // 13 => (invalid)
7489         -1,             // 14 => (invalid)
7490         -1,             // 15 => (invalid)
7491       };
7492       static struct
7493       {
7494         int dx, dy;
7495         int dir;
7496       } check_xy[8] =
7497       {
7498         { -1, -1,       MV_LEFT  | MV_UP   },
7499         {  0, -1,                  MV_UP   },
7500         { +1, -1,       MV_RIGHT | MV_UP   },
7501         { +1,  0,       MV_RIGHT           },
7502         { +1, +1,       MV_RIGHT | MV_DOWN },
7503         {  0, +1,                  MV_DOWN },
7504         { -1, +1,       MV_LEFT  | MV_DOWN },
7505         { -1,  0,       MV_LEFT            },
7506       };
7507       int start_pos, check_order;
7508       boolean can_clone = FALSE;
7509       int i;
7510
7511       // check if there is any free field around current position
7512       for (i = 0; i < 8; i++)
7513       {
7514         int newx = x + check_xy[i].dx;
7515         int newy = y + check_xy[i].dy;
7516
7517         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7518         {
7519           can_clone = TRUE;
7520
7521           break;
7522         }
7523       }
7524
7525       if (can_clone)            // randomly find an element to clone
7526       {
7527         can_clone = FALSE;
7528
7529         start_pos = check_pos[RND(8)];
7530         check_order = (RND(2) ? -1 : +1);
7531
7532         for (i = 0; i < 8; i++)
7533         {
7534           int pos_raw = start_pos + i * check_order;
7535           int pos = (pos_raw + 8) % 8;
7536           int newx = x + check_xy[pos].dx;
7537           int newy = y + check_xy[pos].dy;
7538
7539           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7540           {
7541             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7542             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7543
7544             Store[x][y] = Tile[newx][newy];
7545
7546             can_clone = TRUE;
7547
7548             break;
7549           }
7550         }
7551       }
7552
7553       if (can_clone)            // randomly find a direction to move
7554       {
7555         can_clone = FALSE;
7556
7557         start_pos = check_pos[RND(8)];
7558         check_order = (RND(2) ? -1 : +1);
7559
7560         for (i = 0; i < 8; i++)
7561         {
7562           int pos_raw = start_pos + i * check_order;
7563           int pos = (pos_raw + 8) % 8;
7564           int newx = x + check_xy[pos].dx;
7565           int newy = y + check_xy[pos].dy;
7566           int new_move_dir = check_xy[pos].dir;
7567
7568           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7569           {
7570             MovDir[x][y] = new_move_dir;
7571             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7572
7573             can_clone = TRUE;
7574
7575             break;
7576           }
7577         }
7578       }
7579
7580       if (can_clone)            // cloning and moving successful
7581         return;
7582
7583       // cannot clone -- try to move towards player
7584
7585       start_pos = check_pos[MovDir[x][y] & 0x0f];
7586       check_order = (RND(2) ? -1 : +1);
7587
7588       for (i = 0; i < 3; i++)
7589       {
7590         // first check start_pos, then previous/next or (next/previous) pos
7591         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7592         int pos = (pos_raw + 8) % 8;
7593         int newx = x + check_xy[pos].dx;
7594         int newy = y + check_xy[pos].dy;
7595         int new_move_dir = check_xy[pos].dir;
7596
7597         if (IS_PLAYER(newx, newy))
7598           break;
7599
7600         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7601         {
7602           MovDir[x][y] = new_move_dir;
7603           MovDelay[x][y] = level.android_move_time * 8 + 1;
7604
7605           break;
7606         }
7607       }
7608     }
7609   }
7610   else if (move_pattern == MV_TURNING_LEFT ||
7611            move_pattern == MV_TURNING_RIGHT ||
7612            move_pattern == MV_TURNING_LEFT_RIGHT ||
7613            move_pattern == MV_TURNING_RIGHT_LEFT ||
7614            move_pattern == MV_TURNING_RANDOM ||
7615            move_pattern == MV_ALL_DIRECTIONS)
7616   {
7617     boolean can_turn_left =
7618       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7619     boolean can_turn_right =
7620       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7621
7622     if (element_info[element].move_stepsize == 0)       // "not moving"
7623       return;
7624
7625     if (move_pattern == MV_TURNING_LEFT)
7626       MovDir[x][y] = left_dir;
7627     else if (move_pattern == MV_TURNING_RIGHT)
7628       MovDir[x][y] = right_dir;
7629     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7630       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7631     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7632       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7633     else if (move_pattern == MV_TURNING_RANDOM)
7634       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7635                       can_turn_right && !can_turn_left ? right_dir :
7636                       RND(2) ? left_dir : right_dir);
7637     else if (can_turn_left && can_turn_right)
7638       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7639     else if (can_turn_left)
7640       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7641     else if (can_turn_right)
7642       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7643     else
7644       MovDir[x][y] = back_dir;
7645
7646     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7647   }
7648   else if (move_pattern == MV_HORIZONTAL ||
7649            move_pattern == MV_VERTICAL)
7650   {
7651     if (move_pattern & old_move_dir)
7652       MovDir[x][y] = back_dir;
7653     else if (move_pattern == MV_HORIZONTAL)
7654       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7655     else if (move_pattern == MV_VERTICAL)
7656       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7657
7658     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7659   }
7660   else if (move_pattern & MV_ANY_DIRECTION)
7661   {
7662     MovDir[x][y] = move_pattern;
7663     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7664   }
7665   else if (move_pattern & MV_WIND_DIRECTION)
7666   {
7667     MovDir[x][y] = game.wind_direction;
7668     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7669   }
7670   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7671   {
7672     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7673       MovDir[x][y] = left_dir;
7674     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7675       MovDir[x][y] = right_dir;
7676
7677     if (MovDir[x][y] != old_move_dir)
7678       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7679   }
7680   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7681   {
7682     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7683       MovDir[x][y] = right_dir;
7684     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7685       MovDir[x][y] = left_dir;
7686
7687     if (MovDir[x][y] != old_move_dir)
7688       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7689   }
7690   else if (move_pattern == MV_TOWARDS_PLAYER ||
7691            move_pattern == MV_AWAY_FROM_PLAYER)
7692   {
7693     int attr_x = -1, attr_y = -1;
7694     int newx, newy;
7695     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7696
7697     if (game.all_players_gone)
7698     {
7699       attr_x = game.exit_x;
7700       attr_y = game.exit_y;
7701     }
7702     else
7703     {
7704       int i;
7705
7706       for (i = 0; i < MAX_PLAYERS; i++)
7707       {
7708         struct PlayerInfo *player = &stored_player[i];
7709         int jx = player->jx, jy = player->jy;
7710
7711         if (!player->active)
7712           continue;
7713
7714         if (attr_x == -1 ||
7715             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7716         {
7717           attr_x = jx;
7718           attr_y = jy;
7719         }
7720       }
7721     }
7722
7723     MovDir[x][y] = MV_NONE;
7724     if (attr_x < x)
7725       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7726     else if (attr_x > x)
7727       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7728     if (attr_y < y)
7729       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7730     else if (attr_y > y)
7731       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7732
7733     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7734
7735     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7736     {
7737       boolean first_horiz = RND(2);
7738       int new_move_dir = MovDir[x][y];
7739
7740       if (element_info[element].move_stepsize == 0)     // "not moving"
7741       {
7742         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7743         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744
7745         return;
7746       }
7747
7748       MovDir[x][y] =
7749         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7750       Moving2Blocked(x, y, &newx, &newy);
7751
7752       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7753         return;
7754
7755       MovDir[x][y] =
7756         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7757       Moving2Blocked(x, y, &newx, &newy);
7758
7759       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7760         return;
7761
7762       MovDir[x][y] = old_move_dir;
7763     }
7764   }
7765   else if (move_pattern == MV_WHEN_PUSHED ||
7766            move_pattern == MV_WHEN_DROPPED)
7767   {
7768     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7769       MovDir[x][y] = MV_NONE;
7770
7771     MovDelay[x][y] = 0;
7772   }
7773   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7774   {
7775     struct XY *test_xy = xy_topdown;
7776     static int test_dir[4] =
7777     {
7778       MV_UP,
7779       MV_LEFT,
7780       MV_RIGHT,
7781       MV_DOWN
7782     };
7783     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7784     int move_preference = -1000000;     // start with very low preference
7785     int new_move_dir = MV_NONE;
7786     int start_test = RND(4);
7787     int i;
7788
7789     for (i = 0; i < NUM_DIRECTIONS; i++)
7790     {
7791       int j = (start_test + i) % 4;
7792       int move_dir = test_dir[j];
7793       int move_dir_preference;
7794
7795       xx = x + test_xy[j].x;
7796       yy = y + test_xy[j].y;
7797
7798       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7799           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7800       {
7801         new_move_dir = move_dir;
7802
7803         break;
7804       }
7805
7806       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7807         continue;
7808
7809       move_dir_preference = -1 * RunnerVisit[xx][yy];
7810       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7811         move_dir_preference = PlayerVisit[xx][yy];
7812
7813       if (move_dir_preference > move_preference)
7814       {
7815         // prefer field that has not been visited for the longest time
7816         move_preference = move_dir_preference;
7817         new_move_dir = move_dir;
7818       }
7819       else if (move_dir_preference == move_preference &&
7820                move_dir == old_move_dir)
7821       {
7822         // prefer last direction when all directions are preferred equally
7823         move_preference = move_dir_preference;
7824         new_move_dir = move_dir;
7825       }
7826     }
7827
7828     MovDir[x][y] = new_move_dir;
7829     if (old_move_dir != new_move_dir)
7830       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7831   }
7832 }
7833
7834 static void TurnRound(int x, int y)
7835 {
7836   int direction = MovDir[x][y];
7837
7838   TurnRoundExt(x, y);
7839
7840   GfxDir[x][y] = MovDir[x][y];
7841
7842   if (direction != MovDir[x][y])
7843     GfxFrame[x][y] = 0;
7844
7845   if (MovDelay[x][y])
7846     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7847
7848   ResetGfxFrame(x, y);
7849 }
7850
7851 static boolean JustBeingPushed(int x, int y)
7852 {
7853   int i;
7854
7855   for (i = 0; i < MAX_PLAYERS; i++)
7856   {
7857     struct PlayerInfo *player = &stored_player[i];
7858
7859     if (player->active && player->is_pushing && player->MovPos)
7860     {
7861       int next_jx = player->jx + (player->jx - player->last_jx);
7862       int next_jy = player->jy + (player->jy - player->last_jy);
7863
7864       if (x == next_jx && y == next_jy)
7865         return TRUE;
7866     }
7867   }
7868
7869   return FALSE;
7870 }
7871
7872 static void StartMoving(int x, int y)
7873 {
7874   boolean started_moving = FALSE;       // some elements can fall _and_ move
7875   int element = Tile[x][y];
7876
7877   if (Stop[x][y])
7878     return;
7879
7880   if (MovDelay[x][y] == 0)
7881     GfxAction[x][y] = ACTION_DEFAULT;
7882
7883   if (CAN_FALL(element) && y < lev_fieldy - 1)
7884   {
7885     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7886         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7887       if (JustBeingPushed(x, y))
7888         return;
7889
7890     if (element == EL_QUICKSAND_FULL)
7891     {
7892       if (IS_FREE(x, y + 1))
7893       {
7894         InitMovingField(x, y, MV_DOWN);
7895         started_moving = TRUE;
7896
7897         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7898 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7899         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7900           Store[x][y] = EL_ROCK;
7901 #else
7902         Store[x][y] = EL_ROCK;
7903 #endif
7904
7905         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7906       }
7907       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7908       {
7909         if (!MovDelay[x][y])
7910         {
7911           MovDelay[x][y] = TILEY + 1;
7912
7913           ResetGfxAnimation(x, y);
7914           ResetGfxAnimation(x, y + 1);
7915         }
7916
7917         if (MovDelay[x][y])
7918         {
7919           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7920           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7921
7922           MovDelay[x][y]--;
7923           if (MovDelay[x][y])
7924             return;
7925         }
7926
7927         Tile[x][y] = EL_QUICKSAND_EMPTY;
7928         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7929         Store[x][y + 1] = Store[x][y];
7930         Store[x][y] = 0;
7931
7932         PlayLevelSoundAction(x, y, ACTION_FILLING);
7933       }
7934       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7935       {
7936         if (!MovDelay[x][y])
7937         {
7938           MovDelay[x][y] = TILEY + 1;
7939
7940           ResetGfxAnimation(x, y);
7941           ResetGfxAnimation(x, y + 1);
7942         }
7943
7944         if (MovDelay[x][y])
7945         {
7946           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7947           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7948
7949           MovDelay[x][y]--;
7950           if (MovDelay[x][y])
7951             return;
7952         }
7953
7954         Tile[x][y] = EL_QUICKSAND_EMPTY;
7955         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7956         Store[x][y + 1] = Store[x][y];
7957         Store[x][y] = 0;
7958
7959         PlayLevelSoundAction(x, y, ACTION_FILLING);
7960       }
7961     }
7962     else if (element == EL_QUICKSAND_FAST_FULL)
7963     {
7964       if (IS_FREE(x, y + 1))
7965       {
7966         InitMovingField(x, y, MV_DOWN);
7967         started_moving = TRUE;
7968
7969         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7970 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7971         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7972           Store[x][y] = EL_ROCK;
7973 #else
7974         Store[x][y] = EL_ROCK;
7975 #endif
7976
7977         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7978       }
7979       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7980       {
7981         if (!MovDelay[x][y])
7982         {
7983           MovDelay[x][y] = TILEY + 1;
7984
7985           ResetGfxAnimation(x, y);
7986           ResetGfxAnimation(x, y + 1);
7987         }
7988
7989         if (MovDelay[x][y])
7990         {
7991           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7992           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7993
7994           MovDelay[x][y]--;
7995           if (MovDelay[x][y])
7996             return;
7997         }
7998
7999         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8000         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8001         Store[x][y + 1] = Store[x][y];
8002         Store[x][y] = 0;
8003
8004         PlayLevelSoundAction(x, y, ACTION_FILLING);
8005       }
8006       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8007       {
8008         if (!MovDelay[x][y])
8009         {
8010           MovDelay[x][y] = TILEY + 1;
8011
8012           ResetGfxAnimation(x, y);
8013           ResetGfxAnimation(x, y + 1);
8014         }
8015
8016         if (MovDelay[x][y])
8017         {
8018           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8019           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8020
8021           MovDelay[x][y]--;
8022           if (MovDelay[x][y])
8023             return;
8024         }
8025
8026         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8027         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8028         Store[x][y + 1] = Store[x][y];
8029         Store[x][y] = 0;
8030
8031         PlayLevelSoundAction(x, y, ACTION_FILLING);
8032       }
8033     }
8034     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8035              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8036     {
8037       InitMovingField(x, y, MV_DOWN);
8038       started_moving = TRUE;
8039
8040       Tile[x][y] = EL_QUICKSAND_FILLING;
8041       Store[x][y] = element;
8042
8043       PlayLevelSoundAction(x, y, ACTION_FILLING);
8044     }
8045     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8046              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8047     {
8048       InitMovingField(x, y, MV_DOWN);
8049       started_moving = TRUE;
8050
8051       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8052       Store[x][y] = element;
8053
8054       PlayLevelSoundAction(x, y, ACTION_FILLING);
8055     }
8056     else if (element == EL_MAGIC_WALL_FULL)
8057     {
8058       if (IS_FREE(x, y + 1))
8059       {
8060         InitMovingField(x, y, MV_DOWN);
8061         started_moving = TRUE;
8062
8063         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8064         Store[x][y] = EL_CHANGED(Store[x][y]);
8065       }
8066       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8067       {
8068         if (!MovDelay[x][y])
8069           MovDelay[x][y] = TILEY / 4 + 1;
8070
8071         if (MovDelay[x][y])
8072         {
8073           MovDelay[x][y]--;
8074           if (MovDelay[x][y])
8075             return;
8076         }
8077
8078         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8079         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8080         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8081         Store[x][y] = 0;
8082       }
8083     }
8084     else if (element == EL_BD_MAGIC_WALL_FULL)
8085     {
8086       if (IS_FREE(x, y + 1))
8087       {
8088         InitMovingField(x, y, MV_DOWN);
8089         started_moving = TRUE;
8090
8091         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8092         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8093       }
8094       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8095       {
8096         if (!MovDelay[x][y])
8097           MovDelay[x][y] = TILEY / 4 + 1;
8098
8099         if (MovDelay[x][y])
8100         {
8101           MovDelay[x][y]--;
8102           if (MovDelay[x][y])
8103             return;
8104         }
8105
8106         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8107         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8108         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8109         Store[x][y] = 0;
8110       }
8111     }
8112     else if (element == EL_DC_MAGIC_WALL_FULL)
8113     {
8114       if (IS_FREE(x, y + 1))
8115       {
8116         InitMovingField(x, y, MV_DOWN);
8117         started_moving = TRUE;
8118
8119         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8120         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8121       }
8122       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8123       {
8124         if (!MovDelay[x][y])
8125           MovDelay[x][y] = TILEY / 4 + 1;
8126
8127         if (MovDelay[x][y])
8128         {
8129           MovDelay[x][y]--;
8130           if (MovDelay[x][y])
8131             return;
8132         }
8133
8134         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8135         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8136         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8137         Store[x][y] = 0;
8138       }
8139     }
8140     else if ((CAN_PASS_MAGIC_WALL(element) &&
8141               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8142                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8143              (CAN_PASS_DC_MAGIC_WALL(element) &&
8144               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8145
8146     {
8147       InitMovingField(x, y, MV_DOWN);
8148       started_moving = TRUE;
8149
8150       Tile[x][y] =
8151         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8152          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8153          EL_DC_MAGIC_WALL_FILLING);
8154       Store[x][y] = element;
8155     }
8156     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8157     {
8158       SplashAcid(x, y + 1);
8159
8160       InitMovingField(x, y, MV_DOWN);
8161       started_moving = TRUE;
8162
8163       Store[x][y] = EL_ACID;
8164     }
8165     else if (
8166              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8167               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8168              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8169               CAN_FALL(element) && WasJustFalling[x][y] &&
8170               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8171
8172              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8173               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8174               (Tile[x][y + 1] == EL_BLOCKED)))
8175     {
8176       /* this is needed for a special case not covered by calling "Impact()"
8177          from "ContinueMoving()": if an element moves to a tile directly below
8178          another element which was just falling on that tile (which was empty
8179          in the previous frame), the falling element above would just stop
8180          instead of smashing the element below (in previous version, the above
8181          element was just checked for "moving" instead of "falling", resulting
8182          in incorrect smashes caused by horizontal movement of the above
8183          element; also, the case of the player being the element to smash was
8184          simply not covered here... :-/ ) */
8185
8186       CheckCollision[x][y] = 0;
8187       CheckImpact[x][y] = 0;
8188
8189       Impact(x, y);
8190     }
8191     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8192     {
8193       if (MovDir[x][y] == MV_NONE)
8194       {
8195         InitMovingField(x, y, MV_DOWN);
8196         started_moving = TRUE;
8197       }
8198     }
8199     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8200     {
8201       if (WasJustFalling[x][y]) // prevent animation from being restarted
8202         MovDir[x][y] = MV_DOWN;
8203
8204       InitMovingField(x, y, MV_DOWN);
8205       started_moving = TRUE;
8206     }
8207     else if (element == EL_AMOEBA_DROP)
8208     {
8209       Tile[x][y] = EL_AMOEBA_GROWING;
8210       Store[x][y] = EL_AMOEBA_WET;
8211     }
8212     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8213               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8214              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8215              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8216     {
8217       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8218                                 (IS_FREE(x - 1, y + 1) ||
8219                                  Tile[x - 1][y + 1] == EL_ACID));
8220       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8221                                 (IS_FREE(x + 1, y + 1) ||
8222                                  Tile[x + 1][y + 1] == EL_ACID));
8223       boolean can_fall_any  = (can_fall_left || can_fall_right);
8224       boolean can_fall_both = (can_fall_left && can_fall_right);
8225       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8226
8227       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8228       {
8229         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8230           can_fall_right = FALSE;
8231         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8232           can_fall_left = FALSE;
8233         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8234           can_fall_right = FALSE;
8235         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8236           can_fall_left = FALSE;
8237
8238         can_fall_any  = (can_fall_left || can_fall_right);
8239         can_fall_both = FALSE;
8240       }
8241
8242       if (can_fall_both)
8243       {
8244         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8245           can_fall_right = FALSE;       // slip down on left side
8246         else
8247           can_fall_left = !(can_fall_right = RND(2));
8248
8249         can_fall_both = FALSE;
8250       }
8251
8252       if (can_fall_any)
8253       {
8254         // if not determined otherwise, prefer left side for slipping down
8255         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8256         started_moving = TRUE;
8257       }
8258     }
8259     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8260     {
8261       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8262       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8263       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8264       int belt_dir = game.belt_dir[belt_nr];
8265
8266       if ((belt_dir == MV_LEFT  && left_is_free) ||
8267           (belt_dir == MV_RIGHT && right_is_free))
8268       {
8269         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8270
8271         InitMovingField(x, y, belt_dir);
8272         started_moving = TRUE;
8273
8274         Pushed[x][y] = TRUE;
8275         Pushed[nextx][y] = TRUE;
8276
8277         GfxAction[x][y] = ACTION_DEFAULT;
8278       }
8279       else
8280       {
8281         MovDir[x][y] = 0;       // if element was moving, stop it
8282       }
8283     }
8284   }
8285
8286   // not "else if" because of elements that can fall and move (EL_SPRING)
8287   if (CAN_MOVE(element) && !started_moving)
8288   {
8289     int move_pattern = element_info[element].move_pattern;
8290     int newx, newy;
8291
8292     Moving2Blocked(x, y, &newx, &newy);
8293
8294     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8295       return;
8296
8297     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8298         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8299     {
8300       WasJustMoving[x][y] = 0;
8301       CheckCollision[x][y] = 0;
8302
8303       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8304
8305       if (Tile[x][y] != element)        // element has changed
8306         return;
8307     }
8308
8309     if (!MovDelay[x][y])        // start new movement phase
8310     {
8311       // all objects that can change their move direction after each step
8312       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8313
8314       if (element != EL_YAMYAM &&
8315           element != EL_DARK_YAMYAM &&
8316           element != EL_PACMAN &&
8317           !(move_pattern & MV_ANY_DIRECTION) &&
8318           move_pattern != MV_TURNING_LEFT &&
8319           move_pattern != MV_TURNING_RIGHT &&
8320           move_pattern != MV_TURNING_LEFT_RIGHT &&
8321           move_pattern != MV_TURNING_RIGHT_LEFT &&
8322           move_pattern != MV_TURNING_RANDOM)
8323       {
8324         TurnRound(x, y);
8325
8326         if (MovDelay[x][y] && (element == EL_BUG ||
8327                                element == EL_SPACESHIP ||
8328                                element == EL_SP_SNIKSNAK ||
8329                                element == EL_SP_ELECTRON ||
8330                                element == EL_MOLE))
8331           TEST_DrawLevelField(x, y);
8332       }
8333     }
8334
8335     if (MovDelay[x][y])         // wait some time before next movement
8336     {
8337       MovDelay[x][y]--;
8338
8339       if (element == EL_ROBOT ||
8340           element == EL_YAMYAM ||
8341           element == EL_DARK_YAMYAM)
8342       {
8343         DrawLevelElementAnimationIfNeeded(x, y, element);
8344         PlayLevelSoundAction(x, y, ACTION_WAITING);
8345       }
8346       else if (element == EL_SP_ELECTRON)
8347         DrawLevelElementAnimationIfNeeded(x, y, element);
8348       else if (element == EL_DRAGON)
8349       {
8350         int i;
8351         int dir = MovDir[x][y];
8352         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8353         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8354         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8355                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8356                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8357                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8358         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8359
8360         GfxAction[x][y] = ACTION_ATTACKING;
8361
8362         if (IS_PLAYER(x, y))
8363           DrawPlayerField(x, y);
8364         else
8365           TEST_DrawLevelField(x, y);
8366
8367         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8368
8369         for (i = 1; i <= 3; i++)
8370         {
8371           int xx = x + i * dx;
8372           int yy = y + i * dy;
8373           int sx = SCREENX(xx);
8374           int sy = SCREENY(yy);
8375           int flame_graphic = graphic + (i - 1);
8376
8377           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8378             break;
8379
8380           if (MovDelay[x][y])
8381           {
8382             int flamed = MovingOrBlocked2Element(xx, yy);
8383
8384             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8385               Bang(xx, yy);
8386             else
8387               RemoveMovingField(xx, yy);
8388
8389             ChangeDelay[xx][yy] = 0;
8390
8391             Tile[xx][yy] = EL_FLAMES;
8392
8393             if (IN_SCR_FIELD(sx, sy))
8394             {
8395               TEST_DrawLevelFieldCrumbled(xx, yy);
8396               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8397             }
8398           }
8399           else
8400           {
8401             if (Tile[xx][yy] == EL_FLAMES)
8402               Tile[xx][yy] = EL_EMPTY;
8403             TEST_DrawLevelField(xx, yy);
8404           }
8405         }
8406       }
8407
8408       if (MovDelay[x][y])       // element still has to wait some time
8409       {
8410         PlayLevelSoundAction(x, y, ACTION_WAITING);
8411
8412         return;
8413       }
8414     }
8415
8416     // now make next step
8417
8418     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8419
8420     if (DONT_COLLIDE_WITH(element) &&
8421         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8422         !PLAYER_ENEMY_PROTECTED(newx, newy))
8423     {
8424       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8425
8426       return;
8427     }
8428
8429     else if (CAN_MOVE_INTO_ACID(element) &&
8430              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8431              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8432              (MovDir[x][y] == MV_DOWN ||
8433               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8434     {
8435       SplashAcid(newx, newy);
8436       Store[x][y] = EL_ACID;
8437     }
8438     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8439     {
8440       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8441           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8442           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8443           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8444       {
8445         RemoveField(x, y);
8446         TEST_DrawLevelField(x, y);
8447
8448         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8449         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8450           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8451
8452         game.friends_still_needed--;
8453         if (!game.friends_still_needed &&
8454             !game.GameOver &&
8455             game.all_players_gone)
8456           LevelSolved();
8457
8458         return;
8459       }
8460       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8461       {
8462         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8463           TEST_DrawLevelField(newx, newy);
8464         else
8465           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8466       }
8467       else if (!IS_FREE(newx, newy))
8468       {
8469         GfxAction[x][y] = ACTION_WAITING;
8470
8471         if (IS_PLAYER(x, y))
8472           DrawPlayerField(x, y);
8473         else
8474           TEST_DrawLevelField(x, y);
8475
8476         return;
8477       }
8478     }
8479     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8480     {
8481       if (IS_FOOD_PIG(Tile[newx][newy]))
8482       {
8483         if (IS_MOVING(newx, newy))
8484           RemoveMovingField(newx, newy);
8485         else
8486         {
8487           Tile[newx][newy] = EL_EMPTY;
8488           TEST_DrawLevelField(newx, newy);
8489         }
8490
8491         PlayLevelSound(x, y, SND_PIG_DIGGING);
8492       }
8493       else if (!IS_FREE(newx, newy))
8494       {
8495         if (IS_PLAYER(x, y))
8496           DrawPlayerField(x, y);
8497         else
8498           TEST_DrawLevelField(x, y);
8499
8500         return;
8501       }
8502     }
8503     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8504     {
8505       if (Store[x][y] != EL_EMPTY)
8506       {
8507         boolean can_clone = FALSE;
8508         int xx, yy;
8509
8510         // check if element to clone is still there
8511         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8512         {
8513           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8514           {
8515             can_clone = TRUE;
8516
8517             break;
8518           }
8519         }
8520
8521         // cannot clone or target field not free anymore -- do not clone
8522         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8523           Store[x][y] = EL_EMPTY;
8524       }
8525
8526       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8527       {
8528         if (IS_MV_DIAGONAL(MovDir[x][y]))
8529         {
8530           int diagonal_move_dir = MovDir[x][y];
8531           int stored = Store[x][y];
8532           int change_delay = 8;
8533           int graphic;
8534
8535           // android is moving diagonally
8536
8537           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8538
8539           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8540           GfxElement[x][y] = EL_EMC_ANDROID;
8541           GfxAction[x][y] = ACTION_SHRINKING;
8542           GfxDir[x][y] = diagonal_move_dir;
8543           ChangeDelay[x][y] = change_delay;
8544
8545           if (Store[x][y] == EL_EMPTY)
8546             Store[x][y] = GfxElementEmpty[x][y];
8547
8548           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8549                                    GfxDir[x][y]);
8550
8551           DrawLevelGraphicAnimation(x, y, graphic);
8552           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8553
8554           if (Tile[newx][newy] == EL_ACID)
8555           {
8556             SplashAcid(newx, newy);
8557
8558             return;
8559           }
8560
8561           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8562
8563           Store[newx][newy] = EL_EMC_ANDROID;
8564           GfxElement[newx][newy] = EL_EMC_ANDROID;
8565           GfxAction[newx][newy] = ACTION_GROWING;
8566           GfxDir[newx][newy] = diagonal_move_dir;
8567           ChangeDelay[newx][newy] = change_delay;
8568
8569           graphic = el_act_dir2img(GfxElement[newx][newy],
8570                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8571
8572           DrawLevelGraphicAnimation(newx, newy, graphic);
8573           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8574
8575           return;
8576         }
8577         else
8578         {
8579           Tile[newx][newy] = EL_EMPTY;
8580           TEST_DrawLevelField(newx, newy);
8581
8582           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8583         }
8584       }
8585       else if (!IS_FREE(newx, newy))
8586       {
8587         return;
8588       }
8589     }
8590     else if (IS_CUSTOM_ELEMENT(element) &&
8591              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8592     {
8593       if (!DigFieldByCE(newx, newy, element))
8594         return;
8595
8596       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8597       {
8598         RunnerVisit[x][y] = FrameCounter;
8599         PlayerVisit[x][y] /= 8;         // expire player visit path
8600       }
8601     }
8602     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8603     {
8604       if (!IS_FREE(newx, newy))
8605       {
8606         if (IS_PLAYER(x, y))
8607           DrawPlayerField(x, y);
8608         else
8609           TEST_DrawLevelField(x, y);
8610
8611         return;
8612       }
8613       else
8614       {
8615         boolean wanna_flame = !RND(10);
8616         int dx = newx - x, dy = newy - y;
8617         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8618         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8619         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8620                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8621         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8622                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8623
8624         if ((wanna_flame ||
8625              IS_CLASSIC_ENEMY(element1) ||
8626              IS_CLASSIC_ENEMY(element2)) &&
8627             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8628             element1 != EL_FLAMES && element2 != EL_FLAMES)
8629         {
8630           ResetGfxAnimation(x, y);
8631           GfxAction[x][y] = ACTION_ATTACKING;
8632
8633           if (IS_PLAYER(x, y))
8634             DrawPlayerField(x, y);
8635           else
8636             TEST_DrawLevelField(x, y);
8637
8638           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8639
8640           MovDelay[x][y] = 50;
8641
8642           Tile[newx][newy] = EL_FLAMES;
8643           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8644             Tile[newx1][newy1] = EL_FLAMES;
8645           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8646             Tile[newx2][newy2] = EL_FLAMES;
8647
8648           return;
8649         }
8650       }
8651     }
8652     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8653              Tile[newx][newy] == EL_DIAMOND)
8654     {
8655       if (IS_MOVING(newx, newy))
8656         RemoveMovingField(newx, newy);
8657       else
8658       {
8659         Tile[newx][newy] = EL_EMPTY;
8660         TEST_DrawLevelField(newx, newy);
8661       }
8662
8663       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8664     }
8665     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8666              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8667     {
8668       if (AmoebaNr[newx][newy])
8669       {
8670         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8671         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8672             Tile[newx][newy] == EL_BD_AMOEBA)
8673           AmoebaCnt[AmoebaNr[newx][newy]]--;
8674       }
8675
8676       if (IS_MOVING(newx, newy))
8677       {
8678         RemoveMovingField(newx, newy);
8679       }
8680       else
8681       {
8682         Tile[newx][newy] = EL_EMPTY;
8683         TEST_DrawLevelField(newx, newy);
8684       }
8685
8686       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8687     }
8688     else if ((element == EL_PACMAN || element == EL_MOLE)
8689              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8690     {
8691       if (AmoebaNr[newx][newy])
8692       {
8693         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8694         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8695             Tile[newx][newy] == EL_BD_AMOEBA)
8696           AmoebaCnt[AmoebaNr[newx][newy]]--;
8697       }
8698
8699       if (element == EL_MOLE)
8700       {
8701         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8702         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8703
8704         ResetGfxAnimation(x, y);
8705         GfxAction[x][y] = ACTION_DIGGING;
8706         TEST_DrawLevelField(x, y);
8707
8708         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8709
8710         return;                         // wait for shrinking amoeba
8711       }
8712       else      // element == EL_PACMAN
8713       {
8714         Tile[newx][newy] = EL_EMPTY;
8715         TEST_DrawLevelField(newx, newy);
8716         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8717       }
8718     }
8719     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8720              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8721               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8722     {
8723       // wait for shrinking amoeba to completely disappear
8724       return;
8725     }
8726     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8727     {
8728       // object was running against a wall
8729
8730       TurnRound(x, y);
8731
8732       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8733         DrawLevelElementAnimation(x, y, element);
8734
8735       if (DONT_TOUCH(element))
8736         TestIfBadThingTouchesPlayer(x, y);
8737
8738       return;
8739     }
8740
8741     InitMovingField(x, y, MovDir[x][y]);
8742
8743     PlayLevelSoundAction(x, y, ACTION_MOVING);
8744   }
8745
8746   if (MovDir[x][y])
8747     ContinueMoving(x, y);
8748 }
8749
8750 void ContinueMoving(int x, int y)
8751 {
8752   int element = Tile[x][y];
8753   struct ElementInfo *ei = &element_info[element];
8754   int direction = MovDir[x][y];
8755   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8756   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8757   int newx = x + dx, newy = y + dy;
8758   int stored = Store[x][y];
8759   int stored_new = Store[newx][newy];
8760   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8761   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8762   boolean last_line = (newy == lev_fieldy - 1);
8763   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8764
8765   if (pushed_by_player)         // special case: moving object pushed by player
8766   {
8767     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8768   }
8769   else if (use_step_delay)      // special case: moving object has step delay
8770   {
8771     if (!MovDelay[x][y])
8772       MovPos[x][y] += getElementMoveStepsize(x, y);
8773
8774     if (MovDelay[x][y])
8775       MovDelay[x][y]--;
8776     else
8777       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8778
8779     if (MovDelay[x][y])
8780     {
8781       TEST_DrawLevelField(x, y);
8782
8783       return;   // element is still waiting
8784     }
8785   }
8786   else                          // normal case: generically moving object
8787   {
8788     MovPos[x][y] += getElementMoveStepsize(x, y);
8789   }
8790
8791   if (ABS(MovPos[x][y]) < TILEX)
8792   {
8793     TEST_DrawLevelField(x, y);
8794
8795     return;     // element is still moving
8796   }
8797
8798   // element reached destination field
8799
8800   Tile[x][y] = EL_EMPTY;
8801   Tile[newx][newy] = element;
8802   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8803
8804   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8805   {
8806     element = Tile[newx][newy] = EL_ACID;
8807   }
8808   else if (element == EL_MOLE)
8809   {
8810     Tile[x][y] = EL_SAND;
8811
8812     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8813   }
8814   else if (element == EL_QUICKSAND_FILLING)
8815   {
8816     element = Tile[newx][newy] = get_next_element(element);
8817     Store[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_QUICKSAND_EMPTYING)
8820   {
8821     Tile[x][y] = get_next_element(element);
8822     element = Tile[newx][newy] = Store[x][y];
8823   }
8824   else if (element == EL_QUICKSAND_FAST_FILLING)
8825   {
8826     element = Tile[newx][newy] = get_next_element(element);
8827     Store[newx][newy] = Store[x][y];
8828   }
8829   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8830   {
8831     Tile[x][y] = get_next_element(element);
8832     element = Tile[newx][newy] = Store[x][y];
8833   }
8834   else if (element == EL_MAGIC_WALL_FILLING)
8835   {
8836     element = Tile[newx][newy] = get_next_element(element);
8837     if (!game.magic_wall_active)
8838       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8839     Store[newx][newy] = Store[x][y];
8840   }
8841   else if (element == EL_MAGIC_WALL_EMPTYING)
8842   {
8843     Tile[x][y] = get_next_element(element);
8844     if (!game.magic_wall_active)
8845       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8846     element = Tile[newx][newy] = Store[x][y];
8847
8848     InitField(newx, newy, FALSE);
8849   }
8850   else if (element == EL_BD_MAGIC_WALL_FILLING)
8851   {
8852     element = Tile[newx][newy] = get_next_element(element);
8853     if (!game.magic_wall_active)
8854       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8855     Store[newx][newy] = Store[x][y];
8856   }
8857   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8858   {
8859     Tile[x][y] = get_next_element(element);
8860     if (!game.magic_wall_active)
8861       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8862     element = Tile[newx][newy] = Store[x][y];
8863
8864     InitField(newx, newy, FALSE);
8865   }
8866   else if (element == EL_DC_MAGIC_WALL_FILLING)
8867   {
8868     element = Tile[newx][newy] = get_next_element(element);
8869     if (!game.magic_wall_active)
8870       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8871     Store[newx][newy] = Store[x][y];
8872   }
8873   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8874   {
8875     Tile[x][y] = get_next_element(element);
8876     if (!game.magic_wall_active)
8877       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8878     element = Tile[newx][newy] = Store[x][y];
8879
8880     InitField(newx, newy, FALSE);
8881   }
8882   else if (element == EL_AMOEBA_DROPPING)
8883   {
8884     Tile[x][y] = get_next_element(element);
8885     element = Tile[newx][newy] = Store[x][y];
8886   }
8887   else if (element == EL_SOKOBAN_OBJECT)
8888   {
8889     if (Back[x][y])
8890       Tile[x][y] = Back[x][y];
8891
8892     if (Back[newx][newy])
8893       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8894
8895     Back[x][y] = Back[newx][newy] = 0;
8896   }
8897
8898   Store[x][y] = EL_EMPTY;
8899   MovPos[x][y] = 0;
8900   MovDir[x][y] = 0;
8901   MovDelay[x][y] = 0;
8902
8903   MovDelay[newx][newy] = 0;
8904
8905   if (CAN_CHANGE_OR_HAS_ACTION(element))
8906   {
8907     // copy element change control values to new field
8908     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8909     ChangePage[newx][newy]  = ChangePage[x][y];
8910     ChangeCount[newx][newy] = ChangeCount[x][y];
8911     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8912   }
8913
8914   CustomValue[newx][newy] = CustomValue[x][y];
8915
8916   ChangeDelay[x][y] = 0;
8917   ChangePage[x][y] = -1;
8918   ChangeCount[x][y] = 0;
8919   ChangeEvent[x][y] = -1;
8920
8921   CustomValue[x][y] = 0;
8922
8923   // copy animation control values to new field
8924   GfxFrame[newx][newy]  = GfxFrame[x][y];
8925   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8926   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8927   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8928
8929   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8930
8931   // some elements can leave other elements behind after moving
8932   if (ei->move_leave_element != EL_EMPTY &&
8933       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8934       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8935   {
8936     int move_leave_element = ei->move_leave_element;
8937
8938     // this makes it possible to leave the removed element again
8939     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8940       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8941
8942     Tile[x][y] = move_leave_element;
8943
8944     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8945       MovDir[x][y] = direction;
8946
8947     InitField(x, y, FALSE);
8948
8949     if (GFX_CRUMBLED(Tile[x][y]))
8950       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8951
8952     if (IS_PLAYER_ELEMENT(move_leave_element))
8953       RelocatePlayer(x, y, move_leave_element);
8954   }
8955
8956   // do this after checking for left-behind element
8957   ResetGfxAnimation(x, y);      // reset animation values for old field
8958
8959   if (!CAN_MOVE(element) ||
8960       (CAN_FALL(element) && direction == MV_DOWN &&
8961        (element == EL_SPRING ||
8962         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8963         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8964     GfxDir[x][y] = MovDir[newx][newy] = 0;
8965
8966   TEST_DrawLevelField(x, y);
8967   TEST_DrawLevelField(newx, newy);
8968
8969   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8970
8971   // prevent pushed element from moving on in pushed direction
8972   if (pushed_by_player && CAN_MOVE(element) &&
8973       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8974       !(element_info[element].move_pattern & direction))
8975     TurnRound(newx, newy);
8976
8977   // prevent elements on conveyor belt from moving on in last direction
8978   if (pushed_by_conveyor && CAN_FALL(element) &&
8979       direction & MV_HORIZONTAL)
8980     MovDir[newx][newy] = 0;
8981
8982   if (!pushed_by_player)
8983   {
8984     int nextx = newx + dx, nexty = newy + dy;
8985     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8986
8987     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8988
8989     if (CAN_FALL(element) && direction == MV_DOWN)
8990       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8991
8992     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8993       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8994
8995     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8996       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8997   }
8998
8999   if (DONT_TOUCH(element))      // object may be nasty to player or others
9000   {
9001     TestIfBadThingTouchesPlayer(newx, newy);
9002     TestIfBadThingTouchesFriend(newx, newy);
9003
9004     if (!IS_CUSTOM_ELEMENT(element))
9005       TestIfBadThingTouchesOtherBadThing(newx, newy);
9006   }
9007   else if (element == EL_PENGUIN)
9008     TestIfFriendTouchesBadThing(newx, newy);
9009
9010   if (DONT_GET_HIT_BY(element))
9011   {
9012     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9013   }
9014
9015   // give the player one last chance (one more frame) to move away
9016   if (CAN_FALL(element) && direction == MV_DOWN &&
9017       (last_line || (!IS_FREE(x, newy + 1) &&
9018                      (!IS_PLAYER(x, newy + 1) ||
9019                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9020     Impact(x, newy);
9021
9022   if (pushed_by_player && !game.use_change_when_pushing_bug)
9023   {
9024     int push_side = MV_DIR_OPPOSITE(direction);
9025     struct PlayerInfo *player = PLAYERINFO(x, y);
9026
9027     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9028                                player->index_bit, push_side);
9029     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9030                                         player->index_bit, push_side);
9031   }
9032
9033   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9034     MovDelay[newx][newy] = 1;
9035
9036   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9037
9038   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9039   TestIfElementHitsCustomElement(newx, newy, direction);
9040   TestIfPlayerTouchesCustomElement(newx, newy);
9041   TestIfElementTouchesCustomElement(newx, newy);
9042
9043   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9044       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9045     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9046                              MV_DIR_OPPOSITE(direction));
9047 }
9048
9049 int AmoebaNeighbourNr(int ax, int ay)
9050 {
9051   int i;
9052   int element = Tile[ax][ay];
9053   int group_nr = 0;
9054   struct XY *xy = xy_topdown;
9055
9056   for (i = 0; i < NUM_DIRECTIONS; i++)
9057   {
9058     int x = ax + xy[i].x;
9059     int y = ay + xy[i].y;
9060
9061     if (!IN_LEV_FIELD(x, y))
9062       continue;
9063
9064     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9065       group_nr = AmoebaNr[x][y];
9066   }
9067
9068   return group_nr;
9069 }
9070
9071 static void AmoebaMerge(int ax, int ay)
9072 {
9073   int i, x, y, xx, yy;
9074   int new_group_nr = AmoebaNr[ax][ay];
9075   struct XY *xy = xy_topdown;
9076
9077   if (new_group_nr == 0)
9078     return;
9079
9080   for (i = 0; i < NUM_DIRECTIONS; i++)
9081   {
9082     x = ax + xy[i].x;
9083     y = ay + xy[i].y;
9084
9085     if (!IN_LEV_FIELD(x, y))
9086       continue;
9087
9088     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9089          Tile[x][y] == EL_BD_AMOEBA ||
9090          Tile[x][y] == EL_AMOEBA_DEAD) &&
9091         AmoebaNr[x][y] != new_group_nr)
9092     {
9093       int old_group_nr = AmoebaNr[x][y];
9094
9095       if (old_group_nr == 0)
9096         return;
9097
9098       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9099       AmoebaCnt[old_group_nr] = 0;
9100       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9101       AmoebaCnt2[old_group_nr] = 0;
9102
9103       SCAN_PLAYFIELD(xx, yy)
9104       {
9105         if (AmoebaNr[xx][yy] == old_group_nr)
9106           AmoebaNr[xx][yy] = new_group_nr;
9107       }
9108     }
9109   }
9110 }
9111
9112 void AmoebaToDiamond(int ax, int ay)
9113 {
9114   int i, x, y;
9115
9116   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9117   {
9118     int group_nr = AmoebaNr[ax][ay];
9119
9120 #ifdef DEBUG
9121     if (group_nr == 0)
9122     {
9123       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9124       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9125
9126       return;
9127     }
9128 #endif
9129
9130     SCAN_PLAYFIELD(x, y)
9131     {
9132       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9133       {
9134         AmoebaNr[x][y] = 0;
9135         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9136       }
9137     }
9138
9139     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9140                             SND_AMOEBA_TURNING_TO_GEM :
9141                             SND_AMOEBA_TURNING_TO_ROCK));
9142     Bang(ax, ay);
9143   }
9144   else
9145   {
9146     struct XY *xy = xy_topdown;
9147
9148     for (i = 0; i < NUM_DIRECTIONS; i++)
9149     {
9150       x = ax + xy[i].x;
9151       y = ay + xy[i].y;
9152
9153       if (!IN_LEV_FIELD(x, y))
9154         continue;
9155
9156       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9157       {
9158         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9159                               SND_AMOEBA_TURNING_TO_GEM :
9160                               SND_AMOEBA_TURNING_TO_ROCK));
9161         Bang(x, y);
9162       }
9163     }
9164   }
9165 }
9166
9167 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9168 {
9169   int x, y;
9170   int group_nr = AmoebaNr[ax][ay];
9171   boolean done = FALSE;
9172
9173 #ifdef DEBUG
9174   if (group_nr == 0)
9175   {
9176     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9177     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9178
9179     return;
9180   }
9181 #endif
9182
9183   SCAN_PLAYFIELD(x, y)
9184   {
9185     if (AmoebaNr[x][y] == group_nr &&
9186         (Tile[x][y] == EL_AMOEBA_DEAD ||
9187          Tile[x][y] == EL_BD_AMOEBA ||
9188          Tile[x][y] == EL_AMOEBA_GROWING))
9189     {
9190       AmoebaNr[x][y] = 0;
9191       Tile[x][y] = new_element;
9192       InitField(x, y, FALSE);
9193       TEST_DrawLevelField(x, y);
9194       done = TRUE;
9195     }
9196   }
9197
9198   if (done)
9199     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9200                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9201                             SND_BD_AMOEBA_TURNING_TO_GEM));
9202 }
9203
9204 static void AmoebaGrowing(int x, int y)
9205 {
9206   static DelayCounter sound_delay = { 0 };
9207
9208   if (!MovDelay[x][y])          // start new growing cycle
9209   {
9210     MovDelay[x][y] = 7;
9211
9212     if (DelayReached(&sound_delay))
9213     {
9214       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9215       sound_delay.value = 30;
9216     }
9217   }
9218
9219   if (MovDelay[x][y])           // wait some time before growing bigger
9220   {
9221     MovDelay[x][y]--;
9222     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9223     {
9224       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9225                                            6 - MovDelay[x][y]);
9226
9227       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9228     }
9229
9230     if (!MovDelay[x][y])
9231     {
9232       Tile[x][y] = Store[x][y];
9233       Store[x][y] = 0;
9234       TEST_DrawLevelField(x, y);
9235     }
9236   }
9237 }
9238
9239 static void AmoebaShrinking(int x, int y)
9240 {
9241   static DelayCounter sound_delay = { 0 };
9242
9243   if (!MovDelay[x][y])          // start new shrinking cycle
9244   {
9245     MovDelay[x][y] = 7;
9246
9247     if (DelayReached(&sound_delay))
9248       sound_delay.value = 30;
9249   }
9250
9251   if (MovDelay[x][y])           // wait some time before shrinking
9252   {
9253     MovDelay[x][y]--;
9254     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9255     {
9256       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9257                                            6 - MovDelay[x][y]);
9258
9259       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9260     }
9261
9262     if (!MovDelay[x][y])
9263     {
9264       Tile[x][y] = EL_EMPTY;
9265       TEST_DrawLevelField(x, y);
9266
9267       // don't let mole enter this field in this cycle;
9268       // (give priority to objects falling to this field from above)
9269       Stop[x][y] = TRUE;
9270     }
9271   }
9272 }
9273
9274 static void AmoebaReproduce(int ax, int ay)
9275 {
9276   int i;
9277   int element = Tile[ax][ay];
9278   int graphic = el2img(element);
9279   int newax = ax, neway = ay;
9280   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9281   struct XY *xy = xy_topdown;
9282
9283   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9284   {
9285     Tile[ax][ay] = EL_AMOEBA_DEAD;
9286     TEST_DrawLevelField(ax, ay);
9287     return;
9288   }
9289
9290   if (IS_ANIMATED(graphic))
9291     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9292
9293   if (!MovDelay[ax][ay])        // start making new amoeba field
9294     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9295
9296   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9297   {
9298     MovDelay[ax][ay]--;
9299     if (MovDelay[ax][ay])
9300       return;
9301   }
9302
9303   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9304   {
9305     int start = RND(4);
9306     int x = ax + xy[start].x;
9307     int y = ay + xy[start].y;
9308
9309     if (!IN_LEV_FIELD(x, y))
9310       return;
9311
9312     if (IS_FREE(x, y) ||
9313         CAN_GROW_INTO(Tile[x][y]) ||
9314         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9315         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9316     {
9317       newax = x;
9318       neway = y;
9319     }
9320
9321     if (newax == ax && neway == ay)
9322       return;
9323   }
9324   else                          // normal or "filled" (BD style) amoeba
9325   {
9326     int start = RND(4);
9327     boolean waiting_for_player = FALSE;
9328
9329     for (i = 0; i < NUM_DIRECTIONS; i++)
9330     {
9331       int j = (start + i) % 4;
9332       int x = ax + xy[j].x;
9333       int y = ay + xy[j].y;
9334
9335       if (!IN_LEV_FIELD(x, y))
9336         continue;
9337
9338       if (IS_FREE(x, y) ||
9339           CAN_GROW_INTO(Tile[x][y]) ||
9340           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9341           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9342       {
9343         newax = x;
9344         neway = y;
9345         break;
9346       }
9347       else if (IS_PLAYER(x, y))
9348         waiting_for_player = TRUE;
9349     }
9350
9351     if (newax == ax && neway == ay)             // amoeba cannot grow
9352     {
9353       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9354       {
9355         Tile[ax][ay] = EL_AMOEBA_DEAD;
9356         TEST_DrawLevelField(ax, ay);
9357         AmoebaCnt[AmoebaNr[ax][ay]]--;
9358
9359         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9360         {
9361           if (element == EL_AMOEBA_FULL)
9362             AmoebaToDiamond(ax, ay);
9363           else if (element == EL_BD_AMOEBA)
9364             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9365         }
9366       }
9367       return;
9368     }
9369     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9370     {
9371       // amoeba gets larger by growing in some direction
9372
9373       int new_group_nr = AmoebaNr[ax][ay];
9374
9375 #ifdef DEBUG
9376   if (new_group_nr == 0)
9377   {
9378     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9379           newax, neway);
9380     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9381
9382     return;
9383   }
9384 #endif
9385
9386       AmoebaNr[newax][neway] = new_group_nr;
9387       AmoebaCnt[new_group_nr]++;
9388       AmoebaCnt2[new_group_nr]++;
9389
9390       // if amoeba touches other amoeba(s) after growing, unify them
9391       AmoebaMerge(newax, neway);
9392
9393       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9394       {
9395         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9396         return;
9397       }
9398     }
9399   }
9400
9401   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9402       (neway == lev_fieldy - 1 && newax != ax))
9403   {
9404     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9405     Store[newax][neway] = element;
9406   }
9407   else if (neway == ay || element == EL_EMC_DRIPPER)
9408   {
9409     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9410
9411     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9412   }
9413   else
9414   {
9415     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9416     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9417     Store[ax][ay] = EL_AMOEBA_DROP;
9418     ContinueMoving(ax, ay);
9419     return;
9420   }
9421
9422   TEST_DrawLevelField(newax, neway);
9423 }
9424
9425 static void Life(int ax, int ay)
9426 {
9427   int x1, y1, x2, y2;
9428   int life_time = 40;
9429   int element = Tile[ax][ay];
9430   int graphic = el2img(element);
9431   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9432                          level.biomaze);
9433   boolean changed = FALSE;
9434
9435   if (IS_ANIMATED(graphic))
9436     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9437
9438   if (Stop[ax][ay])
9439     return;
9440
9441   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9442     MovDelay[ax][ay] = life_time;
9443
9444   if (MovDelay[ax][ay])         // wait some time before next cycle
9445   {
9446     MovDelay[ax][ay]--;
9447     if (MovDelay[ax][ay])
9448       return;
9449   }
9450
9451   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9452   {
9453     int xx = ax + x1, yy = ay + y1;
9454     int old_element = Tile[xx][yy];
9455     int num_neighbours = 0;
9456
9457     if (!IN_LEV_FIELD(xx, yy))
9458       continue;
9459
9460     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9461     {
9462       int x = xx + x2, y = yy + y2;
9463
9464       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9465         continue;
9466
9467       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9468       boolean is_neighbour = FALSE;
9469
9470       if (level.use_life_bugs)
9471         is_neighbour =
9472           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9473            (IS_FREE(x, y)                             &&  Stop[x][y]));
9474       else
9475         is_neighbour =
9476           (Last[x][y] == element || is_player_cell);
9477
9478       if (is_neighbour)
9479         num_neighbours++;
9480     }
9481
9482     boolean is_free = FALSE;
9483
9484     if (level.use_life_bugs)
9485       is_free = (IS_FREE(xx, yy));
9486     else
9487       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9488
9489     if (xx == ax && yy == ay)           // field in the middle
9490     {
9491       if (num_neighbours < life_parameter[0] ||
9492           num_neighbours > life_parameter[1])
9493       {
9494         Tile[xx][yy] = EL_EMPTY;
9495         if (Tile[xx][yy] != old_element)
9496           TEST_DrawLevelField(xx, yy);
9497         Stop[xx][yy] = TRUE;
9498         changed = TRUE;
9499       }
9500     }
9501     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9502     {                                   // free border field
9503       if (num_neighbours >= life_parameter[2] &&
9504           num_neighbours <= life_parameter[3])
9505       {
9506         Tile[xx][yy] = element;
9507         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9508         if (Tile[xx][yy] != old_element)
9509           TEST_DrawLevelField(xx, yy);
9510         Stop[xx][yy] = TRUE;
9511         changed = TRUE;
9512       }
9513     }
9514   }
9515
9516   if (changed)
9517     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9518                    SND_GAME_OF_LIFE_GROWING);
9519 }
9520
9521 static void InitRobotWheel(int x, int y)
9522 {
9523   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9524 }
9525
9526 static void RunRobotWheel(int x, int y)
9527 {
9528   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9529 }
9530
9531 static void StopRobotWheel(int x, int y)
9532 {
9533   if (game.robot_wheel_x == x &&
9534       game.robot_wheel_y == y)
9535   {
9536     game.robot_wheel_x = -1;
9537     game.robot_wheel_y = -1;
9538     game.robot_wheel_active = FALSE;
9539   }
9540 }
9541
9542 static void InitTimegateWheel(int x, int y)
9543 {
9544   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9545 }
9546
9547 static void RunTimegateWheel(int x, int y)
9548 {
9549   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9550 }
9551
9552 static void InitMagicBallDelay(int x, int y)
9553 {
9554   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9555 }
9556
9557 static void ActivateMagicBall(int bx, int by)
9558 {
9559   int x, y;
9560
9561   if (level.ball_random)
9562   {
9563     int pos_border = RND(8);    // select one of the eight border elements
9564     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9565     int xx = pos_content % 3;
9566     int yy = pos_content / 3;
9567
9568     x = bx - 1 + xx;
9569     y = by - 1 + yy;
9570
9571     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9572       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9573   }
9574   else
9575   {
9576     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9577     {
9578       int xx = x - bx + 1;
9579       int yy = y - by + 1;
9580
9581       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9582         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9583     }
9584   }
9585
9586   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9587 }
9588
9589 static void CheckExit(int x, int y)
9590 {
9591   if (game.gems_still_needed > 0 ||
9592       game.sokoban_fields_still_needed > 0 ||
9593       game.sokoban_objects_still_needed > 0 ||
9594       game.lights_still_needed > 0)
9595   {
9596     int element = Tile[x][y];
9597     int graphic = el2img(element);
9598
9599     if (IS_ANIMATED(graphic))
9600       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9601
9602     return;
9603   }
9604
9605   // do not re-open exit door closed after last player
9606   if (game.all_players_gone)
9607     return;
9608
9609   Tile[x][y] = EL_EXIT_OPENING;
9610
9611   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9612 }
9613
9614 static void CheckExitEM(int x, int y)
9615 {
9616   if (game.gems_still_needed > 0 ||
9617       game.sokoban_fields_still_needed > 0 ||
9618       game.sokoban_objects_still_needed > 0 ||
9619       game.lights_still_needed > 0)
9620   {
9621     int element = Tile[x][y];
9622     int graphic = el2img(element);
9623
9624     if (IS_ANIMATED(graphic))
9625       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9626
9627     return;
9628   }
9629
9630   // do not re-open exit door closed after last player
9631   if (game.all_players_gone)
9632     return;
9633
9634   Tile[x][y] = EL_EM_EXIT_OPENING;
9635
9636   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9637 }
9638
9639 static void CheckExitSteel(int x, int y)
9640 {
9641   if (game.gems_still_needed > 0 ||
9642       game.sokoban_fields_still_needed > 0 ||
9643       game.sokoban_objects_still_needed > 0 ||
9644       game.lights_still_needed > 0)
9645   {
9646     int element = Tile[x][y];
9647     int graphic = el2img(element);
9648
9649     if (IS_ANIMATED(graphic))
9650       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9651
9652     return;
9653   }
9654
9655   // do not re-open exit door closed after last player
9656   if (game.all_players_gone)
9657     return;
9658
9659   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9660
9661   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9662 }
9663
9664 static void CheckExitSteelEM(int x, int y)
9665 {
9666   if (game.gems_still_needed > 0 ||
9667       game.sokoban_fields_still_needed > 0 ||
9668       game.sokoban_objects_still_needed > 0 ||
9669       game.lights_still_needed > 0)
9670   {
9671     int element = Tile[x][y];
9672     int graphic = el2img(element);
9673
9674     if (IS_ANIMATED(graphic))
9675       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9676
9677     return;
9678   }
9679
9680   // do not re-open exit door closed after last player
9681   if (game.all_players_gone)
9682     return;
9683
9684   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9685
9686   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9687 }
9688
9689 static void CheckExitSP(int x, int y)
9690 {
9691   if (game.gems_still_needed > 0)
9692   {
9693     int element = Tile[x][y];
9694     int graphic = el2img(element);
9695
9696     if (IS_ANIMATED(graphic))
9697       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9698
9699     return;
9700   }
9701
9702   // do not re-open exit door closed after last player
9703   if (game.all_players_gone)
9704     return;
9705
9706   Tile[x][y] = EL_SP_EXIT_OPENING;
9707
9708   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9709 }
9710
9711 static void CloseAllOpenTimegates(void)
9712 {
9713   int x, y;
9714
9715   SCAN_PLAYFIELD(x, y)
9716   {
9717     int element = Tile[x][y];
9718
9719     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9720     {
9721       Tile[x][y] = EL_TIMEGATE_CLOSING;
9722
9723       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9724     }
9725   }
9726 }
9727
9728 static void DrawTwinkleOnField(int x, int y)
9729 {
9730   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9731     return;
9732
9733   if (Tile[x][y] == EL_BD_DIAMOND)
9734     return;
9735
9736   if (MovDelay[x][y] == 0)      // next animation frame
9737     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9738
9739   if (MovDelay[x][y] != 0)      // wait some time before next frame
9740   {
9741     MovDelay[x][y]--;
9742
9743     DrawLevelElementAnimation(x, y, Tile[x][y]);
9744
9745     if (MovDelay[x][y] != 0)
9746     {
9747       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9748                                            10 - MovDelay[x][y]);
9749
9750       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9751     }
9752   }
9753 }
9754
9755 static void WallGrowing(int x, int y)
9756 {
9757   int delay = 6;
9758
9759   if (!MovDelay[x][y])          // next animation frame
9760     MovDelay[x][y] = 3 * delay;
9761
9762   if (MovDelay[x][y])           // wait some time before next frame
9763   {
9764     MovDelay[x][y]--;
9765
9766     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9767     {
9768       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9769       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9770
9771       DrawLevelGraphic(x, y, graphic, frame);
9772     }
9773
9774     if (!MovDelay[x][y])
9775     {
9776       if (MovDir[x][y] == MV_LEFT)
9777       {
9778         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9779           TEST_DrawLevelField(x - 1, y);
9780       }
9781       else if (MovDir[x][y] == MV_RIGHT)
9782       {
9783         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9784           TEST_DrawLevelField(x + 1, y);
9785       }
9786       else if (MovDir[x][y] == MV_UP)
9787       {
9788         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9789           TEST_DrawLevelField(x, y - 1);
9790       }
9791       else
9792       {
9793         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9794           TEST_DrawLevelField(x, y + 1);
9795       }
9796
9797       Tile[x][y] = Store[x][y];
9798       Store[x][y] = 0;
9799       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9800       TEST_DrawLevelField(x, y);
9801     }
9802   }
9803 }
9804
9805 static void CheckWallGrowing(int ax, int ay)
9806 {
9807   int element = Tile[ax][ay];
9808   int graphic = el2img(element);
9809   boolean free_top    = FALSE;
9810   boolean free_bottom = FALSE;
9811   boolean free_left   = FALSE;
9812   boolean free_right  = FALSE;
9813   boolean stop_top    = FALSE;
9814   boolean stop_bottom = FALSE;
9815   boolean stop_left   = FALSE;
9816   boolean stop_right  = FALSE;
9817   boolean new_wall    = FALSE;
9818
9819   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9820                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9821                            element == EL_EXPANDABLE_STEELWALL_ANY);
9822
9823   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9824                              element == EL_EXPANDABLE_WALL_ANY ||
9825                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9826                              element == EL_EXPANDABLE_STEELWALL_ANY);
9827
9828   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9829                              element == EL_EXPANDABLE_WALL_ANY ||
9830                              element == EL_EXPANDABLE_WALL ||
9831                              element == EL_BD_EXPANDABLE_WALL ||
9832                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9833                              element == EL_EXPANDABLE_STEELWALL_ANY);
9834
9835   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9836                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9837
9838   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9839                              element == EL_EXPANDABLE_WALL ||
9840                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9841
9842   int wall_growing = (is_steelwall ?
9843                       EL_EXPANDABLE_STEELWALL_GROWING :
9844                       EL_EXPANDABLE_WALL_GROWING);
9845
9846   int gfx_wall_growing_up    = (is_steelwall ?
9847                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9848                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9849   int gfx_wall_growing_down  = (is_steelwall ?
9850                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9851                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9852   int gfx_wall_growing_left  = (is_steelwall ?
9853                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9854                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9855   int gfx_wall_growing_right = (is_steelwall ?
9856                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9857                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9858
9859   if (IS_ANIMATED(graphic))
9860     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9861
9862   if (!MovDelay[ax][ay])        // start building new wall
9863     MovDelay[ax][ay] = 6;
9864
9865   if (MovDelay[ax][ay])         // wait some time before building new wall
9866   {
9867     MovDelay[ax][ay]--;
9868     if (MovDelay[ax][ay])
9869       return;
9870   }
9871
9872   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9873     free_top = TRUE;
9874   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9875     free_bottom = TRUE;
9876   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9877     free_left = TRUE;
9878   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9879     free_right = TRUE;
9880
9881   if (grow_vertical)
9882   {
9883     if (free_top)
9884     {
9885       Tile[ax][ay - 1] = wall_growing;
9886       Store[ax][ay - 1] = element;
9887       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9888
9889       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9890         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9891
9892       new_wall = TRUE;
9893     }
9894
9895     if (free_bottom)
9896     {
9897       Tile[ax][ay + 1] = wall_growing;
9898       Store[ax][ay + 1] = element;
9899       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9900
9901       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9902         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9903
9904       new_wall = TRUE;
9905     }
9906   }
9907
9908   if (grow_horizontal)
9909   {
9910     if (free_left)
9911     {
9912       Tile[ax - 1][ay] = wall_growing;
9913       Store[ax - 1][ay] = element;
9914       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9915
9916       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9917         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9918
9919       new_wall = TRUE;
9920     }
9921
9922     if (free_right)
9923     {
9924       Tile[ax + 1][ay] = wall_growing;
9925       Store[ax + 1][ay] = element;
9926       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9927
9928       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9929         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9930
9931       new_wall = TRUE;
9932     }
9933   }
9934
9935   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9936     TEST_DrawLevelField(ax, ay);
9937
9938   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9939     stop_top = TRUE;
9940   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9941     stop_bottom = TRUE;
9942   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9943     stop_left = TRUE;
9944   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9945     stop_right = TRUE;
9946
9947   if (((stop_top && stop_bottom) || stop_horizontal) &&
9948       ((stop_left && stop_right) || stop_vertical))
9949     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9950
9951   if (new_wall)
9952     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9953 }
9954
9955 static void CheckForDragon(int x, int y)
9956 {
9957   int i, j;
9958   boolean dragon_found = FALSE;
9959   struct XY *xy = xy_topdown;
9960
9961   for (i = 0; i < NUM_DIRECTIONS; i++)
9962   {
9963     for (j = 0; j < 4; j++)
9964     {
9965       int xx = x + j * xy[i].x;
9966       int yy = y + j * xy[i].y;
9967
9968       if (IN_LEV_FIELD(xx, yy) &&
9969           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9970       {
9971         if (Tile[xx][yy] == EL_DRAGON)
9972           dragon_found = TRUE;
9973       }
9974       else
9975         break;
9976     }
9977   }
9978
9979   if (!dragon_found)
9980   {
9981     for (i = 0; i < NUM_DIRECTIONS; i++)
9982     {
9983       for (j = 0; j < 3; j++)
9984       {
9985         int xx = x + j * xy[i].x;
9986         int yy = y + j * xy[i].y;
9987
9988         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9989         {
9990           Tile[xx][yy] = EL_EMPTY;
9991           TEST_DrawLevelField(xx, yy);
9992         }
9993         else
9994           break;
9995       }
9996     }
9997   }
9998 }
9999
10000 static void InitBuggyBase(int x, int y)
10001 {
10002   int element = Tile[x][y];
10003   int activating_delay = FRAMES_PER_SECOND / 4;
10004
10005   ChangeDelay[x][y] =
10006     (element == EL_SP_BUGGY_BASE ?
10007      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10008      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10009      activating_delay :
10010      element == EL_SP_BUGGY_BASE_ACTIVE ?
10011      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10012 }
10013
10014 static void WarnBuggyBase(int x, int y)
10015 {
10016   int i;
10017   struct XY *xy = xy_topdown;
10018
10019   for (i = 0; i < NUM_DIRECTIONS; i++)
10020   {
10021     int xx = x + xy[i].x;
10022     int yy = y + xy[i].y;
10023
10024     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10025     {
10026       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10027
10028       break;
10029     }
10030   }
10031 }
10032
10033 static void InitTrap(int x, int y)
10034 {
10035   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10036 }
10037
10038 static void ActivateTrap(int x, int y)
10039 {
10040   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10041 }
10042
10043 static void ChangeActiveTrap(int x, int y)
10044 {
10045   int graphic = IMG_TRAP_ACTIVE;
10046
10047   // if new animation frame was drawn, correct crumbled sand border
10048   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10049     TEST_DrawLevelFieldCrumbled(x, y);
10050 }
10051
10052 static int getSpecialActionElement(int element, int number, int base_element)
10053 {
10054   return (element != EL_EMPTY ? element :
10055           number != -1 ? base_element + number - 1 :
10056           EL_EMPTY);
10057 }
10058
10059 static int getModifiedActionNumber(int value_old, int operator, int operand,
10060                                    int value_min, int value_max)
10061 {
10062   int value_new = (operator == CA_MODE_SET      ? operand :
10063                    operator == CA_MODE_ADD      ? value_old + operand :
10064                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10065                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10066                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10067                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10068                    value_old);
10069
10070   return (value_new < value_min ? value_min :
10071           value_new > value_max ? value_max :
10072           value_new);
10073 }
10074
10075 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10076 {
10077   struct ElementInfo *ei = &element_info[element];
10078   struct ElementChangeInfo *change = &ei->change_page[page];
10079   int target_element = change->target_element;
10080   int action_type = change->action_type;
10081   int action_mode = change->action_mode;
10082   int action_arg = change->action_arg;
10083   int action_element = change->action_element;
10084   int i;
10085
10086   if (!change->has_action)
10087     return;
10088
10089   // ---------- determine action paramater values -----------------------------
10090
10091   int level_time_value =
10092     (level.time > 0 ? TimeLeft :
10093      TimePlayed);
10094
10095   int action_arg_element_raw =
10096     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10097      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10098      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10099      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10100      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10101      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10102      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10103      EL_EMPTY);
10104   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10105
10106   int action_arg_direction =
10107     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10108      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10109      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10110      change->actual_trigger_side :
10111      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10112      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10113      MV_NONE);
10114
10115   int action_arg_number_min =
10116     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10117      CA_ARG_MIN);
10118
10119   int action_arg_number_max =
10120     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10121      action_type == CA_SET_LEVEL_GEMS ? 999 :
10122      action_type == CA_SET_LEVEL_TIME ? 9999 :
10123      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10124      action_type == CA_SET_CE_VALUE ? 9999 :
10125      action_type == CA_SET_CE_SCORE ? 9999 :
10126      CA_ARG_MAX);
10127
10128   int action_arg_number_reset =
10129     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10130      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10131      action_type == CA_SET_LEVEL_TIME ? level.time :
10132      action_type == CA_SET_LEVEL_SCORE ? 0 :
10133      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10134      action_type == CA_SET_CE_SCORE ? 0 :
10135      0);
10136
10137   int action_arg_number =
10138     (action_arg <= CA_ARG_MAX ? action_arg :
10139      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10140      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10141      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10142      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10143      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10144      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10145      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10146      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10147      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10148      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10149      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10150      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10151      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10152      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10153      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10154      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10155      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10156      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10157      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10158      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10159      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10160      -1);
10161
10162   int action_arg_number_old =
10163     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10164      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10165      action_type == CA_SET_LEVEL_SCORE ? game.score :
10166      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10167      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10168      0);
10169
10170   int action_arg_number_new =
10171     getModifiedActionNumber(action_arg_number_old,
10172                             action_mode, action_arg_number,
10173                             action_arg_number_min, action_arg_number_max);
10174
10175   int trigger_player_bits =
10176     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10177      change->actual_trigger_player_bits : change->trigger_player);
10178
10179   int action_arg_player_bits =
10180     (action_arg >= CA_ARG_PLAYER_1 &&
10181      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10182      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10183      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10184      PLAYER_BITS_ANY);
10185
10186   // ---------- execute action  -----------------------------------------------
10187
10188   switch (action_type)
10189   {
10190     case CA_NO_ACTION:
10191     {
10192       return;
10193     }
10194
10195     // ---------- level actions  ----------------------------------------------
10196
10197     case CA_RESTART_LEVEL:
10198     {
10199       game.restart_level = TRUE;
10200
10201       break;
10202     }
10203
10204     case CA_SHOW_ENVELOPE:
10205     {
10206       int element = getSpecialActionElement(action_arg_element,
10207                                             action_arg_number, EL_ENVELOPE_1);
10208
10209       if (IS_ENVELOPE(element))
10210         local_player->show_envelope = element;
10211
10212       break;
10213     }
10214
10215     case CA_SET_LEVEL_TIME:
10216     {
10217       if (level.time > 0)       // only modify limited time value
10218       {
10219         TimeLeft = action_arg_number_new;
10220
10221         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10222
10223         DisplayGameControlValues();
10224
10225         if (!TimeLeft && game.time_limit)
10226           for (i = 0; i < MAX_PLAYERS; i++)
10227             KillPlayer(&stored_player[i]);
10228       }
10229
10230       break;
10231     }
10232
10233     case CA_SET_LEVEL_SCORE:
10234     {
10235       game.score = action_arg_number_new;
10236
10237       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10238
10239       DisplayGameControlValues();
10240
10241       break;
10242     }
10243
10244     case CA_SET_LEVEL_GEMS:
10245     {
10246       game.gems_still_needed = action_arg_number_new;
10247
10248       game.snapshot.collected_item = TRUE;
10249
10250       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10251
10252       DisplayGameControlValues();
10253
10254       break;
10255     }
10256
10257     case CA_SET_LEVEL_WIND:
10258     {
10259       game.wind_direction = action_arg_direction;
10260
10261       break;
10262     }
10263
10264     case CA_SET_LEVEL_RANDOM_SEED:
10265     {
10266       // ensure that setting a new random seed while playing is predictable
10267       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10268
10269       break;
10270     }
10271
10272     // ---------- player actions  ---------------------------------------------
10273
10274     case CA_MOVE_PLAYER:
10275     case CA_MOVE_PLAYER_NEW:
10276     {
10277       // automatically move to the next field in specified direction
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279         if (trigger_player_bits & (1 << i))
10280           if (action_type == CA_MOVE_PLAYER ||
10281               stored_player[i].MovPos == 0)
10282             stored_player[i].programmed_action = action_arg_direction;
10283
10284       break;
10285     }
10286
10287     case CA_EXIT_PLAYER:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290         if (action_arg_player_bits & (1 << i))
10291           ExitPlayer(&stored_player[i]);
10292
10293       if (game.players_still_needed == 0)
10294         LevelSolved();
10295
10296       break;
10297     }
10298
10299     case CA_KILL_PLAYER:
10300     {
10301       for (i = 0; i < MAX_PLAYERS; i++)
10302         if (action_arg_player_bits & (1 << i))
10303           KillPlayer(&stored_player[i]);
10304
10305       break;
10306     }
10307
10308     case CA_SET_PLAYER_KEYS:
10309     {
10310       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10311       int element = getSpecialActionElement(action_arg_element,
10312                                             action_arg_number, EL_KEY_1);
10313
10314       if (IS_KEY(element))
10315       {
10316         for (i = 0; i < MAX_PLAYERS; i++)
10317         {
10318           if (trigger_player_bits & (1 << i))
10319           {
10320             stored_player[i].key[KEY_NR(element)] = key_state;
10321
10322             DrawGameDoorValues();
10323           }
10324         }
10325       }
10326
10327       break;
10328     }
10329
10330     case CA_SET_PLAYER_SPEED:
10331     {
10332       for (i = 0; i < MAX_PLAYERS; i++)
10333       {
10334         if (trigger_player_bits & (1 << i))
10335         {
10336           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10337
10338           if (action_arg == CA_ARG_SPEED_FASTER &&
10339               stored_player[i].cannot_move)
10340           {
10341             action_arg_number = STEPSIZE_VERY_SLOW;
10342           }
10343           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10344                    action_arg == CA_ARG_SPEED_FASTER)
10345           {
10346             action_arg_number = 2;
10347             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10348                            CA_MODE_MULTIPLY);
10349           }
10350           else if (action_arg == CA_ARG_NUMBER_RESET)
10351           {
10352             action_arg_number = level.initial_player_stepsize[i];
10353           }
10354
10355           move_stepsize =
10356             getModifiedActionNumber(move_stepsize,
10357                                     action_mode,
10358                                     action_arg_number,
10359                                     action_arg_number_min,
10360                                     action_arg_number_max);
10361
10362           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10363         }
10364       }
10365
10366       break;
10367     }
10368
10369     case CA_SET_PLAYER_SHIELD:
10370     {
10371       for (i = 0; i < MAX_PLAYERS; i++)
10372       {
10373         if (trigger_player_bits & (1 << i))
10374         {
10375           if (action_arg == CA_ARG_SHIELD_OFF)
10376           {
10377             stored_player[i].shield_normal_time_left = 0;
10378             stored_player[i].shield_deadly_time_left = 0;
10379           }
10380           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10381           {
10382             stored_player[i].shield_normal_time_left = 999999;
10383           }
10384           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10385           {
10386             stored_player[i].shield_normal_time_left = 999999;
10387             stored_player[i].shield_deadly_time_left = 999999;
10388           }
10389         }
10390       }
10391
10392       break;
10393     }
10394
10395     case CA_SET_PLAYER_GRAVITY:
10396     {
10397       for (i = 0; i < MAX_PLAYERS; i++)
10398       {
10399         if (trigger_player_bits & (1 << i))
10400         {
10401           stored_player[i].gravity =
10402             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10403              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10404              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10405              stored_player[i].gravity);
10406         }
10407       }
10408
10409       break;
10410     }
10411
10412     case CA_SET_PLAYER_ARTWORK:
10413     {
10414       for (i = 0; i < MAX_PLAYERS; i++)
10415       {
10416         if (trigger_player_bits & (1 << i))
10417         {
10418           int artwork_element = action_arg_element;
10419
10420           if (action_arg == CA_ARG_ELEMENT_RESET)
10421             artwork_element =
10422               (level.use_artwork_element[i] ? level.artwork_element[i] :
10423                stored_player[i].element_nr);
10424
10425           if (stored_player[i].artwork_element != artwork_element)
10426             stored_player[i].Frame = 0;
10427
10428           stored_player[i].artwork_element = artwork_element;
10429
10430           SetPlayerWaiting(&stored_player[i], FALSE);
10431
10432           // set number of special actions for bored and sleeping animation
10433           stored_player[i].num_special_action_bored =
10434             get_num_special_action(artwork_element,
10435                                    ACTION_BORING_1, ACTION_BORING_LAST);
10436           stored_player[i].num_special_action_sleeping =
10437             get_num_special_action(artwork_element,
10438                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10439         }
10440       }
10441
10442       break;
10443     }
10444
10445     case CA_SET_PLAYER_INVENTORY:
10446     {
10447       for (i = 0; i < MAX_PLAYERS; i++)
10448       {
10449         struct PlayerInfo *player = &stored_player[i];
10450         int j, k;
10451
10452         if (trigger_player_bits & (1 << i))
10453         {
10454           int inventory_element = action_arg_element;
10455
10456           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10457               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10458               action_arg == CA_ARG_ELEMENT_ACTION)
10459           {
10460             int element = inventory_element;
10461             int collect_count = element_info[element].collect_count_initial;
10462
10463             if (!IS_CUSTOM_ELEMENT(element))
10464               collect_count = 1;
10465
10466             if (collect_count == 0)
10467               player->inventory_infinite_element = element;
10468             else
10469               for (k = 0; k < collect_count; k++)
10470                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10471                   player->inventory_element[player->inventory_size++] =
10472                     element;
10473           }
10474           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10475                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10476                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10477           {
10478             if (player->inventory_infinite_element != EL_UNDEFINED &&
10479                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10480                                      action_arg_element_raw))
10481               player->inventory_infinite_element = EL_UNDEFINED;
10482
10483             for (k = 0, j = 0; j < player->inventory_size; j++)
10484             {
10485               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10486                                         action_arg_element_raw))
10487                 player->inventory_element[k++] = player->inventory_element[j];
10488             }
10489
10490             player->inventory_size = k;
10491           }
10492           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10493           {
10494             if (player->inventory_size > 0)
10495             {
10496               for (j = 0; j < player->inventory_size - 1; j++)
10497                 player->inventory_element[j] = player->inventory_element[j + 1];
10498
10499               player->inventory_size--;
10500             }
10501           }
10502           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10503           {
10504             if (player->inventory_size > 0)
10505               player->inventory_size--;
10506           }
10507           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10508           {
10509             player->inventory_infinite_element = EL_UNDEFINED;
10510             player->inventory_size = 0;
10511           }
10512           else if (action_arg == CA_ARG_INVENTORY_RESET)
10513           {
10514             player->inventory_infinite_element = EL_UNDEFINED;
10515             player->inventory_size = 0;
10516
10517             if (level.use_initial_inventory[i])
10518             {
10519               for (j = 0; j < level.initial_inventory_size[i]; j++)
10520               {
10521                 int element = level.initial_inventory_content[i][j];
10522                 int collect_count = element_info[element].collect_count_initial;
10523
10524                 if (!IS_CUSTOM_ELEMENT(element))
10525                   collect_count = 1;
10526
10527                 if (collect_count == 0)
10528                   player->inventory_infinite_element = element;
10529                 else
10530                   for (k = 0; k < collect_count; k++)
10531                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10532                       player->inventory_element[player->inventory_size++] =
10533                         element;
10534               }
10535             }
10536           }
10537         }
10538       }
10539
10540       break;
10541     }
10542
10543     // ---------- CE actions  -------------------------------------------------
10544
10545     case CA_SET_CE_VALUE:
10546     {
10547       int last_ce_value = CustomValue[x][y];
10548
10549       CustomValue[x][y] = action_arg_number_new;
10550
10551       if (CustomValue[x][y] != last_ce_value)
10552       {
10553         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10554         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10555
10556         if (CustomValue[x][y] == 0)
10557         {
10558           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10559           ChangeCount[x][y] = 0;        // allow at least one more change
10560
10561           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10562           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10563         }
10564       }
10565
10566       break;
10567     }
10568
10569     case CA_SET_CE_SCORE:
10570     {
10571       int last_ce_score = ei->collect_score;
10572
10573       ei->collect_score = action_arg_number_new;
10574
10575       if (ei->collect_score != last_ce_score)
10576       {
10577         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10578         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10579
10580         if (ei->collect_score == 0)
10581         {
10582           int xx, yy;
10583
10584           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10585           ChangeCount[x][y] = 0;        // allow at least one more change
10586
10587           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10588           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10589
10590           /*
10591             This is a very special case that seems to be a mixture between
10592             CheckElementChange() and CheckTriggeredElementChange(): while
10593             the first one only affects single elements that are triggered
10594             directly, the second one affects multiple elements in the playfield
10595             that are triggered indirectly by another element. This is a third
10596             case: Changing the CE score always affects multiple identical CEs,
10597             so every affected CE must be checked, not only the single CE for
10598             which the CE score was changed in the first place (as every instance
10599             of that CE shares the same CE score, and therefore also can change)!
10600           */
10601           SCAN_PLAYFIELD(xx, yy)
10602           {
10603             if (Tile[xx][yy] == element)
10604               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10605                                  CE_SCORE_GETS_ZERO);
10606           }
10607         }
10608       }
10609
10610       break;
10611     }
10612
10613     case CA_SET_CE_ARTWORK:
10614     {
10615       int artwork_element = action_arg_element;
10616       boolean reset_frame = FALSE;
10617       int xx, yy;
10618
10619       if (action_arg == CA_ARG_ELEMENT_RESET)
10620         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10621                            element);
10622
10623       if (ei->gfx_element != artwork_element)
10624         reset_frame = TRUE;
10625
10626       ei->gfx_element = artwork_element;
10627
10628       SCAN_PLAYFIELD(xx, yy)
10629       {
10630         if (Tile[xx][yy] == element)
10631         {
10632           if (reset_frame)
10633           {
10634             ResetGfxAnimation(xx, yy);
10635             ResetRandomAnimationValue(xx, yy);
10636           }
10637
10638           TEST_DrawLevelField(xx, yy);
10639         }
10640       }
10641
10642       break;
10643     }
10644
10645     // ---------- engine actions  ---------------------------------------------
10646
10647     case CA_SET_ENGINE_SCAN_MODE:
10648     {
10649       InitPlayfieldScanMode(action_arg);
10650
10651       break;
10652     }
10653
10654     default:
10655       break;
10656   }
10657 }
10658
10659 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10660 {
10661   int old_element = Tile[x][y];
10662   int new_element = GetElementFromGroupElement(element);
10663   int previous_move_direction = MovDir[x][y];
10664   int last_ce_value = CustomValue[x][y];
10665   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10666   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10667   boolean add_player_onto_element = (new_element_is_player &&
10668                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10669                                      IS_WALKABLE(old_element));
10670
10671   if (!add_player_onto_element)
10672   {
10673     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10674       RemoveMovingField(x, y);
10675     else
10676       RemoveField(x, y);
10677
10678     Tile[x][y] = new_element;
10679
10680     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10681       MovDir[x][y] = previous_move_direction;
10682
10683     if (element_info[new_element].use_last_ce_value)
10684       CustomValue[x][y] = last_ce_value;
10685
10686     InitField_WithBug1(x, y, FALSE);
10687
10688     new_element = Tile[x][y];   // element may have changed
10689
10690     ResetGfxAnimation(x, y);
10691     ResetRandomAnimationValue(x, y);
10692
10693     TEST_DrawLevelField(x, y);
10694
10695     if (GFX_CRUMBLED(new_element))
10696       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10697
10698     if (old_element == EL_EXPLOSION)
10699     {
10700       Store[x][y] = Store2[x][y] = 0;
10701
10702       // check if new element replaces an exploding player, requiring cleanup
10703       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10704         StorePlayer[x][y] = 0;
10705     }
10706
10707     // check if element under the player changes from accessible to unaccessible
10708     // (needed for special case of dropping element which then changes)
10709     // (must be checked after creating new element for walkable group elements)
10710     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10711         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10712     {
10713       KillPlayer(PLAYERINFO(x, y));
10714
10715       return;
10716     }
10717   }
10718
10719   // "ChangeCount" not set yet to allow "entered by player" change one time
10720   if (new_element_is_player)
10721     RelocatePlayer(x, y, new_element);
10722
10723   if (is_change)
10724     ChangeCount[x][y]++;        // count number of changes in the same frame
10725
10726   TestIfBadThingTouchesPlayer(x, y);
10727   TestIfPlayerTouchesCustomElement(x, y);
10728   TestIfElementTouchesCustomElement(x, y);
10729 }
10730
10731 static void CreateField(int x, int y, int element)
10732 {
10733   CreateFieldExt(x, y, element, FALSE);
10734 }
10735
10736 static void CreateElementFromChange(int x, int y, int element)
10737 {
10738   element = GET_VALID_RUNTIME_ELEMENT(element);
10739
10740   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10741   {
10742     int old_element = Tile[x][y];
10743
10744     // prevent changed element from moving in same engine frame
10745     // unless both old and new element can either fall or move
10746     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10747         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10748       Stop[x][y] = TRUE;
10749   }
10750
10751   CreateFieldExt(x, y, element, TRUE);
10752 }
10753
10754 static boolean ChangeElement(int x, int y, int element, int page)
10755 {
10756   struct ElementInfo *ei = &element_info[element];
10757   struct ElementChangeInfo *change = &ei->change_page[page];
10758   int ce_value = CustomValue[x][y];
10759   int ce_score = ei->collect_score;
10760   int target_element;
10761   int old_element = Tile[x][y];
10762
10763   // always use default change event to prevent running into a loop
10764   if (ChangeEvent[x][y] == -1)
10765     ChangeEvent[x][y] = CE_DELAY;
10766
10767   if (ChangeEvent[x][y] == CE_DELAY)
10768   {
10769     // reset actual trigger element, trigger player and action element
10770     change->actual_trigger_element = EL_EMPTY;
10771     change->actual_trigger_player = EL_EMPTY;
10772     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10773     change->actual_trigger_side = CH_SIDE_NONE;
10774     change->actual_trigger_ce_value = 0;
10775     change->actual_trigger_ce_score = 0;
10776     change->actual_trigger_x = -1;
10777     change->actual_trigger_y = -1;
10778   }
10779
10780   // do not change elements more than a specified maximum number of changes
10781   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10782     return FALSE;
10783
10784   ChangeCount[x][y]++;          // count number of changes in the same frame
10785
10786   if (ei->has_anim_event)
10787     HandleGlobalAnimEventByElementChange(element, page, x, y,
10788                                          change->actual_trigger_x,
10789                                          change->actual_trigger_y);
10790
10791   if (change->explode)
10792   {
10793     Bang(x, y);
10794
10795     return TRUE;
10796   }
10797
10798   if (change->use_target_content)
10799   {
10800     boolean complete_replace = TRUE;
10801     boolean can_replace[3][3];
10802     int xx, yy;
10803
10804     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10805     {
10806       boolean is_empty;
10807       boolean is_walkable;
10808       boolean is_diggable;
10809       boolean is_collectible;
10810       boolean is_removable;
10811       boolean is_destructible;
10812       int ex = x + xx - 1;
10813       int ey = y + yy - 1;
10814       int content_element = change->target_content.e[xx][yy];
10815       int e;
10816
10817       can_replace[xx][yy] = TRUE;
10818
10819       if (ex == x && ey == y)   // do not check changing element itself
10820         continue;
10821
10822       if (content_element == EL_EMPTY_SPACE)
10823       {
10824         can_replace[xx][yy] = FALSE;    // do not replace border with space
10825
10826         continue;
10827       }
10828
10829       if (!IN_LEV_FIELD(ex, ey))
10830       {
10831         can_replace[xx][yy] = FALSE;
10832         complete_replace = FALSE;
10833
10834         continue;
10835       }
10836
10837       e = Tile[ex][ey];
10838
10839       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10840         e = MovingOrBlocked2Element(ex, ey);
10841
10842       is_empty = (IS_FREE(ex, ey) ||
10843                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10844
10845       is_walkable     = (is_empty || IS_WALKABLE(e));
10846       is_diggable     = (is_empty || IS_DIGGABLE(e));
10847       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10848       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10849       is_removable    = (is_diggable || is_collectible);
10850
10851       can_replace[xx][yy] =
10852         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10853           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10854           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10855           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10856           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10857           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10858          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10859
10860       if (!can_replace[xx][yy])
10861         complete_replace = FALSE;
10862     }
10863
10864     if (!change->only_if_complete || complete_replace)
10865     {
10866       boolean something_has_changed = FALSE;
10867
10868       if (change->only_if_complete && change->use_random_replace &&
10869           RND(100) < change->random_percentage)
10870         return FALSE;
10871
10872       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10873       {
10874         int ex = x + xx - 1;
10875         int ey = y + yy - 1;
10876         int content_element;
10877
10878         if (can_replace[xx][yy] && (!change->use_random_replace ||
10879                                     RND(100) < change->random_percentage))
10880         {
10881           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10882             RemoveMovingField(ex, ey);
10883
10884           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10885
10886           content_element = change->target_content.e[xx][yy];
10887           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10888                                               ce_value, ce_score);
10889
10890           CreateElementFromChange(ex, ey, target_element);
10891
10892           something_has_changed = TRUE;
10893
10894           // for symmetry reasons, freeze newly created border elements
10895           if (ex != x || ey != y)
10896             Stop[ex][ey] = TRUE;        // no more moving in this frame
10897         }
10898       }
10899
10900       if (something_has_changed)
10901       {
10902         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10903         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10904       }
10905     }
10906   }
10907   else
10908   {
10909     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10910                                         ce_value, ce_score);
10911
10912     if (element == EL_DIAGONAL_GROWING ||
10913         element == EL_DIAGONAL_SHRINKING)
10914     {
10915       target_element = Store[x][y];
10916
10917       Store[x][y] = EL_EMPTY;
10918     }
10919
10920     // special case: element changes to player (and may be kept if walkable)
10921     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10922       CreateElementFromChange(x, y, EL_EMPTY);
10923
10924     CreateElementFromChange(x, y, target_element);
10925
10926     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10927     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10928   }
10929
10930   // this uses direct change before indirect change
10931   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10932
10933   return TRUE;
10934 }
10935
10936 static void HandleElementChange(int x, int y, int page)
10937 {
10938   int element = MovingOrBlocked2Element(x, y);
10939   struct ElementInfo *ei = &element_info[element];
10940   struct ElementChangeInfo *change = &ei->change_page[page];
10941   boolean handle_action_before_change = FALSE;
10942
10943 #ifdef DEBUG
10944   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10945       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10946   {
10947     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10948           x, y, element, element_info[element].token_name);
10949     Debug("game:playing:HandleElementChange", "This should never happen!");
10950   }
10951 #endif
10952
10953   // this can happen with classic bombs on walkable, changing elements
10954   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10955   {
10956     return;
10957   }
10958
10959   if (ChangeDelay[x][y] == 0)           // initialize element change
10960   {
10961     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10962
10963     if (change->can_change)
10964     {
10965       // !!! not clear why graphic animation should be reset at all here !!!
10966       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10967       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10968
10969       /*
10970         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10971
10972         When using an animation frame delay of 1 (this only happens with
10973         "sp_zonk.moving.left/right" in the classic graphics), the default
10974         (non-moving) animation shows wrong animation frames (while the
10975         moving animation, like "sp_zonk.moving.left/right", is correct,
10976         so this graphical bug never shows up with the classic graphics).
10977         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10978         be drawn instead of the correct frames 0,1,2,3. This is caused by
10979         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10980         an element change: First when the change delay ("ChangeDelay[][]")
10981         counter has reached zero after decrementing, then a second time in
10982         the next frame (after "GfxFrame[][]" was already incremented) when
10983         "ChangeDelay[][]" is reset to the initial delay value again.
10984
10985         This causes frame 0 to be drawn twice, while the last frame won't
10986         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10987
10988         As some animations may already be cleverly designed around this bug
10989         (at least the "Snake Bite" snake tail animation does this), it cannot
10990         simply be fixed here without breaking such existing animations.
10991         Unfortunately, it cannot easily be detected if a graphics set was
10992         designed "before" or "after" the bug was fixed. As a workaround,
10993         a new graphics set option "game.graphics_engine_version" was added
10994         to be able to specify the game's major release version for which the
10995         graphics set was designed, which can then be used to decide if the
10996         bugfix should be used (version 4 and above) or not (version 3 or
10997         below, or if no version was specified at all, as with old sets).
10998
10999         (The wrong/fixed animation frames can be tested with the test level set
11000         "test_gfxframe" and level "000", which contains a specially prepared
11001         custom element at level position (x/y) == (11/9) which uses the zonk
11002         animation mentioned above. Using "game.graphics_engine_version: 4"
11003         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11004         This can also be seen from the debug output for this test element.)
11005       */
11006
11007       // when a custom element is about to change (for example by change delay),
11008       // do not reset graphic animation when the custom element is moving
11009       if (game.graphics_engine_version < 4 &&
11010           !IS_MOVING(x, y))
11011       {
11012         ResetGfxAnimation(x, y);
11013         ResetRandomAnimationValue(x, y);
11014       }
11015
11016       if (change->pre_change_function)
11017         change->pre_change_function(x, y);
11018     }
11019   }
11020
11021   ChangeDelay[x][y]--;
11022
11023   if (ChangeDelay[x][y] != 0)           // continue element change
11024   {
11025     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11026
11027     // also needed if CE can not change, but has CE delay with CE action
11028     if (IS_ANIMATED(graphic))
11029       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11030
11031     if (change->can_change)
11032     {
11033       if (change->change_function)
11034         change->change_function(x, y);
11035     }
11036   }
11037   else                                  // finish element change
11038   {
11039     if (ChangePage[x][y] != -1)         // remember page from delayed change
11040     {
11041       page = ChangePage[x][y];
11042       ChangePage[x][y] = -1;
11043
11044       change = &ei->change_page[page];
11045     }
11046
11047     if (IS_MOVING(x, y))                // never change a running system ;-)
11048     {
11049       ChangeDelay[x][y] = 1;            // try change after next move step
11050       ChangePage[x][y] = page;          // remember page to use for change
11051
11052       return;
11053     }
11054
11055     // special case: set new level random seed before changing element
11056     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11057       handle_action_before_change = TRUE;
11058
11059     if (change->has_action && handle_action_before_change)
11060       ExecuteCustomElementAction(x, y, element, page);
11061
11062     if (change->can_change)
11063     {
11064       if (ChangeElement(x, y, element, page))
11065       {
11066         if (change->post_change_function)
11067           change->post_change_function(x, y);
11068       }
11069     }
11070
11071     if (change->has_action && !handle_action_before_change)
11072       ExecuteCustomElementAction(x, y, element, page);
11073   }
11074 }
11075
11076 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11077                                               int trigger_element,
11078                                               int trigger_event,
11079                                               int trigger_player,
11080                                               int trigger_side,
11081                                               int trigger_page)
11082 {
11083   boolean change_done_any = FALSE;
11084   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11085   int i;
11086
11087   if (!(trigger_events[trigger_element][trigger_event]))
11088     return FALSE;
11089
11090   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11091
11092   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11093   {
11094     int element = EL_CUSTOM_START + i;
11095     boolean change_done = FALSE;
11096     int p;
11097
11098     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11099         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11100       continue;
11101
11102     for (p = 0; p < element_info[element].num_change_pages; p++)
11103     {
11104       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11105
11106       if (change->can_change_or_has_action &&
11107           change->has_event[trigger_event] &&
11108           change->trigger_side & trigger_side &&
11109           change->trigger_player & trigger_player &&
11110           change->trigger_page & trigger_page_bits &&
11111           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11112       {
11113         change->actual_trigger_element = trigger_element;
11114         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11115         change->actual_trigger_player_bits = trigger_player;
11116         change->actual_trigger_side = trigger_side;
11117         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11118         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11119         change->actual_trigger_x = trigger_x;
11120         change->actual_trigger_y = trigger_y;
11121
11122         if ((change->can_change && !change_done) || change->has_action)
11123         {
11124           int x, y;
11125
11126           SCAN_PLAYFIELD(x, y)
11127           {
11128             if (Tile[x][y] == element)
11129             {
11130               if (change->can_change && !change_done)
11131               {
11132                 // if element already changed in this frame, not only prevent
11133                 // another element change (checked in ChangeElement()), but
11134                 // also prevent additional element actions for this element
11135
11136                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11137                     !level.use_action_after_change_bug)
11138                   continue;
11139
11140                 ChangeDelay[x][y] = 1;
11141                 ChangeEvent[x][y] = trigger_event;
11142
11143                 HandleElementChange(x, y, p);
11144               }
11145               else if (change->has_action)
11146               {
11147                 // if element already changed in this frame, not only prevent
11148                 // another element change (checked in ChangeElement()), but
11149                 // also prevent additional element actions for this element
11150
11151                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11152                     !level.use_action_after_change_bug)
11153                   continue;
11154
11155                 ExecuteCustomElementAction(x, y, element, p);
11156                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11157               }
11158             }
11159           }
11160
11161           if (change->can_change)
11162           {
11163             change_done = TRUE;
11164             change_done_any = TRUE;
11165           }
11166         }
11167       }
11168     }
11169   }
11170
11171   RECURSION_LOOP_DETECTION_END();
11172
11173   return change_done_any;
11174 }
11175
11176 static boolean CheckElementChangeExt(int x, int y,
11177                                      int element,
11178                                      int trigger_element,
11179                                      int trigger_event,
11180                                      int trigger_player,
11181                                      int trigger_side)
11182 {
11183   boolean change_done = FALSE;
11184   int p;
11185
11186   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11187       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11188     return FALSE;
11189
11190   if (Tile[x][y] == EL_BLOCKED)
11191   {
11192     Blocked2Moving(x, y, &x, &y);
11193     element = Tile[x][y];
11194   }
11195
11196   // check if element has already changed or is about to change after moving
11197   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11198        Tile[x][y] != element) ||
11199
11200       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11201        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11202         ChangePage[x][y] != -1)))
11203     return FALSE;
11204
11205   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11206
11207   for (p = 0; p < element_info[element].num_change_pages; p++)
11208   {
11209     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11210
11211     /* check trigger element for all events where the element that is checked
11212        for changing interacts with a directly adjacent element -- this is
11213        different to element changes that affect other elements to change on the
11214        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11215     boolean check_trigger_element =
11216       (trigger_event == CE_NEXT_TO_X ||
11217        trigger_event == CE_TOUCHING_X ||
11218        trigger_event == CE_HITTING_X ||
11219        trigger_event == CE_HIT_BY_X ||
11220        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11221
11222     if (change->can_change_or_has_action &&
11223         change->has_event[trigger_event] &&
11224         change->trigger_side & trigger_side &&
11225         change->trigger_player & trigger_player &&
11226         (!check_trigger_element ||
11227          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11228     {
11229       change->actual_trigger_element = trigger_element;
11230       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11231       change->actual_trigger_player_bits = trigger_player;
11232       change->actual_trigger_side = trigger_side;
11233       change->actual_trigger_ce_value = CustomValue[x][y];
11234       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11235       change->actual_trigger_x = x;
11236       change->actual_trigger_y = y;
11237
11238       // special case: trigger element not at (x,y) position for some events
11239       if (check_trigger_element)
11240       {
11241         static struct
11242         {
11243           int dx, dy;
11244         } move_xy[] =
11245           {
11246             {  0,  0 },
11247             { -1,  0 },
11248             { +1,  0 },
11249             {  0,  0 },
11250             {  0, -1 },
11251             {  0,  0 }, { 0, 0 }, { 0, 0 },
11252             {  0, +1 }
11253           };
11254
11255         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11256         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11257
11258         change->actual_trigger_ce_value = CustomValue[xx][yy];
11259         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11260         change->actual_trigger_x = xx;
11261         change->actual_trigger_y = yy;
11262       }
11263
11264       if (change->can_change && !change_done)
11265       {
11266         ChangeDelay[x][y] = 1;
11267         ChangeEvent[x][y] = trigger_event;
11268
11269         HandleElementChange(x, y, p);
11270
11271         change_done = TRUE;
11272       }
11273       else if (change->has_action)
11274       {
11275         ExecuteCustomElementAction(x, y, element, p);
11276         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11277       }
11278     }
11279   }
11280
11281   RECURSION_LOOP_DETECTION_END();
11282
11283   return change_done;
11284 }
11285
11286 static void PlayPlayerSound(struct PlayerInfo *player)
11287 {
11288   int jx = player->jx, jy = player->jy;
11289   int sound_element = player->artwork_element;
11290   int last_action = player->last_action_waiting;
11291   int action = player->action_waiting;
11292
11293   if (player->is_waiting)
11294   {
11295     if (action != last_action)
11296       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11297     else
11298       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11299   }
11300   else
11301   {
11302     if (action != last_action)
11303       StopSound(element_info[sound_element].sound[last_action]);
11304
11305     if (last_action == ACTION_SLEEPING)
11306       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11307   }
11308 }
11309
11310 static void PlayAllPlayersSound(void)
11311 {
11312   int i;
11313
11314   for (i = 0; i < MAX_PLAYERS; i++)
11315     if (stored_player[i].active)
11316       PlayPlayerSound(&stored_player[i]);
11317 }
11318
11319 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11320 {
11321   boolean last_waiting = player->is_waiting;
11322   int move_dir = player->MovDir;
11323
11324   player->dir_waiting = move_dir;
11325   player->last_action_waiting = player->action_waiting;
11326
11327   if (is_waiting)
11328   {
11329     if (!last_waiting)          // not waiting -> waiting
11330     {
11331       player->is_waiting = TRUE;
11332
11333       player->frame_counter_bored =
11334         FrameCounter +
11335         game.player_boring_delay_fixed +
11336         GetSimpleRandom(game.player_boring_delay_random);
11337       player->frame_counter_sleeping =
11338         FrameCounter +
11339         game.player_sleeping_delay_fixed +
11340         GetSimpleRandom(game.player_sleeping_delay_random);
11341
11342       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11343     }
11344
11345     if (game.player_sleeping_delay_fixed +
11346         game.player_sleeping_delay_random > 0 &&
11347         player->anim_delay_counter == 0 &&
11348         player->post_delay_counter == 0 &&
11349         FrameCounter >= player->frame_counter_sleeping)
11350       player->is_sleeping = TRUE;
11351     else if (game.player_boring_delay_fixed +
11352              game.player_boring_delay_random > 0 &&
11353              FrameCounter >= player->frame_counter_bored)
11354       player->is_bored = TRUE;
11355
11356     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11357                               player->is_bored ? ACTION_BORING :
11358                               ACTION_WAITING);
11359
11360     if (player->is_sleeping && player->use_murphy)
11361     {
11362       // special case for sleeping Murphy when leaning against non-free tile
11363
11364       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11365           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11366            !IS_MOVING(player->jx - 1, player->jy)))
11367         move_dir = MV_LEFT;
11368       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11369                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11370                 !IS_MOVING(player->jx + 1, player->jy)))
11371         move_dir = MV_RIGHT;
11372       else
11373         player->is_sleeping = FALSE;
11374
11375       player->dir_waiting = move_dir;
11376     }
11377
11378     if (player->is_sleeping)
11379     {
11380       if (player->num_special_action_sleeping > 0)
11381       {
11382         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11383         {
11384           int last_special_action = player->special_action_sleeping;
11385           int num_special_action = player->num_special_action_sleeping;
11386           int special_action =
11387             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11388              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11389              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11390              last_special_action + 1 : ACTION_SLEEPING);
11391           int special_graphic =
11392             el_act_dir2img(player->artwork_element, special_action, move_dir);
11393
11394           player->anim_delay_counter =
11395             graphic_info[special_graphic].anim_delay_fixed +
11396             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11397           player->post_delay_counter =
11398             graphic_info[special_graphic].post_delay_fixed +
11399             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11400
11401           player->special_action_sleeping = special_action;
11402         }
11403
11404         if (player->anim_delay_counter > 0)
11405         {
11406           player->action_waiting = player->special_action_sleeping;
11407           player->anim_delay_counter--;
11408         }
11409         else if (player->post_delay_counter > 0)
11410         {
11411           player->post_delay_counter--;
11412         }
11413       }
11414     }
11415     else if (player->is_bored)
11416     {
11417       if (player->num_special_action_bored > 0)
11418       {
11419         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11420         {
11421           int special_action =
11422             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11423           int special_graphic =
11424             el_act_dir2img(player->artwork_element, special_action, move_dir);
11425
11426           player->anim_delay_counter =
11427             graphic_info[special_graphic].anim_delay_fixed +
11428             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11429           player->post_delay_counter =
11430             graphic_info[special_graphic].post_delay_fixed +
11431             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11432
11433           player->special_action_bored = special_action;
11434         }
11435
11436         if (player->anim_delay_counter > 0)
11437         {
11438           player->action_waiting = player->special_action_bored;
11439           player->anim_delay_counter--;
11440         }
11441         else if (player->post_delay_counter > 0)
11442         {
11443           player->post_delay_counter--;
11444         }
11445       }
11446     }
11447   }
11448   else if (last_waiting)        // waiting -> not waiting
11449   {
11450     player->is_waiting = FALSE;
11451     player->is_bored = FALSE;
11452     player->is_sleeping = FALSE;
11453
11454     player->frame_counter_bored = -1;
11455     player->frame_counter_sleeping = -1;
11456
11457     player->anim_delay_counter = 0;
11458     player->post_delay_counter = 0;
11459
11460     player->dir_waiting = player->MovDir;
11461     player->action_waiting = ACTION_DEFAULT;
11462
11463     player->special_action_bored = ACTION_DEFAULT;
11464     player->special_action_sleeping = ACTION_DEFAULT;
11465   }
11466 }
11467
11468 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11469 {
11470   if ((!player->is_moving  && player->was_moving) ||
11471       (player->MovPos == 0 && player->was_moving) ||
11472       (player->is_snapping && !player->was_snapping) ||
11473       (player->is_dropping && !player->was_dropping))
11474   {
11475     if (!CheckSaveEngineSnapshotToList())
11476       return;
11477
11478     player->was_moving = FALSE;
11479     player->was_snapping = TRUE;
11480     player->was_dropping = TRUE;
11481   }
11482   else
11483   {
11484     if (player->is_moving)
11485       player->was_moving = TRUE;
11486
11487     if (!player->is_snapping)
11488       player->was_snapping = FALSE;
11489
11490     if (!player->is_dropping)
11491       player->was_dropping = FALSE;
11492   }
11493
11494   static struct MouseActionInfo mouse_action_last = { 0 };
11495   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11496   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11497
11498   if (new_released)
11499     CheckSaveEngineSnapshotToList();
11500
11501   mouse_action_last = mouse_action;
11502 }
11503
11504 static void CheckSingleStepMode(struct PlayerInfo *player)
11505 {
11506   if (tape.single_step && tape.recording && !tape.pausing)
11507   {
11508     // as it is called "single step mode", just return to pause mode when the
11509     // player stopped moving after one tile (or never starts moving at all)
11510     // (reverse logic needed here in case single step mode used in team mode)
11511     if (player->is_moving ||
11512         player->is_pushing ||
11513         player->is_dropping_pressed ||
11514         player->effective_mouse_action.button)
11515       game.enter_single_step_mode = FALSE;
11516   }
11517
11518   CheckSaveEngineSnapshot(player);
11519 }
11520
11521 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11522 {
11523   int left      = player_action & JOY_LEFT;
11524   int right     = player_action & JOY_RIGHT;
11525   int up        = player_action & JOY_UP;
11526   int down      = player_action & JOY_DOWN;
11527   int button1   = player_action & JOY_BUTTON_1;
11528   int button2   = player_action & JOY_BUTTON_2;
11529   int dx        = (left ? -1 : right ? 1 : 0);
11530   int dy        = (up   ? -1 : down  ? 1 : 0);
11531
11532   if (!player->active || tape.pausing)
11533     return 0;
11534
11535   if (player_action)
11536   {
11537     if (button1)
11538       SnapField(player, dx, dy);
11539     else
11540     {
11541       if (button2)
11542         DropElement(player);
11543
11544       MovePlayer(player, dx, dy);
11545     }
11546
11547     CheckSingleStepMode(player);
11548
11549     SetPlayerWaiting(player, FALSE);
11550
11551     return player_action;
11552   }
11553   else
11554   {
11555     // no actions for this player (no input at player's configured device)
11556
11557     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11558     SnapField(player, 0, 0);
11559     CheckGravityMovementWhenNotMoving(player);
11560
11561     if (player->MovPos == 0)
11562       SetPlayerWaiting(player, TRUE);
11563
11564     if (player->MovPos == 0)    // needed for tape.playing
11565       player->is_moving = FALSE;
11566
11567     player->is_dropping = FALSE;
11568     player->is_dropping_pressed = FALSE;
11569     player->drop_pressed_delay = 0;
11570
11571     CheckSingleStepMode(player);
11572
11573     return 0;
11574   }
11575 }
11576
11577 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11578                                          byte *tape_action)
11579 {
11580   if (!tape.use_mouse_actions)
11581     return;
11582
11583   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11584   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11585   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11586 }
11587
11588 static void SetTapeActionFromMouseAction(byte *tape_action,
11589                                          struct MouseActionInfo *mouse_action)
11590 {
11591   if (!tape.use_mouse_actions)
11592     return;
11593
11594   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11595   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11596   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11597 }
11598
11599 static void CheckLevelSolved(void)
11600 {
11601   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11602   {
11603     if (game_em.level_solved &&
11604         !game_em.game_over)                             // game won
11605     {
11606       LevelSolved();
11607
11608       game_em.game_over = TRUE;
11609
11610       game.all_players_gone = TRUE;
11611     }
11612
11613     if (game_em.game_over)                              // game lost
11614       game.all_players_gone = TRUE;
11615   }
11616   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11617   {
11618     if (game_sp.level_solved &&
11619         !game_sp.game_over)                             // game won
11620     {
11621       LevelSolved();
11622
11623       game_sp.game_over = TRUE;
11624
11625       game.all_players_gone = TRUE;
11626     }
11627
11628     if (game_sp.game_over)                              // game lost
11629       game.all_players_gone = TRUE;
11630   }
11631   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11632   {
11633     if (game_mm.level_solved &&
11634         !game_mm.game_over)                             // game won
11635     {
11636       LevelSolved();
11637
11638       game_mm.game_over = TRUE;
11639
11640       game.all_players_gone = TRUE;
11641     }
11642
11643     if (game_mm.game_over)                              // game lost
11644       game.all_players_gone = TRUE;
11645   }
11646 }
11647
11648 static void CheckLevelTime_StepCounter(void)
11649 {
11650   int i;
11651
11652   TimePlayed++;
11653
11654   if (TimeLeft > 0)
11655   {
11656     TimeLeft--;
11657
11658     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11659       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11660
11661     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11662
11663     DisplayGameControlValues();
11664
11665     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11666       for (i = 0; i < MAX_PLAYERS; i++)
11667         KillPlayer(&stored_player[i]);
11668   }
11669   else if (game.no_level_time_limit && !game.all_players_gone)
11670   {
11671     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11672
11673     DisplayGameControlValues();
11674   }
11675 }
11676
11677 static void CheckLevelTime(void)
11678 {
11679   int i;
11680
11681   if (TimeFrames >= FRAMES_PER_SECOND)
11682   {
11683     TimeFrames = 0;
11684
11685     for (i = 0; i < MAX_PLAYERS; i++)
11686     {
11687       struct PlayerInfo *player = &stored_player[i];
11688
11689       if (SHIELD_ON(player))
11690       {
11691         player->shield_normal_time_left--;
11692
11693         if (player->shield_deadly_time_left > 0)
11694           player->shield_deadly_time_left--;
11695       }
11696     }
11697
11698     if (!game.LevelSolved && !level.use_step_counter)
11699     {
11700       TimePlayed++;
11701
11702       if (TimeLeft > 0)
11703       {
11704         TimeLeft--;
11705
11706         if (TimeLeft <= 10 && game.time_limit)
11707           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11708
11709         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11710            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11711
11712         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11713
11714         if (!TimeLeft && game.time_limit)
11715         {
11716           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11717             game_em.lev->killed_out_of_time = TRUE;
11718           else
11719             for (i = 0; i < MAX_PLAYERS; i++)
11720               KillPlayer(&stored_player[i]);
11721         }
11722       }
11723       else if (game.no_level_time_limit && !game.all_players_gone)
11724       {
11725         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11726       }
11727
11728       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11729     }
11730   }
11731
11732   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11733   {
11734     TapeTimeFrames = 0;
11735     TapeTime++;
11736
11737     if (tape.recording || tape.playing)
11738       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11739   }
11740
11741   if (tape.recording || tape.playing)
11742     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11743
11744   UpdateAndDisplayGameControlValues();
11745 }
11746
11747 void AdvanceFrameAndPlayerCounters(int player_nr)
11748 {
11749   int i;
11750
11751   // advance frame counters (global frame counter and tape time frame counter)
11752   FrameCounter++;
11753   TapeTimeFrames++;
11754
11755   // advance time frame counter (used to control available time to solve level)
11756   TimeFrames++;
11757
11758   // advance player counters (counters for move delay, move animation etc.)
11759   for (i = 0; i < MAX_PLAYERS; i++)
11760   {
11761     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11762     int move_delay_value = stored_player[i].move_delay_value;
11763     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11764
11765     if (!advance_player_counters)       // not all players may be affected
11766       continue;
11767
11768     if (move_frames == 0)       // less than one move per game frame
11769     {
11770       int stepsize = TILEX / move_delay_value;
11771       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11772       int count = (stored_player[i].is_moving ?
11773                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11774
11775       if (count % delay == 0)
11776         move_frames = 1;
11777     }
11778
11779     stored_player[i].Frame += move_frames;
11780
11781     if (stored_player[i].MovPos != 0)
11782       stored_player[i].StepFrame += move_frames;
11783
11784     if (stored_player[i].move_delay > 0)
11785       stored_player[i].move_delay--;
11786
11787     // due to bugs in previous versions, counter must count up, not down
11788     if (stored_player[i].push_delay != -1)
11789       stored_player[i].push_delay++;
11790
11791     if (stored_player[i].drop_delay > 0)
11792       stored_player[i].drop_delay--;
11793
11794     if (stored_player[i].is_dropping_pressed)
11795       stored_player[i].drop_pressed_delay++;
11796   }
11797 }
11798
11799 void AdvanceFrameCounter(void)
11800 {
11801   FrameCounter++;
11802 }
11803
11804 void AdvanceGfxFrame(void)
11805 {
11806   int x, y;
11807
11808   SCAN_PLAYFIELD(x, y)
11809   {
11810     GfxFrame[x][y]++;
11811   }
11812 }
11813
11814 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11815                               struct MouseActionInfo *mouse_action_last)
11816 {
11817   if (mouse_action->button)
11818   {
11819     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11820     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11821     int x = mouse_action->lx;
11822     int y = mouse_action->ly;
11823     int element = Tile[x][y];
11824
11825     if (new_button)
11826     {
11827       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11828       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11829                                          ch_button);
11830     }
11831
11832     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11833     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11834                                        ch_button);
11835
11836     if (level.use_step_counter)
11837     {
11838       boolean counted_click = FALSE;
11839
11840       // element clicked that can change when clicked/pressed
11841       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11842           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11843            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11844         counted_click = TRUE;
11845
11846       // element clicked that can trigger change when clicked/pressed
11847       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11848           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11849         counted_click = TRUE;
11850
11851       if (new_button && counted_click)
11852         CheckLevelTime_StepCounter();
11853     }
11854   }
11855 }
11856
11857 void StartGameActions(boolean init_network_game, boolean record_tape,
11858                       int random_seed)
11859 {
11860   unsigned int new_random_seed = InitRND(random_seed);
11861
11862   if (record_tape)
11863     TapeStartRecording(new_random_seed);
11864
11865   if (setup.auto_pause_on_start && !tape.pausing)
11866     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11867
11868   if (init_network_game)
11869   {
11870     SendToServer_LevelFile();
11871     SendToServer_StartPlaying();
11872
11873     return;
11874   }
11875
11876   InitGame();
11877 }
11878
11879 static void GameActionsExt(void)
11880 {
11881 #if 0
11882   static unsigned int game_frame_delay = 0;
11883 #endif
11884   unsigned int game_frame_delay_value;
11885   byte *recorded_player_action;
11886   byte summarized_player_action = 0;
11887   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11888   int i;
11889
11890   // detect endless loops, caused by custom element programming
11891   if (recursion_loop_detected && recursion_loop_depth == 0)
11892   {
11893     char *message = getStringCat3("Internal Error! Element ",
11894                                   EL_NAME(recursion_loop_element),
11895                                   " caused endless loop! Quit the game?");
11896
11897     Warn("element '%s' caused endless loop in game engine",
11898          EL_NAME(recursion_loop_element));
11899
11900     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11901
11902     recursion_loop_detected = FALSE;    // if game should be continued
11903
11904     free(message);
11905
11906     return;
11907   }
11908
11909   if (game.restart_level)
11910     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11911
11912   CheckLevelSolved();
11913
11914   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11915     GameWon();
11916
11917   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11918     TapeStop();
11919
11920   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11921     return;
11922
11923   game_frame_delay_value =
11924     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11925
11926   if (tape.playing && tape.warp_forward && !tape.pausing)
11927     game_frame_delay_value = 0;
11928
11929   SetVideoFrameDelay(game_frame_delay_value);
11930
11931   // (de)activate virtual buttons depending on current game status
11932   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11933   {
11934     if (game.all_players_gone)  // if no players there to be controlled anymore
11935       SetOverlayActive(FALSE);
11936     else if (!tape.playing)     // if game continues after tape stopped playing
11937       SetOverlayActive(TRUE);
11938   }
11939
11940 #if 0
11941 #if 0
11942   // ---------- main game synchronization point ----------
11943
11944   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11945
11946   Debug("game:playing:skip", "skip == %d", skip);
11947
11948 #else
11949   // ---------- main game synchronization point ----------
11950
11951   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11952 #endif
11953 #endif
11954
11955   if (network_playing && !network_player_action_received)
11956   {
11957     // try to get network player actions in time
11958
11959     // last chance to get network player actions without main loop delay
11960     HandleNetworking();
11961
11962     // game was quit by network peer
11963     if (game_status != GAME_MODE_PLAYING)
11964       return;
11965
11966     // check if network player actions still missing and game still running
11967     if (!network_player_action_received && !checkGameEnded())
11968       return;           // failed to get network player actions in time
11969
11970     // do not yet reset "network_player_action_received" (for tape.pausing)
11971   }
11972
11973   if (tape.pausing)
11974     return;
11975
11976   // at this point we know that we really continue executing the game
11977
11978   network_player_action_received = FALSE;
11979
11980   // when playing tape, read previously recorded player input from tape data
11981   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11982
11983   local_player->effective_mouse_action = local_player->mouse_action;
11984
11985   if (recorded_player_action != NULL)
11986     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11987                                  recorded_player_action);
11988
11989   // TapePlayAction() may return NULL when toggling to "pause before death"
11990   if (tape.pausing)
11991     return;
11992
11993   if (tape.set_centered_player)
11994   {
11995     game.centered_player_nr_next = tape.centered_player_nr_next;
11996     game.set_centered_player = TRUE;
11997   }
11998
11999   for (i = 0; i < MAX_PLAYERS; i++)
12000   {
12001     summarized_player_action |= stored_player[i].action;
12002
12003     if (!network_playing && (game.team_mode || tape.playing))
12004       stored_player[i].effective_action = stored_player[i].action;
12005   }
12006
12007   if (network_playing && !checkGameEnded())
12008     SendToServer_MovePlayer(summarized_player_action);
12009
12010   // summarize all actions at local players mapped input device position
12011   // (this allows using different input devices in single player mode)
12012   if (!network.enabled && !game.team_mode)
12013     stored_player[map_player_action[local_player->index_nr]].effective_action =
12014       summarized_player_action;
12015
12016   // summarize all actions at centered player in local team mode
12017   if (tape.recording &&
12018       setup.team_mode && !network.enabled &&
12019       setup.input_on_focus &&
12020       game.centered_player_nr != -1)
12021   {
12022     for (i = 0; i < MAX_PLAYERS; i++)
12023       stored_player[map_player_action[i]].effective_action =
12024         (i == game.centered_player_nr ? summarized_player_action : 0);
12025   }
12026
12027   if (recorded_player_action != NULL)
12028     for (i = 0; i < MAX_PLAYERS; i++)
12029       stored_player[i].effective_action = recorded_player_action[i];
12030
12031   for (i = 0; i < MAX_PLAYERS; i++)
12032   {
12033     tape_action[i] = stored_player[i].effective_action;
12034
12035     /* (this may happen in the RND game engine if a player was not present on
12036        the playfield on level start, but appeared later from a custom element */
12037     if (setup.team_mode &&
12038         tape.recording &&
12039         tape_action[i] &&
12040         !tape.player_participates[i])
12041       tape.player_participates[i] = TRUE;
12042   }
12043
12044   SetTapeActionFromMouseAction(tape_action,
12045                                &local_player->effective_mouse_action);
12046
12047   // only record actions from input devices, but not programmed actions
12048   if (tape.recording)
12049     TapeRecordAction(tape_action);
12050
12051   // remember if game was played (especially after tape stopped playing)
12052   if (!tape.playing && summarized_player_action && !checkGameFailed())
12053     game.GamePlayed = TRUE;
12054
12055 #if USE_NEW_PLAYER_ASSIGNMENTS
12056   // !!! also map player actions in single player mode !!!
12057   // if (game.team_mode)
12058   if (1)
12059   {
12060     byte mapped_action[MAX_PLAYERS];
12061
12062 #if DEBUG_PLAYER_ACTIONS
12063     for (i = 0; i < MAX_PLAYERS; i++)
12064       DebugContinued("", "%d, ", stored_player[i].effective_action);
12065 #endif
12066
12067     for (i = 0; i < MAX_PLAYERS; i++)
12068       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12069
12070     for (i = 0; i < MAX_PLAYERS; i++)
12071       stored_player[i].effective_action = mapped_action[i];
12072
12073 #if DEBUG_PLAYER_ACTIONS
12074     DebugContinued("", "=> ");
12075     for (i = 0; i < MAX_PLAYERS; i++)
12076       DebugContinued("", "%d, ", stored_player[i].effective_action);
12077     DebugContinued("game:playing:player", "\n");
12078 #endif
12079   }
12080 #if DEBUG_PLAYER_ACTIONS
12081   else
12082   {
12083     for (i = 0; i < MAX_PLAYERS; i++)
12084       DebugContinued("", "%d, ", stored_player[i].effective_action);
12085     DebugContinued("game:playing:player", "\n");
12086   }
12087 #endif
12088 #endif
12089
12090   for (i = 0; i < MAX_PLAYERS; i++)
12091   {
12092     // allow engine snapshot in case of changed movement attempt
12093     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12094         (stored_player[i].effective_action & KEY_MOTION))
12095       game.snapshot.changed_action = TRUE;
12096
12097     // allow engine snapshot in case of snapping/dropping attempt
12098     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12099         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12100       game.snapshot.changed_action = TRUE;
12101
12102     game.snapshot.last_action[i] = stored_player[i].effective_action;
12103   }
12104
12105   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12106   {
12107     GameActions_EM_Main();
12108   }
12109   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12110   {
12111     GameActions_SP_Main();
12112   }
12113   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12114   {
12115     GameActions_MM_Main();
12116   }
12117   else
12118   {
12119     GameActions_RND_Main();
12120   }
12121
12122   BlitScreenToBitmap(backbuffer);
12123
12124   CheckLevelSolved();
12125   CheckLevelTime();
12126
12127   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12128
12129   if (global.show_frames_per_second)
12130   {
12131     static unsigned int fps_counter = 0;
12132     static int fps_frames = 0;
12133     unsigned int fps_delay_ms = Counter() - fps_counter;
12134
12135     fps_frames++;
12136
12137     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12138     {
12139       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12140
12141       fps_frames = 0;
12142       fps_counter = Counter();
12143
12144       // always draw FPS to screen after FPS value was updated
12145       redraw_mask |= REDRAW_FPS;
12146     }
12147
12148     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12149     if (GetDrawDeactivationMask() == REDRAW_NONE)
12150       redraw_mask |= REDRAW_FPS;
12151   }
12152 }
12153
12154 static void GameActions_CheckSaveEngineSnapshot(void)
12155 {
12156   if (!game.snapshot.save_snapshot)
12157     return;
12158
12159   // clear flag for saving snapshot _before_ saving snapshot
12160   game.snapshot.save_snapshot = FALSE;
12161
12162   SaveEngineSnapshotToList();
12163 }
12164
12165 void GameActions(void)
12166 {
12167   GameActionsExt();
12168
12169   GameActions_CheckSaveEngineSnapshot();
12170 }
12171
12172 void GameActions_EM_Main(void)
12173 {
12174   byte effective_action[MAX_PLAYERS];
12175   int i;
12176
12177   for (i = 0; i < MAX_PLAYERS; i++)
12178     effective_action[i] = stored_player[i].effective_action;
12179
12180   GameActions_EM(effective_action);
12181 }
12182
12183 void GameActions_SP_Main(void)
12184 {
12185   byte effective_action[MAX_PLAYERS];
12186   int i;
12187
12188   for (i = 0; i < MAX_PLAYERS; i++)
12189     effective_action[i] = stored_player[i].effective_action;
12190
12191   GameActions_SP(effective_action);
12192
12193   for (i = 0; i < MAX_PLAYERS; i++)
12194   {
12195     if (stored_player[i].force_dropping)
12196       stored_player[i].action |= KEY_BUTTON_DROP;
12197
12198     stored_player[i].force_dropping = FALSE;
12199   }
12200 }
12201
12202 void GameActions_MM_Main(void)
12203 {
12204   AdvanceGfxFrame();
12205
12206   GameActions_MM(local_player->effective_mouse_action);
12207 }
12208
12209 void GameActions_RND_Main(void)
12210 {
12211   GameActions_RND();
12212 }
12213
12214 void GameActions_RND(void)
12215 {
12216   static struct MouseActionInfo mouse_action_last = { 0 };
12217   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12218   int magic_wall_x = 0, magic_wall_y = 0;
12219   int i, x, y, element, graphic, last_gfx_frame;
12220
12221   InitPlayfieldScanModeVars();
12222
12223   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12224   {
12225     SCAN_PLAYFIELD(x, y)
12226     {
12227       ChangeCount[x][y] = 0;
12228       ChangeEvent[x][y] = -1;
12229     }
12230   }
12231
12232   if (game.set_centered_player)
12233   {
12234     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12235
12236     // switching to "all players" only possible if all players fit to screen
12237     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12238     {
12239       game.centered_player_nr_next = game.centered_player_nr;
12240       game.set_centered_player = FALSE;
12241     }
12242
12243     // do not switch focus to non-existing (or non-active) player
12244     if (game.centered_player_nr_next >= 0 &&
12245         !stored_player[game.centered_player_nr_next].active)
12246     {
12247       game.centered_player_nr_next = game.centered_player_nr;
12248       game.set_centered_player = FALSE;
12249     }
12250   }
12251
12252   if (game.set_centered_player &&
12253       ScreenMovPos == 0)        // screen currently aligned at tile position
12254   {
12255     int sx, sy;
12256
12257     if (game.centered_player_nr_next == -1)
12258     {
12259       setScreenCenteredToAllPlayers(&sx, &sy);
12260     }
12261     else
12262     {
12263       sx = stored_player[game.centered_player_nr_next].jx;
12264       sy = stored_player[game.centered_player_nr_next].jy;
12265     }
12266
12267     game.centered_player_nr = game.centered_player_nr_next;
12268     game.set_centered_player = FALSE;
12269
12270     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12271     DrawGameDoorValues();
12272   }
12273
12274   // check single step mode (set flag and clear again if any player is active)
12275   game.enter_single_step_mode =
12276     (tape.single_step && tape.recording && !tape.pausing);
12277
12278   for (i = 0; i < MAX_PLAYERS; i++)
12279   {
12280     int actual_player_action = stored_player[i].effective_action;
12281
12282 #if 1
12283     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12284        - rnd_equinox_tetrachloride 048
12285        - rnd_equinox_tetrachloride_ii 096
12286        - rnd_emanuel_schmieg 002
12287        - doctor_sloan_ww 001, 020
12288     */
12289     if (stored_player[i].MovPos == 0)
12290       CheckGravityMovement(&stored_player[i]);
12291 #endif
12292
12293     // overwrite programmed action with tape action
12294     if (stored_player[i].programmed_action)
12295       actual_player_action = stored_player[i].programmed_action;
12296
12297     PlayerActions(&stored_player[i], actual_player_action);
12298
12299     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12300   }
12301
12302   // single step pause mode may already have been toggled by "ScrollPlayer()"
12303   if (game.enter_single_step_mode && !tape.pausing)
12304     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12305
12306   ScrollScreen(NULL, SCROLL_GO_ON);
12307
12308   /* for backwards compatibility, the following code emulates a fixed bug that
12309      occured when pushing elements (causing elements that just made their last
12310      pushing step to already (if possible) make their first falling step in the
12311      same game frame, which is bad); this code is also needed to use the famous
12312      "spring push bug" which is used in older levels and might be wanted to be
12313      used also in newer levels, but in this case the buggy pushing code is only
12314      affecting the "spring" element and no other elements */
12315
12316   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12317   {
12318     for (i = 0; i < MAX_PLAYERS; i++)
12319     {
12320       struct PlayerInfo *player = &stored_player[i];
12321       int x = player->jx;
12322       int y = player->jy;
12323
12324       if (player->active && player->is_pushing && player->is_moving &&
12325           IS_MOVING(x, y) &&
12326           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12327            Tile[x][y] == EL_SPRING))
12328       {
12329         ContinueMoving(x, y);
12330
12331         // continue moving after pushing (this is actually a bug)
12332         if (!IS_MOVING(x, y))
12333           Stop[x][y] = FALSE;
12334       }
12335     }
12336   }
12337
12338   SCAN_PLAYFIELD(x, y)
12339   {
12340     Last[x][y] = Tile[x][y];
12341
12342     ChangeCount[x][y] = 0;
12343     ChangeEvent[x][y] = -1;
12344
12345     // this must be handled before main playfield loop
12346     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12347     {
12348       MovDelay[x][y]--;
12349       if (MovDelay[x][y] <= 0)
12350         RemoveField(x, y);
12351     }
12352
12353     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12354     {
12355       MovDelay[x][y]--;
12356       if (MovDelay[x][y] <= 0)
12357       {
12358         int element = Store[x][y];
12359         int move_direction = MovDir[x][y];
12360         int player_index_bit = Store2[x][y];
12361
12362         Store[x][y] = 0;
12363         Store2[x][y] = 0;
12364
12365         RemoveField(x, y);
12366         TEST_DrawLevelField(x, y);
12367
12368         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12369
12370         if (IS_ENVELOPE(element))
12371           local_player->show_envelope = element;
12372       }
12373     }
12374
12375 #if DEBUG
12376     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12377     {
12378       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12379             x, y);
12380       Debug("game:playing:GameActions_RND", "This should never happen!");
12381
12382       ChangePage[x][y] = -1;
12383     }
12384 #endif
12385
12386     Stop[x][y] = FALSE;
12387     if (WasJustMoving[x][y] > 0)
12388       WasJustMoving[x][y]--;
12389     if (WasJustFalling[x][y] > 0)
12390       WasJustFalling[x][y]--;
12391     if (CheckCollision[x][y] > 0)
12392       CheckCollision[x][y]--;
12393     if (CheckImpact[x][y] > 0)
12394       CheckImpact[x][y]--;
12395
12396     GfxFrame[x][y]++;
12397
12398     /* reset finished pushing action (not done in ContinueMoving() to allow
12399        continuous pushing animation for elements with zero push delay) */
12400     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12401     {
12402       ResetGfxAnimation(x, y);
12403       TEST_DrawLevelField(x, y);
12404     }
12405
12406 #if DEBUG
12407     if (IS_BLOCKED(x, y))
12408     {
12409       int oldx, oldy;
12410
12411       Blocked2Moving(x, y, &oldx, &oldy);
12412       if (!IS_MOVING(oldx, oldy))
12413       {
12414         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12415         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12416         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12417         Debug("game:playing:GameActions_RND", "This should never happen!");
12418       }
12419     }
12420 #endif
12421   }
12422
12423   HandleMouseAction(&mouse_action, &mouse_action_last);
12424
12425   SCAN_PLAYFIELD(x, y)
12426   {
12427     element = Tile[x][y];
12428     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12429     last_gfx_frame = GfxFrame[x][y];
12430
12431     if (element == EL_EMPTY)
12432       graphic = el2img(GfxElementEmpty[x][y]);
12433
12434     ResetGfxFrame(x, y);
12435
12436     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12437       DrawLevelGraphicAnimation(x, y, graphic);
12438
12439     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12440         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12441       ResetRandomAnimationValue(x, y);
12442
12443     SetRandomAnimationValue(x, y);
12444
12445     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12446
12447     if (IS_INACTIVE(element))
12448     {
12449       if (IS_ANIMATED(graphic))
12450         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12451
12452       continue;
12453     }
12454
12455     // this may take place after moving, so 'element' may have changed
12456     if (IS_CHANGING(x, y) &&
12457         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12458     {
12459       int page = element_info[element].event_page_nr[CE_DELAY];
12460
12461       HandleElementChange(x, y, page);
12462
12463       element = Tile[x][y];
12464       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12465     }
12466
12467     CheckNextToConditions(x, y);
12468
12469     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12470     {
12471       StartMoving(x, y);
12472
12473       element = Tile[x][y];
12474       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12475
12476       if (IS_ANIMATED(graphic) &&
12477           !IS_MOVING(x, y) &&
12478           !Stop[x][y])
12479         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12480
12481       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12482         TEST_DrawTwinkleOnField(x, y);
12483     }
12484     else if (element == EL_ACID)
12485     {
12486       if (!Stop[x][y])
12487         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12488     }
12489     else if ((element == EL_EXIT_OPEN ||
12490               element == EL_EM_EXIT_OPEN ||
12491               element == EL_SP_EXIT_OPEN ||
12492               element == EL_STEEL_EXIT_OPEN ||
12493               element == EL_EM_STEEL_EXIT_OPEN ||
12494               element == EL_SP_TERMINAL ||
12495               element == EL_SP_TERMINAL_ACTIVE ||
12496               element == EL_EXTRA_TIME ||
12497               element == EL_SHIELD_NORMAL ||
12498               element == EL_SHIELD_DEADLY) &&
12499              IS_ANIMATED(graphic))
12500       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12501     else if (IS_MOVING(x, y))
12502       ContinueMoving(x, y);
12503     else if (IS_ACTIVE_BOMB(element))
12504       CheckDynamite(x, y);
12505     else if (element == EL_AMOEBA_GROWING)
12506       AmoebaGrowing(x, y);
12507     else if (element == EL_AMOEBA_SHRINKING)
12508       AmoebaShrinking(x, y);
12509
12510 #if !USE_NEW_AMOEBA_CODE
12511     else if (IS_AMOEBALIVE(element))
12512       AmoebaReproduce(x, y);
12513 #endif
12514
12515     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12516       Life(x, y);
12517     else if (element == EL_EXIT_CLOSED)
12518       CheckExit(x, y);
12519     else if (element == EL_EM_EXIT_CLOSED)
12520       CheckExitEM(x, y);
12521     else if (element == EL_STEEL_EXIT_CLOSED)
12522       CheckExitSteel(x, y);
12523     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12524       CheckExitSteelEM(x, y);
12525     else if (element == EL_SP_EXIT_CLOSED)
12526       CheckExitSP(x, y);
12527     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12528              element == EL_EXPANDABLE_STEELWALL_GROWING)
12529       WallGrowing(x, y);
12530     else if (element == EL_EXPANDABLE_WALL ||
12531              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12532              element == EL_EXPANDABLE_WALL_VERTICAL ||
12533              element == EL_EXPANDABLE_WALL_ANY ||
12534              element == EL_BD_EXPANDABLE_WALL ||
12535              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12536              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12537              element == EL_EXPANDABLE_STEELWALL_ANY)
12538       CheckWallGrowing(x, y);
12539     else if (element == EL_FLAMES)
12540       CheckForDragon(x, y);
12541     else if (element == EL_EXPLOSION)
12542       ; // drawing of correct explosion animation is handled separately
12543     else if (element == EL_ELEMENT_SNAPPING ||
12544              element == EL_DIAGONAL_SHRINKING ||
12545              element == EL_DIAGONAL_GROWING)
12546     {
12547       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12548
12549       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12550     }
12551     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12552       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12553
12554     if (IS_BELT_ACTIVE(element))
12555       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12556
12557     if (game.magic_wall_active)
12558     {
12559       int jx = local_player->jx, jy = local_player->jy;
12560
12561       // play the element sound at the position nearest to the player
12562       if ((element == EL_MAGIC_WALL_FULL ||
12563            element == EL_MAGIC_WALL_ACTIVE ||
12564            element == EL_MAGIC_WALL_EMPTYING ||
12565            element == EL_BD_MAGIC_WALL_FULL ||
12566            element == EL_BD_MAGIC_WALL_ACTIVE ||
12567            element == EL_BD_MAGIC_WALL_EMPTYING ||
12568            element == EL_DC_MAGIC_WALL_FULL ||
12569            element == EL_DC_MAGIC_WALL_ACTIVE ||
12570            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12571           ABS(x - jx) + ABS(y - jy) <
12572           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12573       {
12574         magic_wall_x = x;
12575         magic_wall_y = y;
12576       }
12577     }
12578   }
12579
12580 #if USE_NEW_AMOEBA_CODE
12581   // new experimental amoeba growth stuff
12582   if (!(FrameCounter % 8))
12583   {
12584     static unsigned int random = 1684108901;
12585
12586     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12587     {
12588       x = RND(lev_fieldx);
12589       y = RND(lev_fieldy);
12590       element = Tile[x][y];
12591
12592       if (!IS_PLAYER(x, y) &&
12593           (element == EL_EMPTY ||
12594            CAN_GROW_INTO(element) ||
12595            element == EL_QUICKSAND_EMPTY ||
12596            element == EL_QUICKSAND_FAST_EMPTY ||
12597            element == EL_ACID_SPLASH_LEFT ||
12598            element == EL_ACID_SPLASH_RIGHT))
12599       {
12600         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12601             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12602             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12603             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12604           Tile[x][y] = EL_AMOEBA_DROP;
12605       }
12606
12607       random = random * 129 + 1;
12608     }
12609   }
12610 #endif
12611
12612   game.explosions_delayed = FALSE;
12613
12614   SCAN_PLAYFIELD(x, y)
12615   {
12616     element = Tile[x][y];
12617
12618     if (ExplodeField[x][y])
12619       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12620     else if (element == EL_EXPLOSION)
12621       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12622
12623     ExplodeField[x][y] = EX_TYPE_NONE;
12624   }
12625
12626   game.explosions_delayed = TRUE;
12627
12628   if (game.magic_wall_active)
12629   {
12630     if (!(game.magic_wall_time_left % 4))
12631     {
12632       int element = Tile[magic_wall_x][magic_wall_y];
12633
12634       if (element == EL_BD_MAGIC_WALL_FULL ||
12635           element == EL_BD_MAGIC_WALL_ACTIVE ||
12636           element == EL_BD_MAGIC_WALL_EMPTYING)
12637         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12638       else if (element == EL_DC_MAGIC_WALL_FULL ||
12639                element == EL_DC_MAGIC_WALL_ACTIVE ||
12640                element == EL_DC_MAGIC_WALL_EMPTYING)
12641         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12642       else
12643         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12644     }
12645
12646     if (game.magic_wall_time_left > 0)
12647     {
12648       game.magic_wall_time_left--;
12649
12650       if (!game.magic_wall_time_left)
12651       {
12652         SCAN_PLAYFIELD(x, y)
12653         {
12654           element = Tile[x][y];
12655
12656           if (element == EL_MAGIC_WALL_ACTIVE ||
12657               element == EL_MAGIC_WALL_FULL)
12658           {
12659             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12660             TEST_DrawLevelField(x, y);
12661           }
12662           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12663                    element == EL_BD_MAGIC_WALL_FULL)
12664           {
12665             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12666             TEST_DrawLevelField(x, y);
12667           }
12668           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12669                    element == EL_DC_MAGIC_WALL_FULL)
12670           {
12671             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12672             TEST_DrawLevelField(x, y);
12673           }
12674         }
12675
12676         game.magic_wall_active = FALSE;
12677       }
12678     }
12679   }
12680
12681   if (game.light_time_left > 0)
12682   {
12683     game.light_time_left--;
12684
12685     if (game.light_time_left == 0)
12686       RedrawAllLightSwitchesAndInvisibleElements();
12687   }
12688
12689   if (game.timegate_time_left > 0)
12690   {
12691     game.timegate_time_left--;
12692
12693     if (game.timegate_time_left == 0)
12694       CloseAllOpenTimegates();
12695   }
12696
12697   if (game.lenses_time_left > 0)
12698   {
12699     game.lenses_time_left--;
12700
12701     if (game.lenses_time_left == 0)
12702       RedrawAllInvisibleElementsForLenses();
12703   }
12704
12705   if (game.magnify_time_left > 0)
12706   {
12707     game.magnify_time_left--;
12708
12709     if (game.magnify_time_left == 0)
12710       RedrawAllInvisibleElementsForMagnifier();
12711   }
12712
12713   for (i = 0; i < MAX_PLAYERS; i++)
12714   {
12715     struct PlayerInfo *player = &stored_player[i];
12716
12717     if (SHIELD_ON(player))
12718     {
12719       if (player->shield_deadly_time_left)
12720         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12721       else if (player->shield_normal_time_left)
12722         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12723     }
12724   }
12725
12726 #if USE_DELAYED_GFX_REDRAW
12727   SCAN_PLAYFIELD(x, y)
12728   {
12729     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12730     {
12731       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12732          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12733
12734       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12735         DrawLevelField(x, y);
12736
12737       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12738         DrawLevelFieldCrumbled(x, y);
12739
12740       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12741         DrawLevelFieldCrumbledNeighbours(x, y);
12742
12743       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12744         DrawTwinkleOnField(x, y);
12745     }
12746
12747     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12748   }
12749 #endif
12750
12751   DrawAllPlayers();
12752   PlayAllPlayersSound();
12753
12754   for (i = 0; i < MAX_PLAYERS; i++)
12755   {
12756     struct PlayerInfo *player = &stored_player[i];
12757
12758     if (player->show_envelope != 0 && (!player->active ||
12759                                        player->MovPos == 0))
12760     {
12761       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12762
12763       player->show_envelope = 0;
12764     }
12765   }
12766
12767   // use random number generator in every frame to make it less predictable
12768   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12769     RND(1);
12770
12771   mouse_action_last = mouse_action;
12772 }
12773
12774 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12775 {
12776   int min_x = x, min_y = y, max_x = x, max_y = y;
12777   int scr_fieldx = getScreenFieldSizeX();
12778   int scr_fieldy = getScreenFieldSizeY();
12779   int i;
12780
12781   for (i = 0; i < MAX_PLAYERS; i++)
12782   {
12783     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12784
12785     if (!stored_player[i].active || &stored_player[i] == player)
12786       continue;
12787
12788     min_x = MIN(min_x, jx);
12789     min_y = MIN(min_y, jy);
12790     max_x = MAX(max_x, jx);
12791     max_y = MAX(max_y, jy);
12792   }
12793
12794   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12795 }
12796
12797 static boolean AllPlayersInVisibleScreen(void)
12798 {
12799   int i;
12800
12801   for (i = 0; i < MAX_PLAYERS; i++)
12802   {
12803     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12804
12805     if (!stored_player[i].active)
12806       continue;
12807
12808     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12809       return FALSE;
12810   }
12811
12812   return TRUE;
12813 }
12814
12815 void ScrollLevel(int dx, int dy)
12816 {
12817   int scroll_offset = 2 * TILEX_VAR;
12818   int x, y;
12819
12820   BlitBitmap(drawto_field, drawto_field,
12821              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12822              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12823              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12824              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12825              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12826              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12827
12828   if (dx != 0)
12829   {
12830     x = (dx == 1 ? BX1 : BX2);
12831     for (y = BY1; y <= BY2; y++)
12832       DrawScreenField(x, y);
12833   }
12834
12835   if (dy != 0)
12836   {
12837     y = (dy == 1 ? BY1 : BY2);
12838     for (x = BX1; x <= BX2; x++)
12839       DrawScreenField(x, y);
12840   }
12841
12842   redraw_mask |= REDRAW_FIELD;
12843 }
12844
12845 static boolean canFallDown(struct PlayerInfo *player)
12846 {
12847   int jx = player->jx, jy = player->jy;
12848
12849   return (IN_LEV_FIELD(jx, jy + 1) &&
12850           (IS_FREE(jx, jy + 1) ||
12851            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12852           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12853           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12854 }
12855
12856 static boolean canPassField(int x, int y, int move_dir)
12857 {
12858   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12859   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12860   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12861   int nextx = x + dx;
12862   int nexty = y + dy;
12863   int element = Tile[x][y];
12864
12865   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12866           !CAN_MOVE(element) &&
12867           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12868           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12869           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12870 }
12871
12872 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12873 {
12874   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12875   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12876   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12877   int newx = x + dx;
12878   int newy = y + dy;
12879
12880   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12881           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12882           (IS_DIGGABLE(Tile[newx][newy]) ||
12883            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12884            canPassField(newx, newy, move_dir)));
12885 }
12886
12887 static void CheckGravityMovement(struct PlayerInfo *player)
12888 {
12889   if (player->gravity && !player->programmed_action)
12890   {
12891     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12892     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12893     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12894     int jx = player->jx, jy = player->jy;
12895     boolean player_is_moving_to_valid_field =
12896       (!player_is_snapping &&
12897        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12898         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12899     boolean player_can_fall_down = canFallDown(player);
12900
12901     if (player_can_fall_down &&
12902         !player_is_moving_to_valid_field)
12903       player->programmed_action = MV_DOWN;
12904   }
12905 }
12906
12907 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12908 {
12909   return CheckGravityMovement(player);
12910
12911   if (player->gravity && !player->programmed_action)
12912   {
12913     int jx = player->jx, jy = player->jy;
12914     boolean field_under_player_is_free =
12915       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12916     boolean player_is_standing_on_valid_field =
12917       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12918        (IS_WALKABLE(Tile[jx][jy]) &&
12919         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12920
12921     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12922       player->programmed_action = MV_DOWN;
12923   }
12924 }
12925
12926 /*
12927   MovePlayerOneStep()
12928   -----------------------------------------------------------------------------
12929   dx, dy:               direction (non-diagonal) to try to move the player to
12930   real_dx, real_dy:     direction as read from input device (can be diagonal)
12931 */
12932
12933 boolean MovePlayerOneStep(struct PlayerInfo *player,
12934                           int dx, int dy, int real_dx, int real_dy)
12935 {
12936   int jx = player->jx, jy = player->jy;
12937   int new_jx = jx + dx, new_jy = jy + dy;
12938   int can_move;
12939   boolean player_can_move = !player->cannot_move;
12940
12941   if (!player->active || (!dx && !dy))
12942     return MP_NO_ACTION;
12943
12944   player->MovDir = (dx < 0 ? MV_LEFT :
12945                     dx > 0 ? MV_RIGHT :
12946                     dy < 0 ? MV_UP :
12947                     dy > 0 ? MV_DOWN :  MV_NONE);
12948
12949   if (!IN_LEV_FIELD(new_jx, new_jy))
12950     return MP_NO_ACTION;
12951
12952   if (!player_can_move)
12953   {
12954     if (player->MovPos == 0)
12955     {
12956       player->is_moving = FALSE;
12957       player->is_digging = FALSE;
12958       player->is_collecting = FALSE;
12959       player->is_snapping = FALSE;
12960       player->is_pushing = FALSE;
12961     }
12962   }
12963
12964   if (!network.enabled && game.centered_player_nr == -1 &&
12965       !AllPlayersInSight(player, new_jx, new_jy))
12966     return MP_NO_ACTION;
12967
12968   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12969   if (can_move != MP_MOVING)
12970     return can_move;
12971
12972   // check if DigField() has caused relocation of the player
12973   if (player->jx != jx || player->jy != jy)
12974     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12975
12976   StorePlayer[jx][jy] = 0;
12977   player->last_jx = jx;
12978   player->last_jy = jy;
12979   player->jx = new_jx;
12980   player->jy = new_jy;
12981   StorePlayer[new_jx][new_jy] = player->element_nr;
12982
12983   if (player->move_delay_value_next != -1)
12984   {
12985     player->move_delay_value = player->move_delay_value_next;
12986     player->move_delay_value_next = -1;
12987   }
12988
12989   player->MovPos =
12990     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12991
12992   player->step_counter++;
12993
12994   PlayerVisit[jx][jy] = FrameCounter;
12995
12996   player->is_moving = TRUE;
12997
12998 #if 1
12999   // should better be called in MovePlayer(), but this breaks some tapes
13000   ScrollPlayer(player, SCROLL_INIT);
13001 #endif
13002
13003   return MP_MOVING;
13004 }
13005
13006 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13007 {
13008   int jx = player->jx, jy = player->jy;
13009   int old_jx = jx, old_jy = jy;
13010   int moved = MP_NO_ACTION;
13011
13012   if (!player->active)
13013     return FALSE;
13014
13015   if (!dx && !dy)
13016   {
13017     if (player->MovPos == 0)
13018     {
13019       player->is_moving = FALSE;
13020       player->is_digging = FALSE;
13021       player->is_collecting = FALSE;
13022       player->is_snapping = FALSE;
13023       player->is_pushing = FALSE;
13024     }
13025
13026     return FALSE;
13027   }
13028
13029   if (player->move_delay > 0)
13030     return FALSE;
13031
13032   player->move_delay = -1;              // set to "uninitialized" value
13033
13034   // store if player is automatically moved to next field
13035   player->is_auto_moving = (player->programmed_action != MV_NONE);
13036
13037   // remove the last programmed player action
13038   player->programmed_action = 0;
13039
13040   if (player->MovPos)
13041   {
13042     // should only happen if pre-1.2 tape recordings are played
13043     // this is only for backward compatibility
13044
13045     int original_move_delay_value = player->move_delay_value;
13046
13047 #if DEBUG
13048     Debug("game:playing:MovePlayer",
13049           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13050           tape.counter);
13051 #endif
13052
13053     // scroll remaining steps with finest movement resolution
13054     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13055
13056     while (player->MovPos)
13057     {
13058       ScrollPlayer(player, SCROLL_GO_ON);
13059       ScrollScreen(NULL, SCROLL_GO_ON);
13060
13061       AdvanceFrameAndPlayerCounters(player->index_nr);
13062
13063       DrawAllPlayers();
13064       BackToFront_WithFrameDelay(0);
13065     }
13066
13067     player->move_delay_value = original_move_delay_value;
13068   }
13069
13070   player->is_active = FALSE;
13071
13072   if (player->last_move_dir & MV_HORIZONTAL)
13073   {
13074     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13075       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13076   }
13077   else
13078   {
13079     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13080       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13081   }
13082
13083   if (!moved && !player->is_active)
13084   {
13085     player->is_moving = FALSE;
13086     player->is_digging = FALSE;
13087     player->is_collecting = FALSE;
13088     player->is_snapping = FALSE;
13089     player->is_pushing = FALSE;
13090   }
13091
13092   jx = player->jx;
13093   jy = player->jy;
13094
13095   if (moved & MP_MOVING && !ScreenMovPos &&
13096       (player->index_nr == game.centered_player_nr ||
13097        game.centered_player_nr == -1))
13098   {
13099     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13100
13101     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13102     {
13103       // actual player has left the screen -- scroll in that direction
13104       if (jx != old_jx)         // player has moved horizontally
13105         scroll_x += (jx - old_jx);
13106       else                      // player has moved vertically
13107         scroll_y += (jy - old_jy);
13108     }
13109     else
13110     {
13111       int offset_raw = game.scroll_delay_value;
13112
13113       if (jx != old_jx)         // player has moved horizontally
13114       {
13115         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13116         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13117         int new_scroll_x = jx - MIDPOSX + offset_x;
13118
13119         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13120             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13121           scroll_x = new_scroll_x;
13122
13123         // don't scroll over playfield boundaries
13124         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13125
13126         // don't scroll more than one field at a time
13127         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13128
13129         // don't scroll against the player's moving direction
13130         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13131             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13132           scroll_x = old_scroll_x;
13133       }
13134       else                      // player has moved vertically
13135       {
13136         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13137         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13138         int new_scroll_y = jy - MIDPOSY + offset_y;
13139
13140         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13141             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13142           scroll_y = new_scroll_y;
13143
13144         // don't scroll over playfield boundaries
13145         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13146
13147         // don't scroll more than one field at a time
13148         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13149
13150         // don't scroll against the player's moving direction
13151         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13152             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13153           scroll_y = old_scroll_y;
13154       }
13155     }
13156
13157     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13158     {
13159       if (!network.enabled && game.centered_player_nr == -1 &&
13160           !AllPlayersInVisibleScreen())
13161       {
13162         scroll_x = old_scroll_x;
13163         scroll_y = old_scroll_y;
13164       }
13165       else
13166       {
13167         ScrollScreen(player, SCROLL_INIT);
13168         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13169       }
13170     }
13171   }
13172
13173   player->StepFrame = 0;
13174
13175   if (moved & MP_MOVING)
13176   {
13177     if (old_jx != jx && old_jy == jy)
13178       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13179     else if (old_jx == jx && old_jy != jy)
13180       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13181
13182     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13183
13184     player->last_move_dir = player->MovDir;
13185     player->is_moving = TRUE;
13186     player->is_snapping = FALSE;
13187     player->is_switching = FALSE;
13188     player->is_dropping = FALSE;
13189     player->is_dropping_pressed = FALSE;
13190     player->drop_pressed_delay = 0;
13191
13192 #if 0
13193     // should better be called here than above, but this breaks some tapes
13194     ScrollPlayer(player, SCROLL_INIT);
13195 #endif
13196   }
13197   else
13198   {
13199     CheckGravityMovementWhenNotMoving(player);
13200
13201     player->is_moving = FALSE;
13202
13203     /* at this point, the player is allowed to move, but cannot move right now
13204        (e.g. because of something blocking the way) -- ensure that the player
13205        is also allowed to move in the next frame (in old versions before 3.1.1,
13206        the player was forced to wait again for eight frames before next try) */
13207
13208     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13209       player->move_delay = 0;   // allow direct movement in the next frame
13210   }
13211
13212   if (player->move_delay == -1)         // not yet initialized by DigField()
13213     player->move_delay = player->move_delay_value;
13214
13215   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13216   {
13217     TestIfPlayerTouchesBadThing(jx, jy);
13218     TestIfPlayerTouchesCustomElement(jx, jy);
13219   }
13220
13221   if (!player->active)
13222     RemovePlayer(player);
13223
13224   return moved;
13225 }
13226
13227 void ScrollPlayer(struct PlayerInfo *player, int mode)
13228 {
13229   int jx = player->jx, jy = player->jy;
13230   int last_jx = player->last_jx, last_jy = player->last_jy;
13231   int move_stepsize = TILEX / player->move_delay_value;
13232
13233   if (!player->active)
13234     return;
13235
13236   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13237     return;
13238
13239   if (mode == SCROLL_INIT)
13240   {
13241     player->actual_frame_counter.count = FrameCounter;
13242     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13243
13244     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13245         Tile[last_jx][last_jy] == EL_EMPTY)
13246     {
13247       int last_field_block_delay = 0;   // start with no blocking at all
13248       int block_delay_adjustment = player->block_delay_adjustment;
13249
13250       // if player blocks last field, add delay for exactly one move
13251       if (player->block_last_field)
13252       {
13253         last_field_block_delay += player->move_delay_value;
13254
13255         // when blocking enabled, prevent moving up despite gravity
13256         if (player->gravity && player->MovDir == MV_UP)
13257           block_delay_adjustment = -1;
13258       }
13259
13260       // add block delay adjustment (also possible when not blocking)
13261       last_field_block_delay += block_delay_adjustment;
13262
13263       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13264       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13265     }
13266
13267     if (player->MovPos != 0)    // player has not yet reached destination
13268       return;
13269   }
13270   else if (!FrameReached(&player->actual_frame_counter))
13271     return;
13272
13273   if (player->MovPos != 0)
13274   {
13275     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13276     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13277
13278     // before DrawPlayer() to draw correct player graphic for this case
13279     if (player->MovPos == 0)
13280       CheckGravityMovement(player);
13281   }
13282
13283   if (player->MovPos == 0)      // player reached destination field
13284   {
13285     if (player->move_delay_reset_counter > 0)
13286     {
13287       player->move_delay_reset_counter--;
13288
13289       if (player->move_delay_reset_counter == 0)
13290       {
13291         // continue with normal speed after quickly moving through gate
13292         HALVE_PLAYER_SPEED(player);
13293
13294         // be able to make the next move without delay
13295         player->move_delay = 0;
13296       }
13297     }
13298
13299     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13300         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13301         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13302         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13303         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13304         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13305         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13306         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13307     {
13308       ExitPlayer(player);
13309
13310       if (game.players_still_needed == 0 &&
13311           (game.friends_still_needed == 0 ||
13312            IS_SP_ELEMENT(Tile[jx][jy])))
13313         LevelSolved();
13314     }
13315
13316     player->last_jx = jx;
13317     player->last_jy = jy;
13318
13319     // this breaks one level: "machine", level 000
13320     {
13321       int move_direction = player->MovDir;
13322       int enter_side = MV_DIR_OPPOSITE(move_direction);
13323       int leave_side = move_direction;
13324       int old_jx = last_jx;
13325       int old_jy = last_jy;
13326       int old_element = Tile[old_jx][old_jy];
13327       int new_element = Tile[jx][jy];
13328
13329       if (IS_CUSTOM_ELEMENT(old_element))
13330         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13331                                    CE_LEFT_BY_PLAYER,
13332                                    player->index_bit, leave_side);
13333
13334       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13335                                           CE_PLAYER_LEAVES_X,
13336                                           player->index_bit, leave_side);
13337
13338       // needed because pushed element has not yet reached its destination,
13339       // so it would trigger a change event at its previous field location
13340       if (!player->is_pushing)
13341       {
13342         if (IS_CUSTOM_ELEMENT(new_element))
13343           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13344                                      player->index_bit, enter_side);
13345
13346         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13347                                             CE_PLAYER_ENTERS_X,
13348                                             player->index_bit, enter_side);
13349       }
13350
13351       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13352                                         CE_MOVE_OF_X, move_direction);
13353     }
13354
13355     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13356     {
13357       TestIfPlayerTouchesBadThing(jx, jy);
13358       TestIfPlayerTouchesCustomElement(jx, jy);
13359
13360       // needed because pushed element has not yet reached its destination,
13361       // so it would trigger a change event at its previous field location
13362       if (!player->is_pushing)
13363         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13364
13365       if (level.finish_dig_collect &&
13366           (player->is_digging || player->is_collecting))
13367       {
13368         int last_element = player->last_removed_element;
13369         int move_direction = player->MovDir;
13370         int enter_side = MV_DIR_OPPOSITE(move_direction);
13371         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13372                             CE_PLAYER_COLLECTS_X);
13373
13374         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13375                                             player->index_bit, enter_side);
13376
13377         player->last_removed_element = EL_UNDEFINED;
13378       }
13379
13380       if (!player->active)
13381         RemovePlayer(player);
13382     }
13383
13384     if (level.use_step_counter)
13385       CheckLevelTime_StepCounter();
13386
13387     if (tape.single_step && tape.recording && !tape.pausing &&
13388         !player->programmed_action)
13389       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13390
13391     if (!player->programmed_action)
13392       CheckSaveEngineSnapshot(player);
13393   }
13394 }
13395
13396 void ScrollScreen(struct PlayerInfo *player, int mode)
13397 {
13398   static DelayCounter screen_frame_counter = { 0 };
13399
13400   if (mode == SCROLL_INIT)
13401   {
13402     // set scrolling step size according to actual player's moving speed
13403     ScrollStepSize = TILEX / player->move_delay_value;
13404
13405     screen_frame_counter.count = FrameCounter;
13406     screen_frame_counter.value = 1;
13407
13408     ScreenMovDir = player->MovDir;
13409     ScreenMovPos = player->MovPos;
13410     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13411     return;
13412   }
13413   else if (!FrameReached(&screen_frame_counter))
13414     return;
13415
13416   if (ScreenMovPos)
13417   {
13418     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13419     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13420     redraw_mask |= REDRAW_FIELD;
13421   }
13422   else
13423     ScreenMovDir = MV_NONE;
13424 }
13425
13426 void CheckNextToConditions(int x, int y)
13427 {
13428   int element = Tile[x][y];
13429
13430   if (IS_PLAYER(x, y))
13431     TestIfPlayerNextToCustomElement(x, y);
13432
13433   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13434       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13435     TestIfElementNextToCustomElement(x, y);
13436 }
13437
13438 void TestIfPlayerNextToCustomElement(int x, int y)
13439 {
13440   struct XY *xy = xy_topdown;
13441   static int trigger_sides[4][2] =
13442   {
13443     // center side       border side
13444     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13445     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13446     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13447     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13448   };
13449   int i;
13450
13451   if (!IS_PLAYER(x, y))
13452     return;
13453
13454   struct PlayerInfo *player = PLAYERINFO(x, y);
13455
13456   if (player->is_moving)
13457     return;
13458
13459   for (i = 0; i < NUM_DIRECTIONS; i++)
13460   {
13461     int xx = x + xy[i].x;
13462     int yy = y + xy[i].y;
13463     int border_side = trigger_sides[i][1];
13464     int border_element;
13465
13466     if (!IN_LEV_FIELD(xx, yy))
13467       continue;
13468
13469     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13470       continue;         // center and border element not connected
13471
13472     border_element = Tile[xx][yy];
13473
13474     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13475                                player->index_bit, border_side);
13476     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13477                                         CE_PLAYER_NEXT_TO_X,
13478                                         player->index_bit, border_side);
13479
13480     /* use player element that is initially defined in the level playfield,
13481        not the player element that corresponds to the runtime player number
13482        (example: a level that contains EL_PLAYER_3 as the only player would
13483        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13484
13485     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13486                              CE_NEXT_TO_X, border_side);
13487   }
13488 }
13489
13490 void TestIfPlayerTouchesCustomElement(int x, int y)
13491 {
13492   struct XY *xy = xy_topdown;
13493   static int trigger_sides[4][2] =
13494   {
13495     // center side       border side
13496     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13497     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13498     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13499     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13500   };
13501   static int touch_dir[4] =
13502   {
13503     MV_LEFT | MV_RIGHT,
13504     MV_UP   | MV_DOWN,
13505     MV_UP   | MV_DOWN,
13506     MV_LEFT | MV_RIGHT
13507   };
13508   int center_element = Tile[x][y];      // should always be non-moving!
13509   int i;
13510
13511   for (i = 0; i < NUM_DIRECTIONS; i++)
13512   {
13513     int xx = x + xy[i].x;
13514     int yy = y + xy[i].y;
13515     int center_side = trigger_sides[i][0];
13516     int border_side = trigger_sides[i][1];
13517     int border_element;
13518
13519     if (!IN_LEV_FIELD(xx, yy))
13520       continue;
13521
13522     if (IS_PLAYER(x, y))                // player found at center element
13523     {
13524       struct PlayerInfo *player = PLAYERINFO(x, y);
13525
13526       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13527         border_element = Tile[xx][yy];          // may be moving!
13528       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13529         border_element = Tile[xx][yy];
13530       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13531         border_element = MovingOrBlocked2Element(xx, yy);
13532       else
13533         continue;               // center and border element do not touch
13534
13535       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13536                                  player->index_bit, border_side);
13537       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13538                                           CE_PLAYER_TOUCHES_X,
13539                                           player->index_bit, border_side);
13540
13541       {
13542         /* use player element that is initially defined in the level playfield,
13543            not the player element that corresponds to the runtime player number
13544            (example: a level that contains EL_PLAYER_3 as the only player would
13545            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13546         int player_element = PLAYERINFO(x, y)->initial_element;
13547
13548         // as element "X" is the player here, check opposite (center) side
13549         CheckElementChangeBySide(xx, yy, border_element, player_element,
13550                                  CE_TOUCHING_X, center_side);
13551       }
13552     }
13553     else if (IS_PLAYER(xx, yy))         // player found at border element
13554     {
13555       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13556
13557       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13558       {
13559         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13560           continue;             // center and border element do not touch
13561       }
13562
13563       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13564                                  player->index_bit, center_side);
13565       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13566                                           CE_PLAYER_TOUCHES_X,
13567                                           player->index_bit, center_side);
13568
13569       {
13570         /* use player element that is initially defined in the level playfield,
13571            not the player element that corresponds to the runtime player number
13572            (example: a level that contains EL_PLAYER_3 as the only player would
13573            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13574         int player_element = PLAYERINFO(xx, yy)->initial_element;
13575
13576         // as element "X" is the player here, check opposite (border) side
13577         CheckElementChangeBySide(x, y, center_element, player_element,
13578                                  CE_TOUCHING_X, border_side);
13579       }
13580
13581       break;
13582     }
13583   }
13584 }
13585
13586 void TestIfElementNextToCustomElement(int x, int y)
13587 {
13588   struct XY *xy = xy_topdown;
13589   static int trigger_sides[4][2] =
13590   {
13591     // center side      border side
13592     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13593     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13594     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13595     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13596   };
13597   int center_element = Tile[x][y];      // should always be non-moving!
13598   int i;
13599
13600   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13601     return;
13602
13603   for (i = 0; i < NUM_DIRECTIONS; i++)
13604   {
13605     int xx = x + xy[i].x;
13606     int yy = y + xy[i].y;
13607     int border_side = trigger_sides[i][1];
13608     int border_element;
13609
13610     if (!IN_LEV_FIELD(xx, yy))
13611       continue;
13612
13613     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13614       continue;                 // center and border element not connected
13615
13616     border_element = Tile[xx][yy];
13617
13618     // check for change of center element (but change it only once)
13619     if (CheckElementChangeBySide(x, y, center_element, border_element,
13620                                  CE_NEXT_TO_X, border_side))
13621       break;
13622   }
13623 }
13624
13625 void TestIfElementTouchesCustomElement(int x, int y)
13626 {
13627   struct XY *xy = xy_topdown;
13628   static int trigger_sides[4][2] =
13629   {
13630     // center side      border side
13631     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13632     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13633     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13634     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13635   };
13636   static int touch_dir[4] =
13637   {
13638     MV_LEFT | MV_RIGHT,
13639     MV_UP   | MV_DOWN,
13640     MV_UP   | MV_DOWN,
13641     MV_LEFT | MV_RIGHT
13642   };
13643   boolean change_center_element = FALSE;
13644   int center_element = Tile[x][y];      // should always be non-moving!
13645   int border_element_old[NUM_DIRECTIONS];
13646   int i;
13647
13648   for (i = 0; i < NUM_DIRECTIONS; i++)
13649   {
13650     int xx = x + xy[i].x;
13651     int yy = y + xy[i].y;
13652     int border_element;
13653
13654     border_element_old[i] = -1;
13655
13656     if (!IN_LEV_FIELD(xx, yy))
13657       continue;
13658
13659     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13660       border_element = Tile[xx][yy];    // may be moving!
13661     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13662       border_element = Tile[xx][yy];
13663     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13664       border_element = MovingOrBlocked2Element(xx, yy);
13665     else
13666       continue;                 // center and border element do not touch
13667
13668     border_element_old[i] = border_element;
13669   }
13670
13671   for (i = 0; i < NUM_DIRECTIONS; i++)
13672   {
13673     int xx = x + xy[i].x;
13674     int yy = y + xy[i].y;
13675     int center_side = trigger_sides[i][0];
13676     int border_element = border_element_old[i];
13677
13678     if (border_element == -1)
13679       continue;
13680
13681     // check for change of border element
13682     CheckElementChangeBySide(xx, yy, border_element, center_element,
13683                              CE_TOUCHING_X, center_side);
13684
13685     // (center element cannot be player, so we don't have to check this here)
13686   }
13687
13688   for (i = 0; i < NUM_DIRECTIONS; i++)
13689   {
13690     int xx = x + xy[i].x;
13691     int yy = y + xy[i].y;
13692     int border_side = trigger_sides[i][1];
13693     int border_element = border_element_old[i];
13694
13695     if (border_element == -1)
13696       continue;
13697
13698     // check for change of center element (but change it only once)
13699     if (!change_center_element)
13700       change_center_element =
13701         CheckElementChangeBySide(x, y, center_element, border_element,
13702                                  CE_TOUCHING_X, border_side);
13703
13704     if (IS_PLAYER(xx, yy))
13705     {
13706       /* use player element that is initially defined in the level playfield,
13707          not the player element that corresponds to the runtime player number
13708          (example: a level that contains EL_PLAYER_3 as the only player would
13709          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13710       int player_element = PLAYERINFO(xx, yy)->initial_element;
13711
13712       // as element "X" is the player here, check opposite (border) side
13713       CheckElementChangeBySide(x, y, center_element, player_element,
13714                                CE_TOUCHING_X, border_side);
13715     }
13716   }
13717 }
13718
13719 void TestIfElementHitsCustomElement(int x, int y, int direction)
13720 {
13721   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13722   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13723   int hitx = x + dx, hity = y + dy;
13724   int hitting_element = Tile[x][y];
13725   int touched_element;
13726
13727   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13728     return;
13729
13730   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13731                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13732
13733   if (IN_LEV_FIELD(hitx, hity))
13734   {
13735     int opposite_direction = MV_DIR_OPPOSITE(direction);
13736     int hitting_side = direction;
13737     int touched_side = opposite_direction;
13738     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13739                           MovDir[hitx][hity] != direction ||
13740                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13741
13742     object_hit = TRUE;
13743
13744     if (object_hit)
13745     {
13746       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13747                                CE_HITTING_X, touched_side);
13748
13749       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13750                                CE_HIT_BY_X, hitting_side);
13751
13752       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13753                                CE_HIT_BY_SOMETHING, opposite_direction);
13754
13755       if (IS_PLAYER(hitx, hity))
13756       {
13757         /* use player element that is initially defined in the level playfield,
13758            not the player element that corresponds to the runtime player number
13759            (example: a level that contains EL_PLAYER_3 as the only player would
13760            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13761         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13762
13763         CheckElementChangeBySide(x, y, hitting_element, player_element,
13764                                  CE_HITTING_X, touched_side);
13765       }
13766     }
13767   }
13768
13769   // "hitting something" is also true when hitting the playfield border
13770   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13771                            CE_HITTING_SOMETHING, direction);
13772 }
13773
13774 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13775 {
13776   int i, kill_x = -1, kill_y = -1;
13777
13778   int bad_element = -1;
13779   struct XY *test_xy = xy_topdown;
13780   static int test_dir[4] =
13781   {
13782     MV_UP,
13783     MV_LEFT,
13784     MV_RIGHT,
13785     MV_DOWN
13786   };
13787
13788   for (i = 0; i < NUM_DIRECTIONS; i++)
13789   {
13790     int test_x, test_y, test_move_dir, test_element;
13791
13792     test_x = good_x + test_xy[i].x;
13793     test_y = good_y + test_xy[i].y;
13794
13795     if (!IN_LEV_FIELD(test_x, test_y))
13796       continue;
13797
13798     test_move_dir =
13799       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13800
13801     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13802
13803     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13804        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13805     */
13806     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13807         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13808     {
13809       kill_x = test_x;
13810       kill_y = test_y;
13811       bad_element = test_element;
13812
13813       break;
13814     }
13815   }
13816
13817   if (kill_x != -1 || kill_y != -1)
13818   {
13819     if (IS_PLAYER(good_x, good_y))
13820     {
13821       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13822
13823       if (player->shield_deadly_time_left > 0 &&
13824           !IS_INDESTRUCTIBLE(bad_element))
13825         Bang(kill_x, kill_y);
13826       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13827         KillPlayer(player);
13828     }
13829     else
13830       Bang(good_x, good_y);
13831   }
13832 }
13833
13834 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13835 {
13836   int i, kill_x = -1, kill_y = -1;
13837   int bad_element = Tile[bad_x][bad_y];
13838   struct XY *test_xy = xy_topdown;
13839   static int touch_dir[4] =
13840   {
13841     MV_LEFT | MV_RIGHT,
13842     MV_UP   | MV_DOWN,
13843     MV_UP   | MV_DOWN,
13844     MV_LEFT | MV_RIGHT
13845   };
13846   static int test_dir[4] =
13847   {
13848     MV_UP,
13849     MV_LEFT,
13850     MV_RIGHT,
13851     MV_DOWN
13852   };
13853
13854   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13855     return;
13856
13857   for (i = 0; i < NUM_DIRECTIONS; i++)
13858   {
13859     int test_x, test_y, test_move_dir, test_element;
13860
13861     test_x = bad_x + test_xy[i].x;
13862     test_y = bad_y + test_xy[i].y;
13863
13864     if (!IN_LEV_FIELD(test_x, test_y))
13865       continue;
13866
13867     test_move_dir =
13868       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13869
13870     test_element = Tile[test_x][test_y];
13871
13872     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13873        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13874     */
13875     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13876         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13877     {
13878       // good thing is player or penguin that does not move away
13879       if (IS_PLAYER(test_x, test_y))
13880       {
13881         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13882
13883         if (bad_element == EL_ROBOT && player->is_moving)
13884           continue;     // robot does not kill player if he is moving
13885
13886         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13887         {
13888           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13889             continue;           // center and border element do not touch
13890         }
13891
13892         kill_x = test_x;
13893         kill_y = test_y;
13894
13895         break;
13896       }
13897       else if (test_element == EL_PENGUIN)
13898       {
13899         kill_x = test_x;
13900         kill_y = test_y;
13901
13902         break;
13903       }
13904     }
13905   }
13906
13907   if (kill_x != -1 || kill_y != -1)
13908   {
13909     if (IS_PLAYER(kill_x, kill_y))
13910     {
13911       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13912
13913       if (player->shield_deadly_time_left > 0 &&
13914           !IS_INDESTRUCTIBLE(bad_element))
13915         Bang(bad_x, bad_y);
13916       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13917         KillPlayer(player);
13918     }
13919     else
13920       Bang(kill_x, kill_y);
13921   }
13922 }
13923
13924 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13925 {
13926   int bad_element = Tile[bad_x][bad_y];
13927   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13928   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13929   int test_x = bad_x + dx, test_y = bad_y + dy;
13930   int test_move_dir, test_element;
13931   int kill_x = -1, kill_y = -1;
13932
13933   if (!IN_LEV_FIELD(test_x, test_y))
13934     return;
13935
13936   test_move_dir =
13937     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13938
13939   test_element = Tile[test_x][test_y];
13940
13941   if (test_move_dir != bad_move_dir)
13942   {
13943     // good thing can be player or penguin that does not move away
13944     if (IS_PLAYER(test_x, test_y))
13945     {
13946       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13947
13948       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13949          player as being hit when he is moving towards the bad thing, because
13950          the "get hit by" condition would be lost after the player stops) */
13951       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13952         return;         // player moves away from bad thing
13953
13954       kill_x = test_x;
13955       kill_y = test_y;
13956     }
13957     else if (test_element == EL_PENGUIN)
13958     {
13959       kill_x = test_x;
13960       kill_y = test_y;
13961     }
13962   }
13963
13964   if (kill_x != -1 || kill_y != -1)
13965   {
13966     if (IS_PLAYER(kill_x, kill_y))
13967     {
13968       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13969
13970       if (player->shield_deadly_time_left > 0 &&
13971           !IS_INDESTRUCTIBLE(bad_element))
13972         Bang(bad_x, bad_y);
13973       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13974         KillPlayer(player);
13975     }
13976     else
13977       Bang(kill_x, kill_y);
13978   }
13979 }
13980
13981 void TestIfPlayerTouchesBadThing(int x, int y)
13982 {
13983   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13984 }
13985
13986 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13987 {
13988   TestIfGoodThingHitsBadThing(x, y, move_dir);
13989 }
13990
13991 void TestIfBadThingTouchesPlayer(int x, int y)
13992 {
13993   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13994 }
13995
13996 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13997 {
13998   TestIfBadThingHitsGoodThing(x, y, move_dir);
13999 }
14000
14001 void TestIfFriendTouchesBadThing(int x, int y)
14002 {
14003   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14004 }
14005
14006 void TestIfBadThingTouchesFriend(int x, int y)
14007 {
14008   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14009 }
14010
14011 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14012 {
14013   int i, kill_x = bad_x, kill_y = bad_y;
14014   struct XY *xy = xy_topdown;
14015
14016   for (i = 0; i < NUM_DIRECTIONS; i++)
14017   {
14018     int x, y, element;
14019
14020     x = bad_x + xy[i].x;
14021     y = bad_y + xy[i].y;
14022     if (!IN_LEV_FIELD(x, y))
14023       continue;
14024
14025     element = Tile[x][y];
14026     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14027         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14028     {
14029       kill_x = x;
14030       kill_y = y;
14031       break;
14032     }
14033   }
14034
14035   if (kill_x != bad_x || kill_y != bad_y)
14036     Bang(bad_x, bad_y);
14037 }
14038
14039 void KillPlayer(struct PlayerInfo *player)
14040 {
14041   int jx = player->jx, jy = player->jy;
14042
14043   if (!player->active)
14044     return;
14045
14046 #if 0
14047   Debug("game:playing:KillPlayer",
14048         "0: killed == %d, active == %d, reanimated == %d",
14049         player->killed, player->active, player->reanimated);
14050 #endif
14051
14052   /* the following code was introduced to prevent an infinite loop when calling
14053      -> Bang()
14054      -> CheckTriggeredElementChangeExt()
14055      -> ExecuteCustomElementAction()
14056      -> KillPlayer()
14057      -> (infinitely repeating the above sequence of function calls)
14058      which occurs when killing the player while having a CE with the setting
14059      "kill player X when explosion of <player X>"; the solution using a new
14060      field "player->killed" was chosen for backwards compatibility, although
14061      clever use of the fields "player->active" etc. would probably also work */
14062 #if 1
14063   if (player->killed)
14064     return;
14065 #endif
14066
14067   player->killed = TRUE;
14068
14069   // remove accessible field at the player's position
14070   RemoveField(jx, jy);
14071
14072   // deactivate shield (else Bang()/Explode() would not work right)
14073   player->shield_normal_time_left = 0;
14074   player->shield_deadly_time_left = 0;
14075
14076 #if 0
14077   Debug("game:playing:KillPlayer",
14078         "1: killed == %d, active == %d, reanimated == %d",
14079         player->killed, player->active, player->reanimated);
14080 #endif
14081
14082   Bang(jx, jy);
14083
14084 #if 0
14085   Debug("game:playing:KillPlayer",
14086         "2: killed == %d, active == %d, reanimated == %d",
14087         player->killed, player->active, player->reanimated);
14088 #endif
14089
14090   if (player->reanimated)       // killed player may have been reanimated
14091     player->killed = player->reanimated = FALSE;
14092   else
14093     BuryPlayer(player);
14094 }
14095
14096 static void KillPlayerUnlessEnemyProtected(int x, int y)
14097 {
14098   if (!PLAYER_ENEMY_PROTECTED(x, y))
14099     KillPlayer(PLAYERINFO(x, y));
14100 }
14101
14102 static void KillPlayerUnlessExplosionProtected(int x, int y)
14103 {
14104   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14105     KillPlayer(PLAYERINFO(x, y));
14106 }
14107
14108 void BuryPlayer(struct PlayerInfo *player)
14109 {
14110   int jx = player->jx, jy = player->jy;
14111
14112   if (!player->active)
14113     return;
14114
14115   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14116
14117   RemovePlayer(player);
14118
14119   player->buried = TRUE;
14120
14121   if (game.all_players_gone)
14122     game.GameOver = TRUE;
14123 }
14124
14125 void RemovePlayer(struct PlayerInfo *player)
14126 {
14127   int jx = player->jx, jy = player->jy;
14128   int i, found = FALSE;
14129
14130   player->present = FALSE;
14131   player->active = FALSE;
14132
14133   // required for some CE actions (even if the player is not active anymore)
14134   player->MovPos = 0;
14135
14136   if (!ExplodeField[jx][jy])
14137     StorePlayer[jx][jy] = 0;
14138
14139   if (player->is_moving)
14140     TEST_DrawLevelField(player->last_jx, player->last_jy);
14141
14142   for (i = 0; i < MAX_PLAYERS; i++)
14143     if (stored_player[i].active)
14144       found = TRUE;
14145
14146   if (!found)
14147   {
14148     game.all_players_gone = TRUE;
14149     game.GameOver = TRUE;
14150   }
14151
14152   game.exit_x = game.robot_wheel_x = jx;
14153   game.exit_y = game.robot_wheel_y = jy;
14154 }
14155
14156 void ExitPlayer(struct PlayerInfo *player)
14157 {
14158   DrawPlayer(player);   // needed here only to cleanup last field
14159   RemovePlayer(player);
14160
14161   if (game.players_still_needed > 0)
14162     game.players_still_needed--;
14163 }
14164
14165 static void SetFieldForSnapping(int x, int y, int element, int direction,
14166                                 int player_index_bit)
14167 {
14168   struct ElementInfo *ei = &element_info[element];
14169   int direction_bit = MV_DIR_TO_BIT(direction);
14170   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14171   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14172                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14173
14174   Tile[x][y] = EL_ELEMENT_SNAPPING;
14175   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14176   MovDir[x][y] = direction;
14177   Store[x][y] = element;
14178   Store2[x][y] = player_index_bit;
14179
14180   ResetGfxAnimation(x, y);
14181
14182   GfxElement[x][y] = element;
14183   GfxAction[x][y] = action;
14184   GfxDir[x][y] = direction;
14185   GfxFrame[x][y] = -1;
14186 }
14187
14188 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14189                                    int player_index_bit)
14190 {
14191   TestIfElementTouchesCustomElement(x, y);      // for empty space
14192
14193   if (level.finish_dig_collect)
14194   {
14195     int dig_side = MV_DIR_OPPOSITE(direction);
14196     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14197                         CE_PLAYER_COLLECTS_X);
14198
14199     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14200                                         player_index_bit, dig_side);
14201     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14202                                         player_index_bit, dig_side);
14203   }
14204 }
14205
14206 /*
14207   =============================================================================
14208   checkDiagonalPushing()
14209   -----------------------------------------------------------------------------
14210   check if diagonal input device direction results in pushing of object
14211   (by checking if the alternative direction is walkable, diggable, ...)
14212   =============================================================================
14213 */
14214
14215 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14216                                     int x, int y, int real_dx, int real_dy)
14217 {
14218   int jx, jy, dx, dy, xx, yy;
14219
14220   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14221     return TRUE;
14222
14223   // diagonal direction: check alternative direction
14224   jx = player->jx;
14225   jy = player->jy;
14226   dx = x - jx;
14227   dy = y - jy;
14228   xx = jx + (dx == 0 ? real_dx : 0);
14229   yy = jy + (dy == 0 ? real_dy : 0);
14230
14231   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14232 }
14233
14234 /*
14235   =============================================================================
14236   DigField()
14237   -----------------------------------------------------------------------------
14238   x, y:                 field next to player (non-diagonal) to try to dig to
14239   real_dx, real_dy:     direction as read from input device (can be diagonal)
14240   =============================================================================
14241 */
14242
14243 static int DigField(struct PlayerInfo *player,
14244                     int oldx, int oldy, int x, int y,
14245                     int real_dx, int real_dy, int mode)
14246 {
14247   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14248   boolean player_was_pushing = player->is_pushing;
14249   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14250   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14251   int jx = oldx, jy = oldy;
14252   int dx = x - jx, dy = y - jy;
14253   int nextx = x + dx, nexty = y + dy;
14254   int move_direction = (dx == -1 ? MV_LEFT  :
14255                         dx == +1 ? MV_RIGHT :
14256                         dy == -1 ? MV_UP    :
14257                         dy == +1 ? MV_DOWN  : MV_NONE);
14258   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14259   int dig_side = MV_DIR_OPPOSITE(move_direction);
14260   int old_element = Tile[jx][jy];
14261   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14262   int collect_count;
14263
14264   if (is_player)                // function can also be called by EL_PENGUIN
14265   {
14266     if (player->MovPos == 0)
14267     {
14268       player->is_digging = FALSE;
14269       player->is_collecting = FALSE;
14270     }
14271
14272     if (player->MovPos == 0)    // last pushing move finished
14273       player->is_pushing = FALSE;
14274
14275     if (mode == DF_NO_PUSH)     // player just stopped pushing
14276     {
14277       player->is_switching = FALSE;
14278       player->push_delay = -1;
14279
14280       return MP_NO_ACTION;
14281     }
14282   }
14283   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14284     old_element = Back[jx][jy];
14285
14286   // in case of element dropped at player position, check background
14287   else if (Back[jx][jy] != EL_EMPTY &&
14288            game.engine_version >= VERSION_IDENT(2,2,0,0))
14289     old_element = Back[jx][jy];
14290
14291   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14292     return MP_NO_ACTION;        // field has no opening in this direction
14293
14294   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14295     return MP_NO_ACTION;        // field has no opening in this direction
14296
14297   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14298   {
14299     SplashAcid(x, y);
14300
14301     Tile[jx][jy] = player->artwork_element;
14302     InitMovingField(jx, jy, MV_DOWN);
14303     Store[jx][jy] = EL_ACID;
14304     ContinueMoving(jx, jy);
14305     BuryPlayer(player);
14306
14307     return MP_DONT_RUN_INTO;
14308   }
14309
14310   if (player_can_move && DONT_RUN_INTO(element))
14311   {
14312     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14313
14314     return MP_DONT_RUN_INTO;
14315   }
14316
14317   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14318     return MP_NO_ACTION;
14319
14320   collect_count = element_info[element].collect_count_initial;
14321
14322   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14323     return MP_NO_ACTION;
14324
14325   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14326     player_can_move = player_can_move_or_snap;
14327
14328   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14329       game.engine_version >= VERSION_IDENT(2,2,0,0))
14330   {
14331     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14332                                player->index_bit, dig_side);
14333     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14334                                         player->index_bit, dig_side);
14335
14336     if (element == EL_DC_LANDMINE)
14337       Bang(x, y);
14338
14339     if (Tile[x][y] != element)          // field changed by snapping
14340       return MP_ACTION;
14341
14342     return MP_NO_ACTION;
14343   }
14344
14345   if (player->gravity && is_player && !player->is_auto_moving &&
14346       canFallDown(player) && move_direction != MV_DOWN &&
14347       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14348     return MP_NO_ACTION;        // player cannot walk here due to gravity
14349
14350   if (player_can_move &&
14351       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14352   {
14353     int sound_element = SND_ELEMENT(element);
14354     int sound_action = ACTION_WALKING;
14355
14356     if (IS_RND_GATE(element))
14357     {
14358       if (!player->key[RND_GATE_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (IS_RND_GATE_GRAY(element))
14362     {
14363       if (!player->key[RND_GATE_GRAY_NR(element)])
14364         return MP_NO_ACTION;
14365     }
14366     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14367     {
14368       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14369         return MP_NO_ACTION;
14370     }
14371     else if (element == EL_EXIT_OPEN ||
14372              element == EL_EM_EXIT_OPEN ||
14373              element == EL_EM_EXIT_OPENING ||
14374              element == EL_STEEL_EXIT_OPEN ||
14375              element == EL_EM_STEEL_EXIT_OPEN ||
14376              element == EL_EM_STEEL_EXIT_OPENING ||
14377              element == EL_SP_EXIT_OPEN ||
14378              element == EL_SP_EXIT_OPENING)
14379     {
14380       sound_action = ACTION_PASSING;    // player is passing exit
14381     }
14382     else if (element == EL_EMPTY)
14383     {
14384       sound_action = ACTION_MOVING;             // nothing to walk on
14385     }
14386
14387     // play sound from background or player, whatever is available
14388     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14389       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14390     else
14391       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14392   }
14393   else if (player_can_move &&
14394            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14395   {
14396     if (!ACCESS_FROM(element, opposite_direction))
14397       return MP_NO_ACTION;      // field not accessible from this direction
14398
14399     if (CAN_MOVE(element))      // only fixed elements can be passed!
14400       return MP_NO_ACTION;
14401
14402     if (IS_EM_GATE(element))
14403     {
14404       if (!player->key[EM_GATE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EM_GATE_GRAY(element))
14408     {
14409       if (!player->key[EM_GATE_GRAY_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14413     {
14414       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14415         return MP_NO_ACTION;
14416     }
14417     else if (IS_EMC_GATE(element))
14418     {
14419       if (!player->key[EMC_GATE_NR(element)])
14420         return MP_NO_ACTION;
14421     }
14422     else if (IS_EMC_GATE_GRAY(element))
14423     {
14424       if (!player->key[EMC_GATE_GRAY_NR(element)])
14425         return MP_NO_ACTION;
14426     }
14427     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14428     {
14429       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14430         return MP_NO_ACTION;
14431     }
14432     else if (element == EL_DC_GATE_WHITE ||
14433              element == EL_DC_GATE_WHITE_GRAY ||
14434              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14435     {
14436       if (player->num_white_keys == 0)
14437         return MP_NO_ACTION;
14438
14439       player->num_white_keys--;
14440     }
14441     else if (IS_SP_PORT(element))
14442     {
14443       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14444           element == EL_SP_GRAVITY_PORT_RIGHT ||
14445           element == EL_SP_GRAVITY_PORT_UP ||
14446           element == EL_SP_GRAVITY_PORT_DOWN)
14447         player->gravity = !player->gravity;
14448       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14449                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14450                element == EL_SP_GRAVITY_ON_PORT_UP ||
14451                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14452         player->gravity = TRUE;
14453       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14454                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14455                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14456                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14457         player->gravity = FALSE;
14458     }
14459
14460     // automatically move to the next field with double speed
14461     player->programmed_action = move_direction;
14462
14463     if (player->move_delay_reset_counter == 0)
14464     {
14465       player->move_delay_reset_counter = 2;     // two double speed steps
14466
14467       DOUBLE_PLAYER_SPEED(player);
14468     }
14469
14470     PlayLevelSoundAction(x, y, ACTION_PASSING);
14471   }
14472   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14473   {
14474     RemoveField(x, y);
14475
14476     if (mode != DF_SNAP)
14477     {
14478       GfxElement[x][y] = GFX_ELEMENT(element);
14479       player->is_digging = TRUE;
14480     }
14481
14482     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14483
14484     // use old behaviour for old levels (digging)
14485     if (!level.finish_dig_collect)
14486     {
14487       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14488                                           player->index_bit, dig_side);
14489
14490       // if digging triggered player relocation, finish digging tile
14491       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14492         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14493     }
14494
14495     if (mode == DF_SNAP)
14496     {
14497       if (level.block_snap_field)
14498         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14499       else
14500         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14501
14502       // use old behaviour for old levels (snapping)
14503       if (!level.finish_dig_collect)
14504         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14505                                             player->index_bit, dig_side);
14506     }
14507   }
14508   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14509   {
14510     RemoveField(x, y);
14511
14512     if (is_player && mode != DF_SNAP)
14513     {
14514       GfxElement[x][y] = element;
14515       player->is_collecting = TRUE;
14516     }
14517
14518     if (element == EL_SPEED_PILL)
14519     {
14520       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14521     }
14522     else if (element == EL_EXTRA_TIME && level.time > 0)
14523     {
14524       TimeLeft += level.extra_time;
14525
14526       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14527
14528       DisplayGameControlValues();
14529     }
14530     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14531     {
14532       int shield_time = (element == EL_SHIELD_DEADLY ?
14533                          level.shield_deadly_time :
14534                          level.shield_normal_time);
14535
14536       player->shield_normal_time_left += shield_time;
14537       if (element == EL_SHIELD_DEADLY)
14538         player->shield_deadly_time_left += shield_time;
14539     }
14540     else if (element == EL_DYNAMITE ||
14541              element == EL_EM_DYNAMITE ||
14542              element == EL_SP_DISK_RED)
14543     {
14544       if (player->inventory_size < MAX_INVENTORY_SIZE)
14545         player->inventory_element[player->inventory_size++] = element;
14546
14547       DrawGameDoorValues();
14548     }
14549     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14550     {
14551       player->dynabomb_count++;
14552       player->dynabombs_left++;
14553     }
14554     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14555     {
14556       player->dynabomb_size++;
14557     }
14558     else if (element == EL_DYNABOMB_INCREASE_POWER)
14559     {
14560       player->dynabomb_xl = TRUE;
14561     }
14562     else if (IS_KEY(element))
14563     {
14564       player->key[KEY_NR(element)] = TRUE;
14565
14566       DrawGameDoorValues();
14567     }
14568     else if (element == EL_DC_KEY_WHITE)
14569     {
14570       player->num_white_keys++;
14571
14572       // display white keys?
14573       // DrawGameDoorValues();
14574     }
14575     else if (IS_ENVELOPE(element))
14576     {
14577       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14578
14579       if (!wait_for_snapping)
14580         player->show_envelope = element;
14581     }
14582     else if (element == EL_EMC_LENSES)
14583     {
14584       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14585
14586       RedrawAllInvisibleElementsForLenses();
14587     }
14588     else if (element == EL_EMC_MAGNIFIER)
14589     {
14590       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14591
14592       RedrawAllInvisibleElementsForMagnifier();
14593     }
14594     else if (IS_DROPPABLE(element) ||
14595              IS_THROWABLE(element))     // can be collected and dropped
14596     {
14597       int i;
14598
14599       if (collect_count == 0)
14600         player->inventory_infinite_element = element;
14601       else
14602         for (i = 0; i < collect_count; i++)
14603           if (player->inventory_size < MAX_INVENTORY_SIZE)
14604             player->inventory_element[player->inventory_size++] = element;
14605
14606       DrawGameDoorValues();
14607     }
14608     else if (collect_count > 0)
14609     {
14610       game.gems_still_needed -= collect_count;
14611       if (game.gems_still_needed < 0)
14612         game.gems_still_needed = 0;
14613
14614       game.snapshot.collected_item = TRUE;
14615
14616       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14617
14618       DisplayGameControlValues();
14619     }
14620
14621     RaiseScoreElement(element);
14622     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14623
14624     // use old behaviour for old levels (collecting)
14625     if (!level.finish_dig_collect && is_player)
14626     {
14627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14628                                           player->index_bit, dig_side);
14629
14630       // if collecting triggered player relocation, finish collecting tile
14631       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14632         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14633     }
14634
14635     if (mode == DF_SNAP)
14636     {
14637       if (level.block_snap_field)
14638         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14639       else
14640         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14641
14642       // use old behaviour for old levels (snapping)
14643       if (!level.finish_dig_collect)
14644         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14645                                             player->index_bit, dig_side);
14646     }
14647   }
14648   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14649   {
14650     if (mode == DF_SNAP && element != EL_BD_ROCK)
14651       return MP_NO_ACTION;
14652
14653     if (CAN_FALL(element) && dy)
14654       return MP_NO_ACTION;
14655
14656     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14657         !(element == EL_SPRING && level.use_spring_bug))
14658       return MP_NO_ACTION;
14659
14660     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14661         ((move_direction & MV_VERTICAL &&
14662           ((element_info[element].move_pattern & MV_LEFT &&
14663             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14664            (element_info[element].move_pattern & MV_RIGHT &&
14665             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14666          (move_direction & MV_HORIZONTAL &&
14667           ((element_info[element].move_pattern & MV_UP &&
14668             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14669            (element_info[element].move_pattern & MV_DOWN &&
14670             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14671       return MP_NO_ACTION;
14672
14673     // do not push elements already moving away faster than player
14674     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14675         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14676       return MP_NO_ACTION;
14677
14678     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14679     {
14680       if (player->push_delay_value == -1 || !player_was_pushing)
14681         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14682     }
14683     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14684     {
14685       if (player->push_delay_value == -1)
14686         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14687     }
14688     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14689     {
14690       if (!player->is_pushing)
14691         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14692     }
14693
14694     player->is_pushing = TRUE;
14695     player->is_active = TRUE;
14696
14697     if (!(IN_LEV_FIELD(nextx, nexty) &&
14698           (IS_FREE(nextx, nexty) ||
14699            (IS_SB_ELEMENT(element) &&
14700             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14701            (IS_CUSTOM_ELEMENT(element) &&
14702             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14703       return MP_NO_ACTION;
14704
14705     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14706       return MP_NO_ACTION;
14707
14708     if (player->push_delay == -1)       // new pushing; restart delay
14709       player->push_delay = 0;
14710
14711     if (player->push_delay < player->push_delay_value &&
14712         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14713         element != EL_SPRING && element != EL_BALLOON)
14714     {
14715       // make sure that there is no move delay before next try to push
14716       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14717         player->move_delay = 0;
14718
14719       return MP_NO_ACTION;
14720     }
14721
14722     if (IS_CUSTOM_ELEMENT(element) &&
14723         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14724     {
14725       if (!DigFieldByCE(nextx, nexty, element))
14726         return MP_NO_ACTION;
14727     }
14728
14729     if (IS_SB_ELEMENT(element))
14730     {
14731       boolean sokoban_task_solved = FALSE;
14732
14733       if (element == EL_SOKOBAN_FIELD_FULL)
14734       {
14735         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14736
14737         IncrementSokobanFieldsNeeded();
14738         IncrementSokobanObjectsNeeded();
14739       }
14740
14741       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14742       {
14743         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14744
14745         DecrementSokobanFieldsNeeded();
14746         DecrementSokobanObjectsNeeded();
14747
14748         // sokoban object was pushed from empty field to sokoban field
14749         if (Back[x][y] == EL_EMPTY)
14750           sokoban_task_solved = TRUE;
14751       }
14752
14753       Tile[x][y] = EL_SOKOBAN_OBJECT;
14754
14755       if (Back[x][y] == Back[nextx][nexty])
14756         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14757       else if (Back[x][y] != 0)
14758         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14759                                     ACTION_EMPTYING);
14760       else
14761         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14762                                     ACTION_FILLING);
14763
14764       if (sokoban_task_solved &&
14765           game.sokoban_fields_still_needed == 0 &&
14766           game.sokoban_objects_still_needed == 0 &&
14767           level.auto_exit_sokoban)
14768       {
14769         game.players_still_needed = 0;
14770
14771         LevelSolved();
14772
14773         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14774       }
14775     }
14776     else
14777       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14778
14779     InitMovingField(x, y, move_direction);
14780     GfxAction[x][y] = ACTION_PUSHING;
14781
14782     if (mode == DF_SNAP)
14783       ContinueMoving(x, y);
14784     else
14785       MovPos[x][y] = (dx != 0 ? dx : dy);
14786
14787     Pushed[x][y] = TRUE;
14788     Pushed[nextx][nexty] = TRUE;
14789
14790     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14791       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14792     else
14793       player->push_delay_value = -1;    // get new value later
14794
14795     // check for element change _after_ element has been pushed
14796     if (game.use_change_when_pushing_bug)
14797     {
14798       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14799                                  player->index_bit, dig_side);
14800       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14801                                           player->index_bit, dig_side);
14802     }
14803   }
14804   else if (IS_SWITCHABLE(element))
14805   {
14806     if (PLAYER_SWITCHING(player, x, y))
14807     {
14808       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14809                                           player->index_bit, dig_side);
14810
14811       return MP_ACTION;
14812     }
14813
14814     player->is_switching = TRUE;
14815     player->switch_x = x;
14816     player->switch_y = y;
14817
14818     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14819
14820     if (element == EL_ROBOT_WHEEL)
14821     {
14822       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14823
14824       game.robot_wheel_x = x;
14825       game.robot_wheel_y = y;
14826       game.robot_wheel_active = TRUE;
14827
14828       TEST_DrawLevelField(x, y);
14829     }
14830     else if (element == EL_SP_TERMINAL)
14831     {
14832       int xx, yy;
14833
14834       SCAN_PLAYFIELD(xx, yy)
14835       {
14836         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14837         {
14838           Bang(xx, yy);
14839         }
14840         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14841         {
14842           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14843
14844           ResetGfxAnimation(xx, yy);
14845           TEST_DrawLevelField(xx, yy);
14846         }
14847       }
14848     }
14849     else if (IS_BELT_SWITCH(element))
14850     {
14851       ToggleBeltSwitch(x, y);
14852     }
14853     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14854              element == EL_SWITCHGATE_SWITCH_DOWN ||
14855              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14856              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14857     {
14858       ToggleSwitchgateSwitch();
14859     }
14860     else if (element == EL_LIGHT_SWITCH ||
14861              element == EL_LIGHT_SWITCH_ACTIVE)
14862     {
14863       ToggleLightSwitch(x, y);
14864     }
14865     else if (element == EL_TIMEGATE_SWITCH ||
14866              element == EL_DC_TIMEGATE_SWITCH)
14867     {
14868       ActivateTimegateSwitch(x, y);
14869     }
14870     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14871              element == EL_BALLOON_SWITCH_RIGHT ||
14872              element == EL_BALLOON_SWITCH_UP    ||
14873              element == EL_BALLOON_SWITCH_DOWN  ||
14874              element == EL_BALLOON_SWITCH_NONE  ||
14875              element == EL_BALLOON_SWITCH_ANY)
14876     {
14877       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14878                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14879                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14880                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14881                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14882                              move_direction);
14883     }
14884     else if (element == EL_LAMP)
14885     {
14886       Tile[x][y] = EL_LAMP_ACTIVE;
14887       game.lights_still_needed--;
14888
14889       ResetGfxAnimation(x, y);
14890       TEST_DrawLevelField(x, y);
14891     }
14892     else if (element == EL_TIME_ORB_FULL)
14893     {
14894       Tile[x][y] = EL_TIME_ORB_EMPTY;
14895
14896       if (level.time > 0 || level.use_time_orb_bug)
14897       {
14898         TimeLeft += level.time_orb_time;
14899         game.no_level_time_limit = FALSE;
14900
14901         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14902
14903         DisplayGameControlValues();
14904       }
14905
14906       ResetGfxAnimation(x, y);
14907       TEST_DrawLevelField(x, y);
14908     }
14909     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14910              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14911     {
14912       int xx, yy;
14913
14914       game.ball_active = !game.ball_active;
14915
14916       SCAN_PLAYFIELD(xx, yy)
14917       {
14918         int e = Tile[xx][yy];
14919
14920         if (game.ball_active)
14921         {
14922           if (e == EL_EMC_MAGIC_BALL)
14923             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14924           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14925             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14926         }
14927         else
14928         {
14929           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14930             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14931           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14932             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14933         }
14934       }
14935     }
14936
14937     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14938                                         player->index_bit, dig_side);
14939
14940     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14941                                         player->index_bit, dig_side);
14942
14943     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14944                                         player->index_bit, dig_side);
14945
14946     return MP_ACTION;
14947   }
14948   else
14949   {
14950     if (!PLAYER_SWITCHING(player, x, y))
14951     {
14952       player->is_switching = TRUE;
14953       player->switch_x = x;
14954       player->switch_y = y;
14955
14956       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14957                                  player->index_bit, dig_side);
14958       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14959                                           player->index_bit, dig_side);
14960
14961       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14962                                  player->index_bit, dig_side);
14963       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14964                                           player->index_bit, dig_side);
14965     }
14966
14967     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14968                                player->index_bit, dig_side);
14969     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14970                                         player->index_bit, dig_side);
14971
14972     return MP_NO_ACTION;
14973   }
14974
14975   player->push_delay = -1;
14976
14977   if (is_player)                // function can also be called by EL_PENGUIN
14978   {
14979     if (Tile[x][y] != element)          // really digged/collected something
14980     {
14981       player->is_collecting = !player->is_digging;
14982       player->is_active = TRUE;
14983
14984       player->last_removed_element = element;
14985     }
14986   }
14987
14988   return MP_MOVING;
14989 }
14990
14991 static boolean DigFieldByCE(int x, int y, int digging_element)
14992 {
14993   int element = Tile[x][y];
14994
14995   if (!IS_FREE(x, y))
14996   {
14997     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14998                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14999                   ACTION_BREAKING);
15000
15001     // no element can dig solid indestructible elements
15002     if (IS_INDESTRUCTIBLE(element) &&
15003         !IS_DIGGABLE(element) &&
15004         !IS_COLLECTIBLE(element))
15005       return FALSE;
15006
15007     if (AmoebaNr[x][y] &&
15008         (element == EL_AMOEBA_FULL ||
15009          element == EL_BD_AMOEBA ||
15010          element == EL_AMOEBA_GROWING))
15011     {
15012       AmoebaCnt[AmoebaNr[x][y]]--;
15013       AmoebaCnt2[AmoebaNr[x][y]]--;
15014     }
15015
15016     if (IS_MOVING(x, y))
15017       RemoveMovingField(x, y);
15018     else
15019     {
15020       RemoveField(x, y);
15021       TEST_DrawLevelField(x, y);
15022     }
15023
15024     // if digged element was about to explode, prevent the explosion
15025     ExplodeField[x][y] = EX_TYPE_NONE;
15026
15027     PlayLevelSoundAction(x, y, action);
15028   }
15029
15030   Store[x][y] = EL_EMPTY;
15031
15032   // this makes it possible to leave the removed element again
15033   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15034     Store[x][y] = element;
15035
15036   return TRUE;
15037 }
15038
15039 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15040 {
15041   int jx = player->jx, jy = player->jy;
15042   int x = jx + dx, y = jy + dy;
15043   int snap_direction = (dx == -1 ? MV_LEFT  :
15044                         dx == +1 ? MV_RIGHT :
15045                         dy == -1 ? MV_UP    :
15046                         dy == +1 ? MV_DOWN  : MV_NONE);
15047   boolean can_continue_snapping = (level.continuous_snapping &&
15048                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15049
15050   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15051     return FALSE;
15052
15053   if (!player->active || !IN_LEV_FIELD(x, y))
15054     return FALSE;
15055
15056   if (dx && dy)
15057     return FALSE;
15058
15059   if (!dx && !dy)
15060   {
15061     if (player->MovPos == 0)
15062       player->is_pushing = FALSE;
15063
15064     player->is_snapping = FALSE;
15065
15066     if (player->MovPos == 0)
15067     {
15068       player->is_moving = FALSE;
15069       player->is_digging = FALSE;
15070       player->is_collecting = FALSE;
15071     }
15072
15073     return FALSE;
15074   }
15075
15076   // prevent snapping with already pressed snap key when not allowed
15077   if (player->is_snapping && !can_continue_snapping)
15078     return FALSE;
15079
15080   player->MovDir = snap_direction;
15081
15082   if (player->MovPos == 0)
15083   {
15084     player->is_moving = FALSE;
15085     player->is_digging = FALSE;
15086     player->is_collecting = FALSE;
15087   }
15088
15089   player->is_dropping = FALSE;
15090   player->is_dropping_pressed = FALSE;
15091   player->drop_pressed_delay = 0;
15092
15093   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15094     return FALSE;
15095
15096   player->is_snapping = TRUE;
15097   player->is_active = TRUE;
15098
15099   if (player->MovPos == 0)
15100   {
15101     player->is_moving = FALSE;
15102     player->is_digging = FALSE;
15103     player->is_collecting = FALSE;
15104   }
15105
15106   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15107     TEST_DrawLevelField(player->last_jx, player->last_jy);
15108
15109   TEST_DrawLevelField(x, y);
15110
15111   return TRUE;
15112 }
15113
15114 static boolean DropElement(struct PlayerInfo *player)
15115 {
15116   int old_element, new_element;
15117   int dropx = player->jx, dropy = player->jy;
15118   int drop_direction = player->MovDir;
15119   int drop_side = drop_direction;
15120   int drop_element = get_next_dropped_element(player);
15121
15122   /* do not drop an element on top of another element; when holding drop key
15123      pressed without moving, dropped element must move away before the next
15124      element can be dropped (this is especially important if the next element
15125      is dynamite, which can be placed on background for historical reasons) */
15126   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15127     return MP_ACTION;
15128
15129   if (IS_THROWABLE(drop_element))
15130   {
15131     dropx += GET_DX_FROM_DIR(drop_direction);
15132     dropy += GET_DY_FROM_DIR(drop_direction);
15133
15134     if (!IN_LEV_FIELD(dropx, dropy))
15135       return FALSE;
15136   }
15137
15138   old_element = Tile[dropx][dropy];     // old element at dropping position
15139   new_element = drop_element;           // default: no change when dropping
15140
15141   // check if player is active, not moving and ready to drop
15142   if (!player->active || player->MovPos || player->drop_delay > 0)
15143     return FALSE;
15144
15145   // check if player has anything that can be dropped
15146   if (new_element == EL_UNDEFINED)
15147     return FALSE;
15148
15149   // only set if player has anything that can be dropped
15150   player->is_dropping_pressed = TRUE;
15151
15152   // check if drop key was pressed long enough for EM style dynamite
15153   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15154     return FALSE;
15155
15156   // check if anything can be dropped at the current position
15157   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15158     return FALSE;
15159
15160   // collected custom elements can only be dropped on empty fields
15161   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15162     return FALSE;
15163
15164   if (old_element != EL_EMPTY)
15165     Back[dropx][dropy] = old_element;   // store old element on this field
15166
15167   ResetGfxAnimation(dropx, dropy);
15168   ResetRandomAnimationValue(dropx, dropy);
15169
15170   if (player->inventory_size > 0 ||
15171       player->inventory_infinite_element != EL_UNDEFINED)
15172   {
15173     if (player->inventory_size > 0)
15174     {
15175       player->inventory_size--;
15176
15177       DrawGameDoorValues();
15178
15179       if (new_element == EL_DYNAMITE)
15180         new_element = EL_DYNAMITE_ACTIVE;
15181       else if (new_element == EL_EM_DYNAMITE)
15182         new_element = EL_EM_DYNAMITE_ACTIVE;
15183       else if (new_element == EL_SP_DISK_RED)
15184         new_element = EL_SP_DISK_RED_ACTIVE;
15185     }
15186
15187     Tile[dropx][dropy] = new_element;
15188
15189     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15190       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15191                           el2img(Tile[dropx][dropy]), 0);
15192
15193     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15194
15195     // needed if previous element just changed to "empty" in the last frame
15196     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15197
15198     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15199                                player->index_bit, drop_side);
15200     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15201                                         CE_PLAYER_DROPS_X,
15202                                         player->index_bit, drop_side);
15203
15204     TestIfElementTouchesCustomElement(dropx, dropy);
15205   }
15206   else          // player is dropping a dyna bomb
15207   {
15208     player->dynabombs_left--;
15209
15210     Tile[dropx][dropy] = new_element;
15211
15212     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15213       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15214                           el2img(Tile[dropx][dropy]), 0);
15215
15216     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15217   }
15218
15219   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15220     InitField_WithBug1(dropx, dropy, FALSE);
15221
15222   new_element = Tile[dropx][dropy];     // element might have changed
15223
15224   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15225       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15226   {
15227     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15228       MovDir[dropx][dropy] = drop_direction;
15229
15230     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15231
15232     // do not cause impact style collision by dropping elements that can fall
15233     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15234   }
15235
15236   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15237   player->is_dropping = TRUE;
15238
15239   player->drop_pressed_delay = 0;
15240   player->is_dropping_pressed = FALSE;
15241
15242   player->drop_x = dropx;
15243   player->drop_y = dropy;
15244
15245   return TRUE;
15246 }
15247
15248 // ----------------------------------------------------------------------------
15249 // game sound playing functions
15250 // ----------------------------------------------------------------------------
15251
15252 static int *loop_sound_frame = NULL;
15253 static int *loop_sound_volume = NULL;
15254
15255 void InitPlayLevelSound(void)
15256 {
15257   int num_sounds = getSoundListSize();
15258
15259   checked_free(loop_sound_frame);
15260   checked_free(loop_sound_volume);
15261
15262   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15263   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15264 }
15265
15266 static void PlayLevelSound(int x, int y, int nr)
15267 {
15268   int sx = SCREENX(x), sy = SCREENY(y);
15269   int volume, stereo_position;
15270   int max_distance = 8;
15271   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15272
15273   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15274       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15275     return;
15276
15277   if (!IN_LEV_FIELD(x, y) ||
15278       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15279       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15280     return;
15281
15282   volume = SOUND_MAX_VOLUME;
15283
15284   if (!IN_SCR_FIELD(sx, sy))
15285   {
15286     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15287     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15288
15289     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15290   }
15291
15292   stereo_position = (SOUND_MAX_LEFT +
15293                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15294                      (SCR_FIELDX + 2 * max_distance));
15295
15296   if (IS_LOOP_SOUND(nr))
15297   {
15298     /* This assures that quieter loop sounds do not overwrite louder ones,
15299        while restarting sound volume comparison with each new game frame. */
15300
15301     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15302       return;
15303
15304     loop_sound_volume[nr] = volume;
15305     loop_sound_frame[nr] = FrameCounter;
15306   }
15307
15308   PlaySoundExt(nr, volume, stereo_position, type);
15309 }
15310
15311 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15312 {
15313   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15314                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15315                  y < LEVELY(BY1) ? LEVELY(BY1) :
15316                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15317                  sound_action);
15318 }
15319
15320 static void PlayLevelSoundAction(int x, int y, int action)
15321 {
15322   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15323 }
15324
15325 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15326 {
15327   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15328
15329   if (sound_effect != SND_UNDEFINED)
15330     PlayLevelSound(x, y, sound_effect);
15331 }
15332
15333 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15334                                               int action)
15335 {
15336   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15337
15338   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15339     PlayLevelSound(x, y, sound_effect);
15340 }
15341
15342 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15343 {
15344   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15345
15346   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15347     PlayLevelSound(x, y, sound_effect);
15348 }
15349
15350 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15351 {
15352   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15353
15354   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15355     StopSound(sound_effect);
15356 }
15357
15358 static int getLevelMusicNr(void)
15359 {
15360   int level_pos = level_nr - leveldir_current->first_level;
15361
15362   if (levelset.music[level_nr] != MUS_UNDEFINED)
15363     return levelset.music[level_nr];            // from config file
15364   else
15365     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15366 }
15367
15368 static void FadeLevelSounds(void)
15369 {
15370   FadeSounds();
15371 }
15372
15373 static void FadeLevelMusic(void)
15374 {
15375   int music_nr = getLevelMusicNr();
15376   char *curr_music = getCurrentlyPlayingMusicFilename();
15377   char *next_music = getMusicInfoEntryFilename(music_nr);
15378
15379   if (!strEqual(curr_music, next_music))
15380     FadeMusic();
15381 }
15382
15383 void FadeLevelSoundsAndMusic(void)
15384 {
15385   FadeLevelSounds();
15386   FadeLevelMusic();
15387 }
15388
15389 static void PlayLevelMusic(void)
15390 {
15391   int music_nr = getLevelMusicNr();
15392   char *curr_music = getCurrentlyPlayingMusicFilename();
15393   char *next_music = getMusicInfoEntryFilename(music_nr);
15394
15395   if (!strEqual(curr_music, next_music))
15396     PlayMusicLoop(music_nr);
15397 }
15398
15399 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15400 {
15401   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15402   int offset = 0;
15403   int x = xx - offset;
15404   int y = yy - offset;
15405
15406   switch (sample)
15407   {
15408     case SOUND_blank:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15410       break;
15411
15412     case SOUND_roll:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15414       break;
15415
15416     case SOUND_stone:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15418       break;
15419
15420     case SOUND_nut:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15422       break;
15423
15424     case SOUND_crack:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15426       break;
15427
15428     case SOUND_bug:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15430       break;
15431
15432     case SOUND_tank:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15434       break;
15435
15436     case SOUND_android_clone:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15438       break;
15439
15440     case SOUND_android_move:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15442       break;
15443
15444     case SOUND_spring:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15446       break;
15447
15448     case SOUND_slurp:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15450       break;
15451
15452     case SOUND_eater:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15454       break;
15455
15456     case SOUND_eater_eat:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15458       break;
15459
15460     case SOUND_alien:
15461       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15462       break;
15463
15464     case SOUND_collect:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15466       break;
15467
15468     case SOUND_diamond:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15470       break;
15471
15472     case SOUND_squash:
15473       // !!! CHECK THIS !!!
15474 #if 1
15475       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15476 #else
15477       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15478 #endif
15479       break;
15480
15481     case SOUND_wonderfall:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15483       break;
15484
15485     case SOUND_drip:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15487       break;
15488
15489     case SOUND_push:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15491       break;
15492
15493     case SOUND_dirt:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15495       break;
15496
15497     case SOUND_acid:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15499       break;
15500
15501     case SOUND_ball:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15503       break;
15504
15505     case SOUND_slide:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15507       break;
15508
15509     case SOUND_wonder:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15511       break;
15512
15513     case SOUND_door:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15515       break;
15516
15517     case SOUND_exit_open:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15519       break;
15520
15521     case SOUND_exit_leave:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15523       break;
15524
15525     case SOUND_dynamite:
15526       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15527       break;
15528
15529     case SOUND_tick:
15530       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15531       break;
15532
15533     case SOUND_press:
15534       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15535       break;
15536
15537     case SOUND_wheel:
15538       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15539       break;
15540
15541     case SOUND_boom:
15542       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15543       break;
15544
15545     case SOUND_die:
15546       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15547       break;
15548
15549     case SOUND_time:
15550       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15551       break;
15552
15553     default:
15554       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15555       break;
15556   }
15557 }
15558
15559 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15560 {
15561   int element = map_element_SP_to_RND(element_sp);
15562   int action = map_action_SP_to_RND(action_sp);
15563   int offset = (setup.sp_show_border_elements ? 0 : 1);
15564   int x = xx - offset;
15565   int y = yy - offset;
15566
15567   PlayLevelSoundElementAction(x, y, element, action);
15568 }
15569
15570 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15571 {
15572   int element = map_element_MM_to_RND(element_mm);
15573   int action = map_action_MM_to_RND(action_mm);
15574   int offset = 0;
15575   int x = xx - offset;
15576   int y = yy - offset;
15577
15578   if (!IS_MM_ELEMENT(element))
15579     element = EL_MM_DEFAULT;
15580
15581   PlayLevelSoundElementAction(x, y, element, action);
15582 }
15583
15584 void PlaySound_MM(int sound_mm)
15585 {
15586   int sound = map_sound_MM_to_RND(sound_mm);
15587
15588   if (sound == SND_UNDEFINED)
15589     return;
15590
15591   PlaySound(sound);
15592 }
15593
15594 void PlaySoundLoop_MM(int sound_mm)
15595 {
15596   int sound = map_sound_MM_to_RND(sound_mm);
15597
15598   if (sound == SND_UNDEFINED)
15599     return;
15600
15601   PlaySoundLoop(sound);
15602 }
15603
15604 void StopSound_MM(int sound_mm)
15605 {
15606   int sound = map_sound_MM_to_RND(sound_mm);
15607
15608   if (sound == SND_UNDEFINED)
15609     return;
15610
15611   StopSound(sound);
15612 }
15613
15614 void RaiseScore(int value)
15615 {
15616   game.score += value;
15617
15618   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15619
15620   DisplayGameControlValues();
15621 }
15622
15623 void RaiseScoreElement(int element)
15624 {
15625   switch (element)
15626   {
15627     case EL_EMERALD:
15628     case EL_BD_DIAMOND:
15629     case EL_EMERALD_YELLOW:
15630     case EL_EMERALD_RED:
15631     case EL_EMERALD_PURPLE:
15632     case EL_SP_INFOTRON:
15633       RaiseScore(level.score[SC_EMERALD]);
15634       break;
15635     case EL_DIAMOND:
15636       RaiseScore(level.score[SC_DIAMOND]);
15637       break;
15638     case EL_CRYSTAL:
15639       RaiseScore(level.score[SC_CRYSTAL]);
15640       break;
15641     case EL_PEARL:
15642       RaiseScore(level.score[SC_PEARL]);
15643       break;
15644     case EL_BUG:
15645     case EL_BD_BUTTERFLY:
15646     case EL_SP_ELECTRON:
15647       RaiseScore(level.score[SC_BUG]);
15648       break;
15649     case EL_SPACESHIP:
15650     case EL_BD_FIREFLY:
15651     case EL_SP_SNIKSNAK:
15652       RaiseScore(level.score[SC_SPACESHIP]);
15653       break;
15654     case EL_YAMYAM:
15655     case EL_DARK_YAMYAM:
15656       RaiseScore(level.score[SC_YAMYAM]);
15657       break;
15658     case EL_ROBOT:
15659       RaiseScore(level.score[SC_ROBOT]);
15660       break;
15661     case EL_PACMAN:
15662       RaiseScore(level.score[SC_PACMAN]);
15663       break;
15664     case EL_NUT:
15665       RaiseScore(level.score[SC_NUT]);
15666       break;
15667     case EL_DYNAMITE:
15668     case EL_EM_DYNAMITE:
15669     case EL_SP_DISK_RED:
15670     case EL_DYNABOMB_INCREASE_NUMBER:
15671     case EL_DYNABOMB_INCREASE_SIZE:
15672     case EL_DYNABOMB_INCREASE_POWER:
15673       RaiseScore(level.score[SC_DYNAMITE]);
15674       break;
15675     case EL_SHIELD_NORMAL:
15676     case EL_SHIELD_DEADLY:
15677       RaiseScore(level.score[SC_SHIELD]);
15678       break;
15679     case EL_EXTRA_TIME:
15680       RaiseScore(level.extra_time_score);
15681       break;
15682     case EL_KEY_1:
15683     case EL_KEY_2:
15684     case EL_KEY_3:
15685     case EL_KEY_4:
15686     case EL_EM_KEY_1:
15687     case EL_EM_KEY_2:
15688     case EL_EM_KEY_3:
15689     case EL_EM_KEY_4:
15690     case EL_EMC_KEY_5:
15691     case EL_EMC_KEY_6:
15692     case EL_EMC_KEY_7:
15693     case EL_EMC_KEY_8:
15694     case EL_DC_KEY_WHITE:
15695       RaiseScore(level.score[SC_KEY]);
15696       break;
15697     default:
15698       RaiseScore(element_info[element].collect_score);
15699       break;
15700   }
15701 }
15702
15703 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15704 {
15705   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15706   {
15707     if (!quick_quit)
15708     {
15709       // prevent short reactivation of overlay buttons while closing door
15710       SetOverlayActive(FALSE);
15711       UnmapGameButtons();
15712
15713       // door may still be open due to skipped or envelope style request
15714       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15715     }
15716
15717     if (network.enabled)
15718     {
15719       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15720     }
15721     else
15722     {
15723       if (quick_quit)
15724         FadeSkipNextFadeIn();
15725
15726       SetGameStatus(GAME_MODE_MAIN);
15727
15728       DrawMainMenu();
15729     }
15730   }
15731   else          // continue playing the game
15732   {
15733     if (tape.playing && tape.deactivate_display)
15734       TapeDeactivateDisplayOff(TRUE);
15735
15736     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15737
15738     if (tape.playing && tape.deactivate_display)
15739       TapeDeactivateDisplayOn();
15740   }
15741 }
15742
15743 void RequestQuitGame(boolean escape_key_pressed)
15744 {
15745   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15746   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15747                         level_editor_test_game);
15748   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15749                           quick_quit || score_info_tape_play);
15750
15751   RequestQuitGameExt(skip_request, quick_quit,
15752                      "Do you really want to quit the game?");
15753 }
15754
15755 static char *getRestartGameMessage(void)
15756 {
15757   boolean play_again = hasStartedNetworkGame();
15758   static char message[MAX_OUTPUT_LINESIZE];
15759   char *game_over_text = "Game over!";
15760   char *play_again_text = " Play it again?";
15761
15762   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15763       game_mm.game_over_message != NULL)
15764     game_over_text = game_mm.game_over_message;
15765
15766   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15767            (play_again ? play_again_text : ""));
15768
15769   return message;
15770 }
15771
15772 static void RequestRestartGame(void)
15773 {
15774   char *message = getRestartGameMessage();
15775   boolean has_started_game = hasStartedNetworkGame();
15776   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15777   int door_state = DOOR_CLOSE_1;
15778
15779   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15780   {
15781     CloseDoor(door_state);
15782
15783     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15784   }
15785   else
15786   {
15787     // if game was invoked from level editor, also close tape recorder door
15788     if (level_editor_test_game)
15789       door_state = DOOR_CLOSE_ALL;
15790
15791     CloseDoor(door_state);
15792
15793     SetGameStatus(GAME_MODE_MAIN);
15794
15795     DrawMainMenu();
15796   }
15797 }
15798
15799 boolean CheckRestartGame(void)
15800 {
15801   static int game_over_delay = 0;
15802   int game_over_delay_value = 50;
15803   boolean game_over = checkGameFailed();
15804
15805   if (!game_over)
15806   {
15807     game_over_delay = game_over_delay_value;
15808
15809     return FALSE;
15810   }
15811
15812   if (game_over_delay > 0)
15813   {
15814     if (game_over_delay == game_over_delay_value / 2)
15815       PlaySound(SND_GAME_LOSING);
15816
15817     game_over_delay--;
15818
15819     return FALSE;
15820   }
15821
15822   // do not ask to play again if request dialog is already active
15823   if (game.request_active)
15824     return FALSE;
15825
15826   // do not ask to play again if request dialog already handled
15827   if (game.RestartGameRequested)
15828     return FALSE;
15829
15830   // do not ask to play again if game was never actually played
15831   if (!game.GamePlayed)
15832     return FALSE;
15833
15834   // do not ask to play again if this was disabled in setup menu
15835   if (!setup.ask_on_game_over)
15836     return FALSE;
15837
15838   game.RestartGameRequested = TRUE;
15839
15840   RequestRestartGame();
15841
15842   return TRUE;
15843 }
15844
15845 boolean checkGameSolved(void)
15846 {
15847   // set for all game engines if level was solved
15848   return game.LevelSolved_GameEnd;
15849 }
15850
15851 boolean checkGameFailed(void)
15852 {
15853   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15854     return (game_em.game_over && !game_em.level_solved);
15855   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15856     return (game_sp.game_over && !game_sp.level_solved);
15857   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15858     return (game_mm.game_over && !game_mm.level_solved);
15859   else                          // GAME_ENGINE_TYPE_RND
15860     return (game.GameOver && !game.LevelSolved);
15861 }
15862
15863 boolean checkGameEnded(void)
15864 {
15865   return (checkGameSolved() || checkGameFailed());
15866 }
15867
15868
15869 // ----------------------------------------------------------------------------
15870 // random generator functions
15871 // ----------------------------------------------------------------------------
15872
15873 unsigned int InitEngineRandom_RND(int seed)
15874 {
15875   game.num_random_calls = 0;
15876
15877   return InitEngineRandom(seed);
15878 }
15879
15880 unsigned int RND(int max)
15881 {
15882   if (max > 0)
15883   {
15884     game.num_random_calls++;
15885
15886     return GetEngineRandom(max);
15887   }
15888
15889   return 0;
15890 }
15891
15892
15893 // ----------------------------------------------------------------------------
15894 // game engine snapshot handling functions
15895 // ----------------------------------------------------------------------------
15896
15897 struct EngineSnapshotInfo
15898 {
15899   // runtime values for custom element collect score
15900   int collect_score[NUM_CUSTOM_ELEMENTS];
15901
15902   // runtime values for group element choice position
15903   int choice_pos[NUM_GROUP_ELEMENTS];
15904
15905   // runtime values for belt position animations
15906   int belt_graphic[4][NUM_BELT_PARTS];
15907   int belt_anim_mode[4][NUM_BELT_PARTS];
15908 };
15909
15910 static struct EngineSnapshotInfo engine_snapshot_rnd;
15911 static char *snapshot_level_identifier = NULL;
15912 static int snapshot_level_nr = -1;
15913
15914 static void SaveEngineSnapshotValues_RND(void)
15915 {
15916   static int belt_base_active_element[4] =
15917   {
15918     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15919     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15920     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15921     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15922   };
15923   int i, j;
15924
15925   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15926   {
15927     int element = EL_CUSTOM_START + i;
15928
15929     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15930   }
15931
15932   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15933   {
15934     int element = EL_GROUP_START + i;
15935
15936     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15937   }
15938
15939   for (i = 0; i < 4; i++)
15940   {
15941     for (j = 0; j < NUM_BELT_PARTS; j++)
15942     {
15943       int element = belt_base_active_element[i] + j;
15944       int graphic = el2img(element);
15945       int anim_mode = graphic_info[graphic].anim_mode;
15946
15947       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15948       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15949     }
15950   }
15951 }
15952
15953 static void LoadEngineSnapshotValues_RND(void)
15954 {
15955   unsigned int num_random_calls = game.num_random_calls;
15956   int i, j;
15957
15958   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15959   {
15960     int element = EL_CUSTOM_START + i;
15961
15962     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15963   }
15964
15965   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15966   {
15967     int element = EL_GROUP_START + i;
15968
15969     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15970   }
15971
15972   for (i = 0; i < 4; i++)
15973   {
15974     for (j = 0; j < NUM_BELT_PARTS; j++)
15975     {
15976       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15977       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15978
15979       graphic_info[graphic].anim_mode = anim_mode;
15980     }
15981   }
15982
15983   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15984   {
15985     InitRND(tape.random_seed);
15986     for (i = 0; i < num_random_calls; i++)
15987       RND(1);
15988   }
15989
15990   if (game.num_random_calls != num_random_calls)
15991   {
15992     Error("number of random calls out of sync");
15993     Error("number of random calls should be %d", num_random_calls);
15994     Error("number of random calls is %d", game.num_random_calls);
15995
15996     Fail("this should not happen -- please debug");
15997   }
15998 }
15999
16000 void FreeEngineSnapshotSingle(void)
16001 {
16002   FreeSnapshotSingle();
16003
16004   setString(&snapshot_level_identifier, NULL);
16005   snapshot_level_nr = -1;
16006 }
16007
16008 void FreeEngineSnapshotList(void)
16009 {
16010   FreeSnapshotList();
16011 }
16012
16013 static ListNode *SaveEngineSnapshotBuffers(void)
16014 {
16015   ListNode *buffers = NULL;
16016
16017   // copy some special values to a structure better suited for the snapshot
16018
16019   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16020     SaveEngineSnapshotValues_RND();
16021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16022     SaveEngineSnapshotValues_EM();
16023   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16024     SaveEngineSnapshotValues_SP(&buffers);
16025   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16026     SaveEngineSnapshotValues_MM();
16027
16028   // save values stored in special snapshot structure
16029
16030   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16031     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16032   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16033     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16034   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16035     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16036   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16037     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16038
16039   // save further RND engine values
16040
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16044
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16051
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16055
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16057
16058   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16060
16061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16062   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16063   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16065   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16066   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16067   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16069   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16070   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16071   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16072   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16073   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16074   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16075   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16076   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16077   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16078   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16079
16080   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16081   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16082
16083   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16084   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16085   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16086
16087   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16088   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16089
16090   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16091   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16092   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16093   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16094   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16095   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16096
16097   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16098   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16099
16100 #if 0
16101   ListNode *node = engine_snapshot_list_rnd;
16102   int num_bytes = 0;
16103
16104   while (node != NULL)
16105   {
16106     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16107
16108     node = node->next;
16109   }
16110
16111   Debug("game:playing:SaveEngineSnapshotBuffers",
16112         "size of engine snapshot: %d bytes", num_bytes);
16113 #endif
16114
16115   return buffers;
16116 }
16117
16118 void SaveEngineSnapshotSingle(void)
16119 {
16120   ListNode *buffers = SaveEngineSnapshotBuffers();
16121
16122   // finally save all snapshot buffers to single snapshot
16123   SaveSnapshotSingle(buffers);
16124
16125   // save level identification information
16126   setString(&snapshot_level_identifier, leveldir_current->identifier);
16127   snapshot_level_nr = level_nr;
16128 }
16129
16130 boolean CheckSaveEngineSnapshotToList(void)
16131 {
16132   boolean save_snapshot =
16133     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16134      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16135       game.snapshot.changed_action) ||
16136      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16137       game.snapshot.collected_item));
16138
16139   game.snapshot.changed_action = FALSE;
16140   game.snapshot.collected_item = FALSE;
16141   game.snapshot.save_snapshot = save_snapshot;
16142
16143   return save_snapshot;
16144 }
16145
16146 void SaveEngineSnapshotToList(void)
16147 {
16148   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16149       tape.quick_resume)
16150     return;
16151
16152   ListNode *buffers = SaveEngineSnapshotBuffers();
16153
16154   // finally save all snapshot buffers to snapshot list
16155   SaveSnapshotToList(buffers);
16156 }
16157
16158 void SaveEngineSnapshotToListInitial(void)
16159 {
16160   FreeEngineSnapshotList();
16161
16162   SaveEngineSnapshotToList();
16163 }
16164
16165 static void LoadEngineSnapshotValues(void)
16166 {
16167   // restore special values from snapshot structure
16168
16169   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16170     LoadEngineSnapshotValues_RND();
16171   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16172     LoadEngineSnapshotValues_EM();
16173   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16174     LoadEngineSnapshotValues_SP();
16175   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16176     LoadEngineSnapshotValues_MM();
16177 }
16178
16179 void LoadEngineSnapshotSingle(void)
16180 {
16181   LoadSnapshotSingle();
16182
16183   LoadEngineSnapshotValues();
16184 }
16185
16186 static void LoadEngineSnapshot_Undo(int steps)
16187 {
16188   LoadSnapshotFromList_Older(steps);
16189
16190   LoadEngineSnapshotValues();
16191 }
16192
16193 static void LoadEngineSnapshot_Redo(int steps)
16194 {
16195   LoadSnapshotFromList_Newer(steps);
16196
16197   LoadEngineSnapshotValues();
16198 }
16199
16200 boolean CheckEngineSnapshotSingle(void)
16201 {
16202   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16203           snapshot_level_nr == level_nr);
16204 }
16205
16206 boolean CheckEngineSnapshotList(void)
16207 {
16208   return CheckSnapshotList();
16209 }
16210
16211
16212 // ---------- new game button stuff -------------------------------------------
16213
16214 static struct
16215 {
16216   int graphic;
16217   struct XY *pos;
16218   int gadget_id;
16219   boolean *setup_value;
16220   boolean allowed_on_tape;
16221   boolean is_touch_button;
16222   char *infotext;
16223 } gamebutton_info[NUM_GAME_BUTTONS] =
16224 {
16225   {
16226     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16227     GAME_CTRL_ID_STOP,                          NULL,
16228     TRUE, FALSE,                                "stop game"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16232     GAME_CTRL_ID_PAUSE,                         NULL,
16233     TRUE, FALSE,                                "pause game"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16237     GAME_CTRL_ID_PLAY,                          NULL,
16238     TRUE, FALSE,                                "play game"
16239   },
16240   {
16241     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16242     GAME_CTRL_ID_UNDO,                          NULL,
16243     TRUE, FALSE,                                "undo step"
16244   },
16245   {
16246     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16247     GAME_CTRL_ID_REDO,                          NULL,
16248     TRUE, FALSE,                                "redo step"
16249   },
16250   {
16251     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16252     GAME_CTRL_ID_SAVE,                          NULL,
16253     TRUE, FALSE,                                "save game"
16254   },
16255   {
16256     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16257     GAME_CTRL_ID_PAUSE2,                        NULL,
16258     TRUE, FALSE,                                "pause game"
16259   },
16260   {
16261     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16262     GAME_CTRL_ID_LOAD,                          NULL,
16263     TRUE, FALSE,                                "load game"
16264   },
16265   {
16266     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16267     GAME_CTRL_ID_RESTART,                       NULL,
16268     TRUE, FALSE,                                "restart game"
16269   },
16270   {
16271     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16272     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16273     FALSE, FALSE,                               "stop game"
16274   },
16275   {
16276     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16277     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16278     FALSE, FALSE,                               "pause game"
16279   },
16280   {
16281     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16282     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16283     FALSE, FALSE,                               "play game"
16284   },
16285   {
16286     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16287     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16288     FALSE, FALSE,                               "restart game"
16289   },
16290   {
16291     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16292     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16293     FALSE, TRUE,                                "stop game"
16294   },
16295   {
16296     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16297     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16298     FALSE, TRUE,                                "pause game"
16299   },
16300   {
16301     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16302     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16303     FALSE, TRUE,                                "restart game"
16304   },
16305   {
16306     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16307     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16308     TRUE, FALSE,                                "background music on/off"
16309   },
16310   {
16311     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16312     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16313     TRUE, FALSE,                                "sound loops on/off"
16314   },
16315   {
16316     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16317     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16318     TRUE, FALSE,                                "normal sounds on/off"
16319   },
16320   {
16321     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16322     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16323     FALSE, FALSE,                               "background music on/off"
16324   },
16325   {
16326     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16327     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16328     FALSE, FALSE,                               "sound loops on/off"
16329   },
16330   {
16331     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16332     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16333     FALSE, FALSE,                               "normal sounds on/off"
16334   }
16335 };
16336
16337 void CreateGameButtons(void)
16338 {
16339   int i;
16340
16341   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16342   {
16343     int graphic = gamebutton_info[i].graphic;
16344     struct GraphicInfo *gfx = &graphic_info[graphic];
16345     struct XY *pos = gamebutton_info[i].pos;
16346     struct GadgetInfo *gi;
16347     int button_type;
16348     boolean checked;
16349     unsigned int event_mask;
16350     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16351     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16352     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16353     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16354     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16355     int gd_x   = gfx->src_x;
16356     int gd_y   = gfx->src_y;
16357     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16358     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16359     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16360     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16361     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16362     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16363     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16364     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16365     int id = i;
16366
16367     // do not use touch buttons if overlay touch buttons are disabled
16368     if (is_touch_button && !setup.touch.overlay_buttons)
16369       continue;
16370
16371     if (gfx->bitmap == NULL)
16372     {
16373       game_gadget[id] = NULL;
16374
16375       continue;
16376     }
16377
16378     if (id == GAME_CTRL_ID_STOP ||
16379         id == GAME_CTRL_ID_PANEL_STOP ||
16380         id == GAME_CTRL_ID_TOUCH_STOP ||
16381         id == GAME_CTRL_ID_PLAY ||
16382         id == GAME_CTRL_ID_PANEL_PLAY ||
16383         id == GAME_CTRL_ID_SAVE ||
16384         id == GAME_CTRL_ID_LOAD ||
16385         id == GAME_CTRL_ID_RESTART ||
16386         id == GAME_CTRL_ID_PANEL_RESTART ||
16387         id == GAME_CTRL_ID_TOUCH_RESTART)
16388     {
16389       button_type = GD_TYPE_NORMAL_BUTTON;
16390       checked = FALSE;
16391       event_mask = GD_EVENT_RELEASED;
16392     }
16393     else if (id == GAME_CTRL_ID_UNDO ||
16394              id == GAME_CTRL_ID_REDO)
16395     {
16396       button_type = GD_TYPE_NORMAL_BUTTON;
16397       checked = FALSE;
16398       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16399     }
16400     else
16401     {
16402       button_type = GD_TYPE_CHECK_BUTTON;
16403       checked = (gamebutton_info[i].setup_value != NULL ?
16404                  *gamebutton_info[i].setup_value : FALSE);
16405       event_mask = GD_EVENT_PRESSED;
16406     }
16407
16408     gi = CreateGadget(GDI_CUSTOM_ID, id,
16409                       GDI_IMAGE_ID, graphic,
16410                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16411                       GDI_X, base_x + x,
16412                       GDI_Y, base_y + y,
16413                       GDI_WIDTH, gfx->width,
16414                       GDI_HEIGHT, gfx->height,
16415                       GDI_TYPE, button_type,
16416                       GDI_STATE, GD_BUTTON_UNPRESSED,
16417                       GDI_CHECKED, checked,
16418                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16419                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16420                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16421                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16422                       GDI_DIRECT_DRAW, FALSE,
16423                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16424                       GDI_EVENT_MASK, event_mask,
16425                       GDI_CALLBACK_ACTION, HandleGameButtons,
16426                       GDI_END);
16427
16428     if (gi == NULL)
16429       Fail("cannot create gadget");
16430
16431     game_gadget[id] = gi;
16432   }
16433 }
16434
16435 void FreeGameButtons(void)
16436 {
16437   int i;
16438
16439   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16440     FreeGadget(game_gadget[i]);
16441 }
16442
16443 static void UnmapGameButtonsAtSamePosition(int id)
16444 {
16445   int i;
16446
16447   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16448     if (i != id &&
16449         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16450         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16451       UnmapGadget(game_gadget[i]);
16452 }
16453
16454 static void UnmapGameButtonsAtSamePosition_All(void)
16455 {
16456   if (setup.show_load_save_buttons)
16457   {
16458     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16459     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16460     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16461   }
16462   else if (setup.show_undo_redo_buttons)
16463   {
16464     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16465     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16466     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16467   }
16468   else
16469   {
16470     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16471     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16472     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16473
16474     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16475     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16476     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16477   }
16478 }
16479
16480 void MapLoadSaveButtons(void)
16481 {
16482   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16483   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16484
16485   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16486   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16487 }
16488
16489 void MapUndoRedoButtons(void)
16490 {
16491   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16492   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16493
16494   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16495   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16496 }
16497
16498 void ModifyPauseButtons(void)
16499 {
16500   static int ids[] =
16501   {
16502     GAME_CTRL_ID_PAUSE,
16503     GAME_CTRL_ID_PAUSE2,
16504     GAME_CTRL_ID_PANEL_PAUSE,
16505     GAME_CTRL_ID_TOUCH_PAUSE,
16506     -1
16507   };
16508   int i;
16509
16510   // do not redraw pause button on closed door (may happen when restarting game)
16511   if (!(GetDoorState() & DOOR_OPEN_1))
16512     return;
16513
16514   for (i = 0; ids[i] > -1; i++)
16515     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16516 }
16517
16518 static void MapGameButtonsExt(boolean on_tape)
16519 {
16520   int i;
16521
16522   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16523   {
16524     if ((i == GAME_CTRL_ID_UNDO ||
16525          i == GAME_CTRL_ID_REDO) &&
16526         game_status != GAME_MODE_PLAYING)
16527       continue;
16528
16529     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16530       MapGadget(game_gadget[i]);
16531   }
16532
16533   UnmapGameButtonsAtSamePosition_All();
16534
16535   RedrawGameButtons();
16536 }
16537
16538 static void UnmapGameButtonsExt(boolean on_tape)
16539 {
16540   int i;
16541
16542   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16543     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16544       UnmapGadget(game_gadget[i]);
16545 }
16546
16547 static void RedrawGameButtonsExt(boolean on_tape)
16548 {
16549   int i;
16550
16551   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16552     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16553       RedrawGadget(game_gadget[i]);
16554 }
16555
16556 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16557 {
16558   if (gi == NULL)
16559     return;
16560
16561   gi->checked = state;
16562 }
16563
16564 static void RedrawSoundButtonGadget(int id)
16565 {
16566   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16567              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16568              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16569              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16570              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16571              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16572              id);
16573
16574   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16575   RedrawGadget(game_gadget[id2]);
16576 }
16577
16578 void MapGameButtons(void)
16579 {
16580   MapGameButtonsExt(FALSE);
16581 }
16582
16583 void UnmapGameButtons(void)
16584 {
16585   UnmapGameButtonsExt(FALSE);
16586 }
16587
16588 void RedrawGameButtons(void)
16589 {
16590   RedrawGameButtonsExt(FALSE);
16591 }
16592
16593 void MapGameButtonsOnTape(void)
16594 {
16595   MapGameButtonsExt(TRUE);
16596 }
16597
16598 void UnmapGameButtonsOnTape(void)
16599 {
16600   UnmapGameButtonsExt(TRUE);
16601 }
16602
16603 void RedrawGameButtonsOnTape(void)
16604 {
16605   RedrawGameButtonsExt(TRUE);
16606 }
16607
16608 static void GameUndoRedoExt(void)
16609 {
16610   ClearPlayerAction();
16611
16612   tape.pausing = TRUE;
16613
16614   RedrawPlayfield();
16615   UpdateAndDisplayGameControlValues();
16616
16617   DrawCompleteVideoDisplay();
16618   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16619   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16620   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16621
16622   ModifyPauseButtons();
16623
16624   BackToFront();
16625 }
16626
16627 static void GameUndo(int steps)
16628 {
16629   if (!CheckEngineSnapshotList())
16630     return;
16631
16632   int tape_property_bits = tape.property_bits;
16633
16634   LoadEngineSnapshot_Undo(steps);
16635
16636   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16637
16638   GameUndoRedoExt();
16639 }
16640
16641 static void GameRedo(int steps)
16642 {
16643   if (!CheckEngineSnapshotList())
16644     return;
16645
16646   int tape_property_bits = tape.property_bits;
16647
16648   LoadEngineSnapshot_Redo(steps);
16649
16650   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16651
16652   GameUndoRedoExt();
16653 }
16654
16655 static void HandleGameButtonsExt(int id, int button)
16656 {
16657   static boolean game_undo_executed = FALSE;
16658   int steps = BUTTON_STEPSIZE(button);
16659   boolean handle_game_buttons =
16660     (game_status == GAME_MODE_PLAYING ||
16661      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16662
16663   if (!handle_game_buttons)
16664     return;
16665
16666   switch (id)
16667   {
16668     case GAME_CTRL_ID_STOP:
16669     case GAME_CTRL_ID_PANEL_STOP:
16670     case GAME_CTRL_ID_TOUCH_STOP:
16671       TapeStopGame();
16672
16673       break;
16674
16675     case GAME_CTRL_ID_PAUSE:
16676     case GAME_CTRL_ID_PAUSE2:
16677     case GAME_CTRL_ID_PANEL_PAUSE:
16678     case GAME_CTRL_ID_TOUCH_PAUSE:
16679       if (network.enabled && game_status == GAME_MODE_PLAYING)
16680       {
16681         if (tape.pausing)
16682           SendToServer_ContinuePlaying();
16683         else
16684           SendToServer_PausePlaying();
16685       }
16686       else
16687         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16688
16689       game_undo_executed = FALSE;
16690
16691       break;
16692
16693     case GAME_CTRL_ID_PLAY:
16694     case GAME_CTRL_ID_PANEL_PLAY:
16695       if (game_status == GAME_MODE_MAIN)
16696       {
16697         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16698       }
16699       else if (tape.pausing)
16700       {
16701         if (network.enabled)
16702           SendToServer_ContinuePlaying();
16703         else
16704           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16705       }
16706       break;
16707
16708     case GAME_CTRL_ID_UNDO:
16709       // Important: When using "save snapshot when collecting an item" mode,
16710       // load last (current) snapshot for first "undo" after pressing "pause"
16711       // (else the last-but-one snapshot would be loaded, because the snapshot
16712       // pointer already points to the last snapshot when pressing "pause",
16713       // which is fine for "every step/move" mode, but not for "every collect")
16714       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16715           !game_undo_executed)
16716         steps--;
16717
16718       game_undo_executed = TRUE;
16719
16720       GameUndo(steps);
16721       break;
16722
16723     case GAME_CTRL_ID_REDO:
16724       GameRedo(steps);
16725       break;
16726
16727     case GAME_CTRL_ID_SAVE:
16728       TapeQuickSave();
16729       break;
16730
16731     case GAME_CTRL_ID_LOAD:
16732       TapeQuickLoad();
16733       break;
16734
16735     case GAME_CTRL_ID_RESTART:
16736     case GAME_CTRL_ID_PANEL_RESTART:
16737     case GAME_CTRL_ID_TOUCH_RESTART:
16738       TapeRestartGame();
16739
16740       break;
16741
16742     case SOUND_CTRL_ID_MUSIC:
16743     case SOUND_CTRL_ID_PANEL_MUSIC:
16744       if (setup.sound_music)
16745       { 
16746         setup.sound_music = FALSE;
16747
16748         FadeMusic();
16749       }
16750       else if (audio.music_available)
16751       { 
16752         setup.sound = setup.sound_music = TRUE;
16753
16754         SetAudioMode(setup.sound);
16755
16756         if (game_status == GAME_MODE_PLAYING)
16757           PlayLevelMusic();
16758       }
16759
16760       RedrawSoundButtonGadget(id);
16761
16762       break;
16763
16764     case SOUND_CTRL_ID_LOOPS:
16765     case SOUND_CTRL_ID_PANEL_LOOPS:
16766       if (setup.sound_loops)
16767         setup.sound_loops = FALSE;
16768       else if (audio.loops_available)
16769       {
16770         setup.sound = setup.sound_loops = TRUE;
16771
16772         SetAudioMode(setup.sound);
16773       }
16774
16775       RedrawSoundButtonGadget(id);
16776
16777       break;
16778
16779     case SOUND_CTRL_ID_SIMPLE:
16780     case SOUND_CTRL_ID_PANEL_SIMPLE:
16781       if (setup.sound_simple)
16782         setup.sound_simple = FALSE;
16783       else if (audio.sound_available)
16784       {
16785         setup.sound = setup.sound_simple = TRUE;
16786
16787         SetAudioMode(setup.sound);
16788       }
16789
16790       RedrawSoundButtonGadget(id);
16791
16792       break;
16793
16794     default:
16795       break;
16796   }
16797 }
16798
16799 static void HandleGameButtons(struct GadgetInfo *gi)
16800 {
16801   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16802 }
16803
16804 void HandleSoundButtonKeys(Key key)
16805 {
16806   if (key == setup.shortcut.sound_simple)
16807     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16808   else if (key == setup.shortcut.sound_loops)
16809     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16810   else if (key == setup.shortcut.sound_music)
16811     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16812 }