fixed bug with limiting forced scroll delay value to lower default value
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   // ---------- initialize trigger events -------------------------------------
3286
3287   // initialize trigger events information
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   // add trigger events from element change event properties
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       struct ElementChangeInfo *change = &ei->change_page[j];
3300
3301       if (!change->can_change_or_has_action)
3302         continue;
3303
3304       if (change->has_event[CE_BY_OTHER_ACTION])
3305       {
3306         int trigger_element = change->trigger_element;
3307
3308         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3309         {
3310           if (change->has_event[k])
3311           {
3312             if (IS_GROUP_ELEMENT(trigger_element))
3313             {
3314               struct ElementGroupInfo *group =
3315                 element_info[trigger_element].group;
3316
3317               for (l = 0; l < group->num_elements_resolved; l++)
3318                 trigger_events[group->element_resolved[l]][k] = TRUE;
3319             }
3320             else if (trigger_element == EL_ANY_ELEMENT)
3321               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3322                 trigger_events[l][k] = TRUE;
3323             else
3324               trigger_events[trigger_element][k] = TRUE;
3325           }
3326         }
3327       }
3328     }
3329   }
3330
3331   // ---------- initialize push delay -----------------------------------------
3332
3333   // initialize push delay values to default
3334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3335   {
3336     if (!IS_CUSTOM_ELEMENT(i))
3337     {
3338       // set default push delay values (corrected since version 3.0.7-1)
3339       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3340       {
3341         element_info[i].push_delay_fixed = 2;
3342         element_info[i].push_delay_random = 8;
3343       }
3344       else
3345       {
3346         element_info[i].push_delay_fixed = 8;
3347         element_info[i].push_delay_random = 8;
3348       }
3349     }
3350   }
3351
3352   // set push delay value for certain elements from pre-defined list
3353   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3354   {
3355     int e = push_delay_list[i].element;
3356
3357     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3358     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359   }
3360
3361   // set push delay value for Supaplex elements for newer engine versions
3362   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3363   {
3364     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     {
3366       if (IS_SP_ELEMENT(i))
3367       {
3368         // set SP push delay to just enough to push under a falling zonk
3369         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3370
3371         element_info[i].push_delay_fixed  = delay;
3372         element_info[i].push_delay_random = 0;
3373       }
3374     }
3375   }
3376
3377   // ---------- initialize move stepsize --------------------------------------
3378
3379   // initialize move stepsize values to default
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381     if (!IS_CUSTOM_ELEMENT(i))
3382       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3383
3384   // set move stepsize value for certain elements from pre-defined list
3385   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = move_stepsize_list[i].element;
3388
3389     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390
3391     // set move stepsize value for certain elements for older engine versions
3392     if (use_old_move_stepsize_for_magic_wall)
3393     {
3394       if (e == EL_MAGIC_WALL_FILLING ||
3395           e == EL_MAGIC_WALL_EMPTYING ||
3396           e == EL_BD_MAGIC_WALL_FILLING ||
3397           e == EL_BD_MAGIC_WALL_EMPTYING)
3398         element_info[e].move_stepsize *= 2;
3399     }
3400   }
3401
3402   // ---------- initialize collect score --------------------------------------
3403
3404   // initialize collect score values for custom elements from initial value
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (IS_CUSTOM_ELEMENT(i))
3407       element_info[i].collect_score = element_info[i].collect_score_initial;
3408
3409   // ---------- initialize collect count --------------------------------------
3410
3411   // initialize collect count values for non-custom elements
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].collect_count_initial = 0;
3415
3416   // add collect count values for all elements from pre-defined list
3417   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3418     element_info[collect_count_list[i].element].collect_count_initial =
3419       collect_count_list[i].count;
3420
3421   // ---------- initialize access direction -----------------------------------
3422
3423   // initialize access direction values to default (access from every side)
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3427
3428   // set access direction value for certain elements from pre-defined list
3429   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3430     element_info[access_direction_list[i].element].access_direction =
3431       access_direction_list[i].direction;
3432
3433   // ---------- initialize explosion content ----------------------------------
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435   {
3436     if (IS_CUSTOM_ELEMENT(i))
3437       continue;
3438
3439     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3440     {
3441       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3442
3443       element_info[i].content.e[x][y] =
3444         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3445          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3446          i == EL_PLAYER_3 ? EL_EMERALD :
3447          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3448          i == EL_MOLE ? EL_EMERALD_RED :
3449          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3450          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3451          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3452          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3453          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3454          i == EL_WALL_EMERALD ? EL_EMERALD :
3455          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3456          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3457          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3458          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3459          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3460          i == EL_WALL_PEARL ? EL_PEARL :
3461          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462          EL_EMPTY);
3463     }
3464   }
3465
3466   // ---------- initialize recursion detection --------------------------------
3467   recursion_loop_depth = 0;
3468   recursion_loop_detected = FALSE;
3469   recursion_loop_element = EL_UNDEFINED;
3470
3471   // ---------- initialize graphics engine ------------------------------------
3472   game.scroll_delay_value =
3473     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3474      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3475      !setup.forced_scroll_delay           ? 0 :
3476      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3477   if (game.forced_scroll_delay_value == -1)
3478     game.scroll_delay_value =
3479       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3480
3481   // ---------- initialize game engine snapshots ------------------------------
3482   for (i = 0; i < MAX_PLAYERS; i++)
3483     game.snapshot.last_action[i] = 0;
3484   game.snapshot.changed_action = FALSE;
3485   game.snapshot.collected_item = FALSE;
3486   game.snapshot.mode =
3487     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3488      SNAPSHOT_MODE_EVERY_STEP :
3489      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3490      SNAPSHOT_MODE_EVERY_MOVE :
3491      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3492      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3493   game.snapshot.save_snapshot = FALSE;
3494
3495   // ---------- initialize level time for Supaplex engine ---------------------
3496   // Supaplex levels with time limit currently unsupported -- should be added
3497   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3498     level.time = 0;
3499
3500   // ---------- initialize flags for handling game actions --------------------
3501
3502   // set flags for game actions to default values
3503   game.use_key_actions = TRUE;
3504   game.use_mouse_actions = FALSE;
3505
3506   // when using Mirror Magic game engine, handle mouse events only
3507   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3508   {
3509     game.use_key_actions = FALSE;
3510     game.use_mouse_actions = TRUE;
3511   }
3512
3513   // check for custom elements with mouse click events
3514   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3515   {
3516     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3517     {
3518       int element = EL_CUSTOM_START + i;
3519
3520       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3521           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3522           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3523           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3524         game.use_mouse_actions = TRUE;
3525     }
3526   }
3527 }
3528
3529 static int get_num_special_action(int element, int action_first,
3530                                   int action_last)
3531 {
3532   int num_special_action = 0;
3533   int i, j;
3534
3535   for (i = action_first; i <= action_last; i++)
3536   {
3537     boolean found = FALSE;
3538
3539     for (j = 0; j < NUM_DIRECTIONS; j++)
3540       if (el_act_dir2img(element, i, j) !=
3541           el_act_dir2img(element, ACTION_DEFAULT, j))
3542         found = TRUE;
3543
3544     if (found)
3545       num_special_action++;
3546     else
3547       break;
3548   }
3549
3550   return num_special_action;
3551 }
3552
3553
3554 // ============================================================================
3555 // InitGame()
3556 // ----------------------------------------------------------------------------
3557 // initialize and start new game
3558 // ============================================================================
3559
3560 #if DEBUG_INIT_PLAYER
3561 static void DebugPrintPlayerStatus(char *message)
3562 {
3563   int i;
3564
3565   if (!options.debug)
3566     return;
3567
3568   Debug("game:init:player", "%s:", message);
3569
3570   for (i = 0; i < MAX_PLAYERS; i++)
3571   {
3572     struct PlayerInfo *player = &stored_player[i];
3573
3574     Debug("game:init:player",
3575           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3576           i + 1,
3577           player->present,
3578           player->connected,
3579           player->connected_locally,
3580           player->connected_network,
3581           player->active,
3582           (local_player == player ? " (local player)" : ""));
3583   }
3584 }
3585 #endif
3586
3587 void InitGame(void)
3588 {
3589   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3590   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3591   int fade_mask = REDRAW_FIELD;
3592   boolean restarting = (game_status == GAME_MODE_PLAYING);
3593   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3594   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3595   int initial_move_dir = MV_DOWN;
3596   int i, j, x, y;
3597
3598   // required here to update video display before fading (FIX THIS)
3599   DrawMaskedBorder(REDRAW_DOOR_2);
3600
3601   if (!game.restart_level)
3602     CloseDoor(DOOR_CLOSE_1);
3603
3604   if (restarting)
3605   {
3606     // force fading out global animations displayed during game play
3607     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3608   }
3609   else
3610   {
3611     SetGameStatus(GAME_MODE_PLAYING);
3612   }
3613
3614   if (level_editor_test_game)
3615     FadeSkipNextFadeOut();
3616   else
3617     FadeSetEnterScreen();
3618
3619   if (CheckFadeAll())
3620     fade_mask = REDRAW_ALL;
3621
3622   FadeLevelSoundsAndMusic();
3623
3624   ExpireSoundLoops(TRUE);
3625
3626   FadeOut(fade_mask);
3627
3628   if (restarting)
3629   {
3630     // force restarting global animations displayed during game play
3631     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3632
3633     // this is required for "transforming" fade modes like cross-fading
3634     // (else global animations will be stopped, but not restarted here)
3635     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3636
3637     SetGameStatus(GAME_MODE_PLAYING);
3638   }
3639
3640   if (level_editor_test_game)
3641     FadeSkipNextFadeIn();
3642
3643   // needed if different viewport properties defined for playing
3644   ChangeViewportPropertiesIfNeeded();
3645
3646   ClearField();
3647
3648   DrawCompleteVideoDisplay();
3649
3650   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3651
3652   InitGameEngine();
3653   InitGameControlValues();
3654
3655   if (tape.recording)
3656   {
3657     // initialize tape actions from game when recording tape
3658     tape.use_key_actions   = game.use_key_actions;
3659     tape.use_mouse_actions = game.use_mouse_actions;
3660
3661     // initialize visible playfield size when recording tape (for team mode)
3662     tape.scr_fieldx = SCR_FIELDX;
3663     tape.scr_fieldy = SCR_FIELDY;
3664   }
3665
3666   // don't play tapes over network
3667   network_playing = (network.enabled && !tape.playing);
3668
3669   for (i = 0; i < MAX_PLAYERS; i++)
3670   {
3671     struct PlayerInfo *player = &stored_player[i];
3672
3673     player->index_nr = i;
3674     player->index_bit = (1 << i);
3675     player->element_nr = EL_PLAYER_1 + i;
3676
3677     player->present = FALSE;
3678     player->active = FALSE;
3679     player->mapped = FALSE;
3680
3681     player->killed = FALSE;
3682     player->reanimated = FALSE;
3683     player->buried = FALSE;
3684
3685     player->action = 0;
3686     player->effective_action = 0;
3687     player->programmed_action = 0;
3688     player->snap_action = 0;
3689
3690     player->mouse_action.lx = 0;
3691     player->mouse_action.ly = 0;
3692     player->mouse_action.button = 0;
3693     player->mouse_action.button_hint = 0;
3694
3695     player->effective_mouse_action.lx = 0;
3696     player->effective_mouse_action.ly = 0;
3697     player->effective_mouse_action.button = 0;
3698     player->effective_mouse_action.button_hint = 0;
3699
3700     for (j = 0; j < MAX_NUM_KEYS; j++)
3701       player->key[j] = FALSE;
3702
3703     player->num_white_keys = 0;
3704
3705     player->dynabomb_count = 0;
3706     player->dynabomb_size = 1;
3707     player->dynabombs_left = 0;
3708     player->dynabomb_xl = FALSE;
3709
3710     player->MovDir = initial_move_dir;
3711     player->MovPos = 0;
3712     player->GfxPos = 0;
3713     player->GfxDir = initial_move_dir;
3714     player->GfxAction = ACTION_DEFAULT;
3715     player->Frame = 0;
3716     player->StepFrame = 0;
3717
3718     player->initial_element = player->element_nr;
3719     player->artwork_element =
3720       (level.use_artwork_element[i] ? level.artwork_element[i] :
3721        player->element_nr);
3722     player->use_murphy = FALSE;
3723
3724     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3725     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3726
3727     player->gravity = level.initial_player_gravity[i];
3728
3729     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3730
3731     player->actual_frame_counter.count = 0;
3732     player->actual_frame_counter.value = 1;
3733
3734     player->step_counter = 0;
3735
3736     player->last_move_dir = initial_move_dir;
3737
3738     player->is_active = FALSE;
3739
3740     player->is_waiting = FALSE;
3741     player->is_moving = FALSE;
3742     player->is_auto_moving = FALSE;
3743     player->is_digging = FALSE;
3744     player->is_snapping = FALSE;
3745     player->is_collecting = FALSE;
3746     player->is_pushing = FALSE;
3747     player->is_switching = FALSE;
3748     player->is_dropping = FALSE;
3749     player->is_dropping_pressed = FALSE;
3750
3751     player->is_bored = FALSE;
3752     player->is_sleeping = FALSE;
3753
3754     player->was_waiting = TRUE;
3755     player->was_moving = FALSE;
3756     player->was_snapping = FALSE;
3757     player->was_dropping = FALSE;
3758
3759     player->force_dropping = FALSE;
3760
3761     player->frame_counter_bored = -1;
3762     player->frame_counter_sleeping = -1;
3763
3764     player->anim_delay_counter = 0;
3765     player->post_delay_counter = 0;
3766
3767     player->dir_waiting = initial_move_dir;
3768     player->action_waiting = ACTION_DEFAULT;
3769     player->last_action_waiting = ACTION_DEFAULT;
3770     player->special_action_bored = ACTION_DEFAULT;
3771     player->special_action_sleeping = ACTION_DEFAULT;
3772
3773     player->switch_x = -1;
3774     player->switch_y = -1;
3775
3776     player->drop_x = -1;
3777     player->drop_y = -1;
3778
3779     player->show_envelope = 0;
3780
3781     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3782
3783     player->push_delay       = -1;      // initialized when pushing starts
3784     player->push_delay_value = game.initial_push_delay_value;
3785
3786     player->drop_delay = 0;
3787     player->drop_pressed_delay = 0;
3788
3789     player->last_jx = -1;
3790     player->last_jy = -1;
3791     player->jx = -1;
3792     player->jy = -1;
3793
3794     player->shield_normal_time_left = 0;
3795     player->shield_deadly_time_left = 0;
3796
3797     player->last_removed_element = EL_UNDEFINED;
3798
3799     player->inventory_infinite_element = EL_UNDEFINED;
3800     player->inventory_size = 0;
3801
3802     if (level.use_initial_inventory[i])
3803     {
3804       for (j = 0; j < level.initial_inventory_size[i]; j++)
3805       {
3806         int element = level.initial_inventory_content[i][j];
3807         int collect_count = element_info[element].collect_count_initial;
3808         int k;
3809
3810         if (!IS_CUSTOM_ELEMENT(element))
3811           collect_count = 1;
3812
3813         if (collect_count == 0)
3814           player->inventory_infinite_element = element;
3815         else
3816           for (k = 0; k < collect_count; k++)
3817             if (player->inventory_size < MAX_INVENTORY_SIZE)
3818               player->inventory_element[player->inventory_size++] = element;
3819       }
3820     }
3821
3822     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3823     SnapField(player, 0, 0);
3824
3825     map_player_action[i] = i;
3826   }
3827
3828   network_player_action_received = FALSE;
3829
3830   // initial null action
3831   if (network_playing)
3832     SendToServer_MovePlayer(MV_NONE);
3833
3834   FrameCounter = 0;
3835   TimeFrames = 0;
3836   TimePlayed = 0;
3837   TimeLeft = level.time;
3838   TapeTime = 0;
3839
3840   ScreenMovDir = MV_NONE;
3841   ScreenMovPos = 0;
3842   ScreenGfxPos = 0;
3843
3844   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3845
3846   game.robot_wheel_x = -1;
3847   game.robot_wheel_y = -1;
3848
3849   game.exit_x = -1;
3850   game.exit_y = -1;
3851
3852   game.all_players_gone = FALSE;
3853
3854   game.LevelSolved = FALSE;
3855   game.GameOver = FALSE;
3856
3857   game.GamePlayed = !tape.playing;
3858
3859   game.LevelSolved_GameWon = FALSE;
3860   game.LevelSolved_GameEnd = FALSE;
3861   game.LevelSolved_SaveTape = FALSE;
3862   game.LevelSolved_SaveScore = FALSE;
3863
3864   game.LevelSolved_CountingTime = 0;
3865   game.LevelSolved_CountingScore = 0;
3866   game.LevelSolved_CountingHealth = 0;
3867
3868   game.panel.active = TRUE;
3869
3870   game.no_level_time_limit = (level.time == 0);
3871   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3872
3873   game.yamyam_content_nr = 0;
3874   game.robot_wheel_active = FALSE;
3875   game.magic_wall_active = FALSE;
3876   game.magic_wall_time_left = 0;
3877   game.light_time_left = 0;
3878   game.timegate_time_left = 0;
3879   game.switchgate_pos = 0;
3880   game.wind_direction = level.wind_direction_initial;
3881
3882   game.time_final = 0;
3883   game.score_time_final = 0;
3884
3885   game.score = 0;
3886   game.score_final = 0;
3887
3888   game.health = MAX_HEALTH;
3889   game.health_final = MAX_HEALTH;
3890
3891   game.gems_still_needed = level.gems_needed;
3892   game.sokoban_fields_still_needed = 0;
3893   game.sokoban_objects_still_needed = 0;
3894   game.lights_still_needed = 0;
3895   game.players_still_needed = 0;
3896   game.friends_still_needed = 0;
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_active = level.ball_active_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.explosions_delayed = TRUE;
3905
3906   game.envelope_active = FALSE;
3907
3908   // special case: set custom artwork setting to initial value
3909   game.use_masked_elements = game.use_masked_elements_initial;
3910
3911   for (i = 0; i < NUM_BELTS; i++)
3912   {
3913     game.belt_dir[i] = MV_NONE;
3914     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3915   }
3916
3917   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3918     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3919
3920 #if DEBUG_INIT_PLAYER
3921   DebugPrintPlayerStatus("Player status at level initialization");
3922 #endif
3923
3924   SCAN_PLAYFIELD(x, y)
3925   {
3926     Tile[x][y] = Last[x][y] = level.field[x][y];
3927     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3928     ChangeDelay[x][y] = 0;
3929     ChangePage[x][y] = -1;
3930     CustomValue[x][y] = 0;              // initialized in InitField()
3931     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3932     AmoebaNr[x][y] = 0;
3933     WasJustMoving[x][y] = 0;
3934     WasJustFalling[x][y] = 0;
3935     CheckCollision[x][y] = 0;
3936     CheckImpact[x][y] = 0;
3937     Stop[x][y] = FALSE;
3938     Pushed[x][y] = FALSE;
3939
3940     ChangeCount[x][y] = 0;
3941     ChangeEvent[x][y] = -1;
3942
3943     ExplodePhase[x][y] = 0;
3944     ExplodeDelay[x][y] = 0;
3945     ExplodeField[x][y] = EX_TYPE_NONE;
3946
3947     RunnerVisit[x][y] = 0;
3948     PlayerVisit[x][y] = 0;
3949
3950     GfxFrame[x][y] = 0;
3951     GfxRandom[x][y] = INIT_GFX_RANDOM();
3952     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3953     GfxElement[x][y] = EL_UNDEFINED;
3954     GfxElementEmpty[x][y] = EL_EMPTY;
3955     GfxAction[x][y] = ACTION_DEFAULT;
3956     GfxDir[x][y] = MV_NONE;
3957     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3958   }
3959
3960   SCAN_PLAYFIELD(x, y)
3961   {
3962     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3963       emulate_bd = FALSE;
3964     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3965       emulate_sp = FALSE;
3966
3967     InitField(x, y, TRUE);
3968
3969     ResetGfxAnimation(x, y);
3970   }
3971
3972   InitBeltMovement();
3973
3974   for (i = 0; i < MAX_PLAYERS; i++)
3975   {
3976     struct PlayerInfo *player = &stored_player[i];
3977
3978     // set number of special actions for bored and sleeping animation
3979     player->num_special_action_bored =
3980       get_num_special_action(player->artwork_element,
3981                              ACTION_BORING_1, ACTION_BORING_LAST);
3982     player->num_special_action_sleeping =
3983       get_num_special_action(player->artwork_element,
3984                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3985   }
3986
3987   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3988                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3989
3990   // initialize type of slippery elements
3991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3992   {
3993     if (!IS_CUSTOM_ELEMENT(i))
3994     {
3995       // default: elements slip down either to the left or right randomly
3996       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3997
3998       // SP style elements prefer to slip down on the left side
3999       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4000         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4001
4002       // BD style elements prefer to slip down on the left side
4003       if (game.emulation == EMU_BOULDERDASH)
4004         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4005     }
4006   }
4007
4008   // initialize explosion and ignition delay
4009   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4010   {
4011     if (!IS_CUSTOM_ELEMENT(i))
4012     {
4013       int num_phase = 8;
4014       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4015                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4016                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4017       int last_phase = (num_phase + 1) * delay;
4018       int half_phase = (num_phase / 2) * delay;
4019
4020       element_info[i].explosion_delay = last_phase - 1;
4021       element_info[i].ignition_delay = half_phase;
4022
4023       if (i == EL_BLACK_ORB)
4024         element_info[i].ignition_delay = 1;
4025     }
4026   }
4027
4028   // correct non-moving belts to start moving left
4029   for (i = 0; i < NUM_BELTS; i++)
4030     if (game.belt_dir[i] == MV_NONE)
4031       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4032
4033 #if USE_NEW_PLAYER_ASSIGNMENTS
4034   // use preferred player also in local single-player mode
4035   if (!network.enabled && !game.team_mode)
4036   {
4037     int new_index_nr = setup.network_player_nr;
4038
4039     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4040     {
4041       for (i = 0; i < MAX_PLAYERS; i++)
4042         stored_player[i].connected_locally = FALSE;
4043
4044       stored_player[new_index_nr].connected_locally = TRUE;
4045     }
4046   }
4047
4048   for (i = 0; i < MAX_PLAYERS; i++)
4049   {
4050     stored_player[i].connected = FALSE;
4051
4052     // in network game mode, the local player might not be the first player
4053     if (stored_player[i].connected_locally)
4054       local_player = &stored_player[i];
4055   }
4056
4057   if (!network.enabled)
4058     local_player->connected = TRUE;
4059
4060   if (tape.playing)
4061   {
4062     for (i = 0; i < MAX_PLAYERS; i++)
4063       stored_player[i].connected = tape.player_participates[i];
4064   }
4065   else if (network.enabled)
4066   {
4067     // add team mode players connected over the network (needed for correct
4068     // assignment of player figures from level to locally playing players)
4069
4070     for (i = 0; i < MAX_PLAYERS; i++)
4071       if (stored_player[i].connected_network)
4072         stored_player[i].connected = TRUE;
4073   }
4074   else if (game.team_mode)
4075   {
4076     // try to guess locally connected team mode players (needed for correct
4077     // assignment of player figures from level to locally playing players)
4078
4079     for (i = 0; i < MAX_PLAYERS; i++)
4080       if (setup.input[i].use_joystick ||
4081           setup.input[i].key.left != KSYM_UNDEFINED)
4082         stored_player[i].connected = TRUE;
4083   }
4084
4085 #if DEBUG_INIT_PLAYER
4086   DebugPrintPlayerStatus("Player status after level initialization");
4087 #endif
4088
4089 #if DEBUG_INIT_PLAYER
4090   Debug("game:init:player", "Reassigning players ...");
4091 #endif
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       struct PlayerInfo *field_player = NULL;
4101
4102 #if DEBUG_INIT_PLAYER
4103       Debug("game:init:player",
4104             "- looking for field player for player %d ...", i + 1);
4105 #endif
4106
4107       // assign first free player found that is present in the playfield
4108
4109       // first try: look for unmapped playfield player that is not connected
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111         if (field_player == NULL &&
4112             stored_player[j].present &&
4113             !stored_player[j].mapped &&
4114             !stored_player[j].connected)
4115           field_player = &stored_player[j];
4116
4117       // second try: look for *any* unmapped playfield player
4118       for (j = 0; j < MAX_PLAYERS; j++)
4119         if (field_player == NULL &&
4120             stored_player[j].present &&
4121             !stored_player[j].mapped)
4122           field_player = &stored_player[j];
4123
4124       if (field_player != NULL)
4125       {
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128 #if DEBUG_INIT_PLAYER
4129         Debug("game:init:player", "- found player %d",
4130               field_player->index_nr + 1);
4131 #endif
4132
4133         player->present = FALSE;
4134         player->active = FALSE;
4135
4136         field_player->present = TRUE;
4137         field_player->active = TRUE;
4138
4139         /*
4140         player->initial_element = field_player->initial_element;
4141         player->artwork_element = field_player->artwork_element;
4142
4143         player->block_last_field       = field_player->block_last_field;
4144         player->block_delay_adjustment = field_player->block_delay_adjustment;
4145         */
4146
4147         StorePlayer[jx][jy] = field_player->element_nr;
4148
4149         field_player->jx = field_player->last_jx = jx;
4150         field_player->jy = field_player->last_jy = jy;
4151
4152         if (local_player == player)
4153           local_player = field_player;
4154
4155         map_player_action[field_player->index_nr] = i;
4156
4157         field_player->mapped = TRUE;
4158
4159 #if DEBUG_INIT_PLAYER
4160         Debug("game:init:player", "- map_player_action[%d] == %d",
4161               field_player->index_nr + 1, i + 1);
4162 #endif
4163       }
4164     }
4165
4166     if (player->connected && player->present)
4167       player->mapped = TRUE;
4168   }
4169
4170 #if DEBUG_INIT_PLAYER
4171   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4172 #endif
4173
4174 #else
4175
4176   // check if any connected player was not found in playfield
4177   for (i = 0; i < MAX_PLAYERS; i++)
4178   {
4179     struct PlayerInfo *player = &stored_player[i];
4180
4181     if (player->connected && !player->present)
4182     {
4183       for (j = 0; j < MAX_PLAYERS; j++)
4184       {
4185         struct PlayerInfo *field_player = &stored_player[j];
4186         int jx = field_player->jx, jy = field_player->jy;
4187
4188         // assign first free player found that is present in the playfield
4189         if (field_player->present && !field_player->connected)
4190         {
4191           player->present = TRUE;
4192           player->active = TRUE;
4193
4194           field_player->present = FALSE;
4195           field_player->active = FALSE;
4196
4197           player->initial_element = field_player->initial_element;
4198           player->artwork_element = field_player->artwork_element;
4199
4200           player->block_last_field       = field_player->block_last_field;
4201           player->block_delay_adjustment = field_player->block_delay_adjustment;
4202
4203           StorePlayer[jx][jy] = player->element_nr;
4204
4205           player->jx = player->last_jx = jx;
4206           player->jy = player->last_jy = jy;
4207
4208           break;
4209         }
4210       }
4211     }
4212   }
4213 #endif
4214
4215 #if 0
4216   Debug("game:init:player", "local_player->present == %d",
4217         local_player->present);
4218 #endif
4219
4220   // set focus to local player for network games, else to all players
4221   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4222   game.centered_player_nr_next = game.centered_player_nr;
4223   game.set_centered_player = FALSE;
4224   game.set_centered_player_wrap = FALSE;
4225
4226   if (network_playing && tape.recording)
4227   {
4228     // store client dependent player focus when recording network games
4229     tape.centered_player_nr_next = game.centered_player_nr_next;
4230     tape.set_centered_player = TRUE;
4231   }
4232
4233   if (tape.playing)
4234   {
4235     // when playing a tape, eliminate all players who do not participate
4236
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238
4239     if (!game.team_mode)
4240     {
4241       for (i = 0; i < MAX_PLAYERS; i++)
4242       {
4243         if (stored_player[i].active &&
4244             !tape.player_participates[map_player_action[i]])
4245         {
4246           struct PlayerInfo *player = &stored_player[i];
4247           int jx = player->jx, jy = player->jy;
4248
4249 #if DEBUG_INIT_PLAYER
4250           Debug("game:init:player", "Removing player %d at (%d, %d)",
4251                 i + 1, jx, jy);
4252 #endif
4253
4254           player->active = FALSE;
4255           StorePlayer[jx][jy] = 0;
4256           Tile[jx][jy] = EL_EMPTY;
4257         }
4258       }
4259     }
4260
4261 #else
4262
4263     for (i = 0; i < MAX_PLAYERS; i++)
4264     {
4265       if (stored_player[i].active &&
4266           !tape.player_participates[i])
4267       {
4268         struct PlayerInfo *player = &stored_player[i];
4269         int jx = player->jx, jy = player->jy;
4270
4271         player->active = FALSE;
4272         StorePlayer[jx][jy] = 0;
4273         Tile[jx][jy] = EL_EMPTY;
4274       }
4275     }
4276 #endif
4277   }
4278   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4279   {
4280     // when in single player mode, eliminate all but the local player
4281
4282     for (i = 0; i < MAX_PLAYERS; i++)
4283     {
4284       struct PlayerInfo *player = &stored_player[i];
4285
4286       if (player->active && player != local_player)
4287       {
4288         int jx = player->jx, jy = player->jy;
4289
4290         player->active = FALSE;
4291         player->present = FALSE;
4292
4293         StorePlayer[jx][jy] = 0;
4294         Tile[jx][jy] = EL_EMPTY;
4295       }
4296     }
4297   }
4298
4299   for (i = 0; i < MAX_PLAYERS; i++)
4300     if (stored_player[i].active)
4301       game.players_still_needed++;
4302
4303   if (level.solved_by_one_player)
4304     game.players_still_needed = 1;
4305
4306   // when recording the game, store which players take part in the game
4307   if (tape.recording)
4308   {
4309 #if USE_NEW_PLAYER_ASSIGNMENTS
4310     for (i = 0; i < MAX_PLAYERS; i++)
4311       if (stored_player[i].connected)
4312         tape.player_participates[i] = TRUE;
4313 #else
4314     for (i = 0; i < MAX_PLAYERS; i++)
4315       if (stored_player[i].active)
4316         tape.player_participates[i] = TRUE;
4317 #endif
4318   }
4319
4320 #if DEBUG_INIT_PLAYER
4321   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4322 #endif
4323
4324   if (BorderElement == EL_EMPTY)
4325   {
4326     SBX_Left = 0;
4327     SBX_Right = lev_fieldx - SCR_FIELDX;
4328     SBY_Upper = 0;
4329     SBY_Lower = lev_fieldy - SCR_FIELDY;
4330   }
4331   else
4332   {
4333     SBX_Left = -1;
4334     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4335     SBY_Upper = -1;
4336     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4337   }
4338
4339   if (full_lev_fieldx <= SCR_FIELDX)
4340     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4341   if (full_lev_fieldy <= SCR_FIELDY)
4342     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4343
4344   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4345     SBX_Left--;
4346   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4347     SBY_Upper--;
4348
4349   // if local player not found, look for custom element that might create
4350   // the player (make some assumptions about the right custom element)
4351   if (!local_player->present)
4352   {
4353     int start_x = 0, start_y = 0;
4354     int found_rating = 0;
4355     int found_element = EL_UNDEFINED;
4356     int player_nr = local_player->index_nr;
4357
4358     SCAN_PLAYFIELD(x, y)
4359     {
4360       int element = Tile[x][y];
4361       int content;
4362       int xx, yy;
4363       boolean is_player;
4364
4365       if (level.use_start_element[player_nr] &&
4366           level.start_element[player_nr] == element &&
4367           found_rating < 4)
4368       {
4369         start_x = x;
4370         start_y = y;
4371
4372         found_rating = 4;
4373         found_element = element;
4374       }
4375
4376       if (!IS_CUSTOM_ELEMENT(element))
4377         continue;
4378
4379       if (CAN_CHANGE(element))
4380       {
4381         for (i = 0; i < element_info[element].num_change_pages; i++)
4382         {
4383           // check for player created from custom element as single target
4384           content = element_info[element].change_page[i].target_element;
4385           is_player = IS_PLAYER_ELEMENT(content);
4386
4387           if (is_player && (found_rating < 3 ||
4388                             (found_rating == 3 && element < found_element)))
4389           {
4390             start_x = x;
4391             start_y = y;
4392
4393             found_rating = 3;
4394             found_element = element;
4395           }
4396         }
4397       }
4398
4399       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4400       {
4401         // check for player created from custom element as explosion content
4402         content = element_info[element].content.e[xx][yy];
4403         is_player = IS_PLAYER_ELEMENT(content);
4404
4405         if (is_player && (found_rating < 2 ||
4406                           (found_rating == 2 && element < found_element)))
4407         {
4408           start_x = x + xx - 1;
4409           start_y = y + yy - 1;
4410
4411           found_rating = 2;
4412           found_element = element;
4413         }
4414
4415         if (!CAN_CHANGE(element))
4416           continue;
4417
4418         for (i = 0; i < element_info[element].num_change_pages; i++)
4419         {
4420           // check for player created from custom element as extended target
4421           content =
4422             element_info[element].change_page[i].target_content.e[xx][yy];
4423
4424           is_player = IS_PLAYER_ELEMENT(content);
4425
4426           if (is_player && (found_rating < 1 ||
4427                             (found_rating == 1 && element < found_element)))
4428           {
4429             start_x = x + xx - 1;
4430             start_y = y + yy - 1;
4431
4432             found_rating = 1;
4433             found_element = element;
4434           }
4435         }
4436       }
4437     }
4438
4439     scroll_x = SCROLL_POSITION_X(start_x);
4440     scroll_y = SCROLL_POSITION_Y(start_y);
4441   }
4442   else
4443   {
4444     scroll_x = SCROLL_POSITION_X(local_player->jx);
4445     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4446   }
4447
4448   // !!! FIX THIS (START) !!!
4449   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4450   {
4451     InitGameEngine_EM();
4452   }
4453   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4454   {
4455     InitGameEngine_SP();
4456   }
4457   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4458   {
4459     InitGameEngine_MM();
4460   }
4461   else
4462   {
4463     DrawLevel(REDRAW_FIELD);
4464     DrawAllPlayers();
4465
4466     // after drawing the level, correct some elements
4467     if (game.timegate_time_left == 0)
4468       CloseAllOpenTimegates();
4469   }
4470
4471   // blit playfield from scroll buffer to normal back buffer for fading in
4472   BlitScreenToBitmap(backbuffer);
4473   // !!! FIX THIS (END) !!!
4474
4475   DrawMaskedBorder(fade_mask);
4476
4477   FadeIn(fade_mask);
4478
4479 #if 1
4480   // full screen redraw is required at this point in the following cases:
4481   // - special editor door undrawn when game was started from level editor
4482   // - drawing area (playfield) was changed and has to be removed completely
4483   redraw_mask = REDRAW_ALL;
4484   BackToFront();
4485 #endif
4486
4487   if (!game.restart_level)
4488   {
4489     // copy default game door content to main double buffer
4490
4491     // !!! CHECK AGAIN !!!
4492     SetPanelBackground();
4493     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4494     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4495   }
4496
4497   SetPanelBackground();
4498   SetDrawBackgroundMask(REDRAW_DOOR_1);
4499
4500   UpdateAndDisplayGameControlValues();
4501
4502   if (!game.restart_level)
4503   {
4504     UnmapGameButtons();
4505     UnmapTapeButtons();
4506
4507     FreeGameButtons();
4508     CreateGameButtons();
4509
4510     MapGameButtons();
4511     MapTapeButtons();
4512
4513     // copy actual game door content to door double buffer for OpenDoor()
4514     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4515
4516     OpenDoor(DOOR_OPEN_ALL);
4517
4518     KeyboardAutoRepeatOffUnlessAutoplay();
4519
4520 #if DEBUG_INIT_PLAYER
4521     DebugPrintPlayerStatus("Player status (final)");
4522 #endif
4523   }
4524
4525   UnmapAllGadgets();
4526
4527   MapGameButtons();
4528   MapTapeButtons();
4529
4530   if (!game.restart_level && !tape.playing)
4531   {
4532     LevelStats_incPlayed(level_nr);
4533
4534     SaveLevelSetup_SeriesInfo();
4535   }
4536
4537   game.restart_level = FALSE;
4538
4539   game.request_active = FALSE;
4540   game.request_active_or_moving = FALSE;
4541
4542   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4543     InitGameActions_MM();
4544
4545   SaveEngineSnapshotToListInitial();
4546
4547   if (!game.restart_level)
4548   {
4549     PlaySound(SND_GAME_STARTING);
4550
4551     if (setup.sound_music)
4552       PlayLevelMusic();
4553   }
4554
4555   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4556 }
4557
4558 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4559                         int actual_player_x, int actual_player_y)
4560 {
4561   // this is used for non-R'n'D game engines to update certain engine values
4562
4563   // needed to determine if sounds are played within the visible screen area
4564   scroll_x = actual_scroll_x;
4565   scroll_y = actual_scroll_y;
4566
4567   // needed to get player position for "follow finger" playing input method
4568   local_player->jx = actual_player_x;
4569   local_player->jy = actual_player_y;
4570 }
4571
4572 void InitMovDir(int x, int y)
4573 {
4574   int i, element = Tile[x][y];
4575   static int xy[4][2] =
4576   {
4577     {  0, +1 },
4578     { +1,  0 },
4579     {  0, -1 },
4580     { -1,  0 }
4581   };
4582   static int direction[3][4] =
4583   {
4584     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4585     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4586     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4587   };
4588
4589   switch (element)
4590   {
4591     case EL_BUG_RIGHT:
4592     case EL_BUG_UP:
4593     case EL_BUG_LEFT:
4594     case EL_BUG_DOWN:
4595       Tile[x][y] = EL_BUG;
4596       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4597       break;
4598
4599     case EL_SPACESHIP_RIGHT:
4600     case EL_SPACESHIP_UP:
4601     case EL_SPACESHIP_LEFT:
4602     case EL_SPACESHIP_DOWN:
4603       Tile[x][y] = EL_SPACESHIP;
4604       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4605       break;
4606
4607     case EL_BD_BUTTERFLY_RIGHT:
4608     case EL_BD_BUTTERFLY_UP:
4609     case EL_BD_BUTTERFLY_LEFT:
4610     case EL_BD_BUTTERFLY_DOWN:
4611       Tile[x][y] = EL_BD_BUTTERFLY;
4612       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4613       break;
4614
4615     case EL_BD_FIREFLY_RIGHT:
4616     case EL_BD_FIREFLY_UP:
4617     case EL_BD_FIREFLY_LEFT:
4618     case EL_BD_FIREFLY_DOWN:
4619       Tile[x][y] = EL_BD_FIREFLY;
4620       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4621       break;
4622
4623     case EL_PACMAN_RIGHT:
4624     case EL_PACMAN_UP:
4625     case EL_PACMAN_LEFT:
4626     case EL_PACMAN_DOWN:
4627       Tile[x][y] = EL_PACMAN;
4628       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4629       break;
4630
4631     case EL_YAMYAM_LEFT:
4632     case EL_YAMYAM_RIGHT:
4633     case EL_YAMYAM_UP:
4634     case EL_YAMYAM_DOWN:
4635       Tile[x][y] = EL_YAMYAM;
4636       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4637       break;
4638
4639     case EL_SP_SNIKSNAK:
4640       MovDir[x][y] = MV_UP;
4641       break;
4642
4643     case EL_SP_ELECTRON:
4644       MovDir[x][y] = MV_LEFT;
4645       break;
4646
4647     case EL_MOLE_LEFT:
4648     case EL_MOLE_RIGHT:
4649     case EL_MOLE_UP:
4650     case EL_MOLE_DOWN:
4651       Tile[x][y] = EL_MOLE;
4652       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4653       break;
4654
4655     case EL_SPRING_LEFT:
4656     case EL_SPRING_RIGHT:
4657       Tile[x][y] = EL_SPRING;
4658       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4659       break;
4660
4661     default:
4662       if (IS_CUSTOM_ELEMENT(element))
4663       {
4664         struct ElementInfo *ei = &element_info[element];
4665         int move_direction_initial = ei->move_direction_initial;
4666         int move_pattern = ei->move_pattern;
4667
4668         if (move_direction_initial == MV_START_PREVIOUS)
4669         {
4670           if (MovDir[x][y] != MV_NONE)
4671             return;
4672
4673           move_direction_initial = MV_START_AUTOMATIC;
4674         }
4675
4676         if (move_direction_initial == MV_START_RANDOM)
4677           MovDir[x][y] = 1 << RND(4);
4678         else if (move_direction_initial & MV_ANY_DIRECTION)
4679           MovDir[x][y] = move_direction_initial;
4680         else if (move_pattern == MV_ALL_DIRECTIONS ||
4681                  move_pattern == MV_TURNING_LEFT ||
4682                  move_pattern == MV_TURNING_RIGHT ||
4683                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4684                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4685                  move_pattern == MV_TURNING_RANDOM)
4686           MovDir[x][y] = 1 << RND(4);
4687         else if (move_pattern == MV_HORIZONTAL)
4688           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4689         else if (move_pattern == MV_VERTICAL)
4690           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4691         else if (move_pattern & MV_ANY_DIRECTION)
4692           MovDir[x][y] = element_info[element].move_pattern;
4693         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4694                  move_pattern == MV_ALONG_RIGHT_SIDE)
4695         {
4696           // use random direction as default start direction
4697           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4698             MovDir[x][y] = 1 << RND(4);
4699
4700           for (i = 0; i < NUM_DIRECTIONS; i++)
4701           {
4702             int x1 = x + xy[i][0];
4703             int y1 = y + xy[i][1];
4704
4705             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4706             {
4707               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4708                 MovDir[x][y] = direction[0][i];
4709               else
4710                 MovDir[x][y] = direction[1][i];
4711
4712               break;
4713             }
4714           }
4715         }                
4716       }
4717       else
4718       {
4719         MovDir[x][y] = 1 << RND(4);
4720
4721         if (element != EL_BUG &&
4722             element != EL_SPACESHIP &&
4723             element != EL_BD_BUTTERFLY &&
4724             element != EL_BD_FIREFLY)
4725           break;
4726
4727         for (i = 0; i < NUM_DIRECTIONS; i++)
4728         {
4729           int x1 = x + xy[i][0];
4730           int y1 = y + xy[i][1];
4731
4732           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4733           {
4734             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4735             {
4736               MovDir[x][y] = direction[0][i];
4737               break;
4738             }
4739             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4740                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4741             {
4742               MovDir[x][y] = direction[1][i];
4743               break;
4744             }
4745           }
4746         }
4747       }
4748       break;
4749   }
4750
4751   GfxDir[x][y] = MovDir[x][y];
4752 }
4753
4754 void InitAmoebaNr(int x, int y)
4755 {
4756   int i;
4757   int group_nr = AmoebaNeighbourNr(x, y);
4758
4759   if (group_nr == 0)
4760   {
4761     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4762     {
4763       if (AmoebaCnt[i] == 0)
4764       {
4765         group_nr = i;
4766         break;
4767       }
4768     }
4769   }
4770
4771   AmoebaNr[x][y] = group_nr;
4772   AmoebaCnt[group_nr]++;
4773   AmoebaCnt2[group_nr]++;
4774 }
4775
4776 static void LevelSolved_SetFinalGameValues(void)
4777 {
4778   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4779   game.score_time_final = (level.use_step_counter ? TimePlayed :
4780                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4781
4782   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4783                       game_em.lev->score :
4784                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4785                       game_mm.score :
4786                       game.score);
4787
4788   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4789                        MM_HEALTH(game_mm.laser_overload_value) :
4790                        game.health);
4791
4792   game.LevelSolved_CountingTime = game.time_final;
4793   game.LevelSolved_CountingScore = game.score_final;
4794   game.LevelSolved_CountingHealth = game.health_final;
4795 }
4796
4797 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4798 {
4799   game.LevelSolved_CountingTime = time;
4800   game.LevelSolved_CountingScore = score;
4801   game.LevelSolved_CountingHealth = health;
4802
4803   game_panel_controls[GAME_PANEL_TIME].value = time;
4804   game_panel_controls[GAME_PANEL_SCORE].value = score;
4805   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4806
4807   DisplayGameControlValues();
4808 }
4809
4810 static void LevelSolved(void)
4811 {
4812   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4813       game.players_still_needed > 0)
4814     return;
4815
4816   game.LevelSolved = TRUE;
4817   game.GameOver = TRUE;
4818
4819   tape.solved = TRUE;
4820
4821   // needed here to display correct panel values while player walks into exit
4822   LevelSolved_SetFinalGameValues();
4823 }
4824
4825 void GameWon(void)
4826 {
4827   static int time_count_steps;
4828   static int time, time_final;
4829   static float score, score_final; // needed for time score < 10 for 10 seconds
4830   static int health, health_final;
4831   static int game_over_delay_1 = 0;
4832   static int game_over_delay_2 = 0;
4833   static int game_over_delay_3 = 0;
4834   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4835   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4836
4837   if (!game.LevelSolved_GameWon)
4838   {
4839     int i;
4840
4841     // do not start end game actions before the player stops moving (to exit)
4842     if (local_player->active && local_player->MovPos)
4843       return;
4844
4845     // calculate final game values after player finished walking into exit
4846     LevelSolved_SetFinalGameValues();
4847
4848     game.LevelSolved_GameWon = TRUE;
4849     game.LevelSolved_SaveTape = tape.recording;
4850     game.LevelSolved_SaveScore = !tape.playing;
4851
4852     if (!tape.playing)
4853     {
4854       LevelStats_incSolved(level_nr);
4855
4856       SaveLevelSetup_SeriesInfo();
4857     }
4858
4859     if (tape.auto_play)         // tape might already be stopped here
4860       tape.auto_play_level_solved = TRUE;
4861
4862     TapeStop();
4863
4864     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4865     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4866     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4867
4868     time = time_final = game.time_final;
4869     score = score_final = game.score_final;
4870     health = health_final = game.health_final;
4871
4872     // update game panel values before (delayed) counting of score (if any)
4873     LevelSolved_DisplayFinalGameValues(time, score, health);
4874
4875     // if level has time score defined, calculate new final game values
4876     if (time_score > 0)
4877     {
4878       int time_final_max = 999;
4879       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4880       int time_frames = 0;
4881       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4882       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4883
4884       if (TimeLeft > 0)
4885       {
4886         time_final = 0;
4887         time_frames = time_frames_left;
4888       }
4889       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4890       {
4891         time_final = time_final_max;
4892         time_frames = time_frames_final_max - time_frames_played;
4893       }
4894
4895       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4896
4897       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4898
4899       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4900       {
4901         health_final = 0;
4902         score_final += health * time_score;
4903       }
4904
4905       game.score_final = score_final;
4906       game.health_final = health_final;
4907     }
4908
4909     // if not counting score after game, immediately update game panel values
4910     if (level_editor_test_game || !setup.count_score_after_game)
4911     {
4912       time = time_final;
4913       score = score_final;
4914
4915       LevelSolved_DisplayFinalGameValues(time, score, health);
4916     }
4917
4918     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4919     {
4920       // check if last player has left the level
4921       if (game.exit_x >= 0 &&
4922           game.exit_y >= 0)
4923       {
4924         int x = game.exit_x;
4925         int y = game.exit_y;
4926         int element = Tile[x][y];
4927
4928         // close exit door after last player
4929         if ((game.all_players_gone &&
4930              (element == EL_EXIT_OPEN ||
4931               element == EL_SP_EXIT_OPEN ||
4932               element == EL_STEEL_EXIT_OPEN)) ||
4933             element == EL_EM_EXIT_OPEN ||
4934             element == EL_EM_STEEL_EXIT_OPEN)
4935         {
4936
4937           Tile[x][y] =
4938             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4939              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4940              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4941              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4942              EL_EM_STEEL_EXIT_CLOSING);
4943
4944           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4945         }
4946
4947         // player disappears
4948         DrawLevelField(x, y);
4949       }
4950
4951       for (i = 0; i < MAX_PLAYERS; i++)
4952       {
4953         struct PlayerInfo *player = &stored_player[i];
4954
4955         if (player->present)
4956         {
4957           RemovePlayer(player);
4958
4959           // player disappears
4960           DrawLevelField(player->jx, player->jy);
4961         }
4962       }
4963     }
4964
4965     PlaySound(SND_GAME_WINNING);
4966   }
4967
4968   if (setup.count_score_after_game)
4969   {
4970     if (time != time_final)
4971     {
4972       if (game_over_delay_1 > 0)
4973       {
4974         game_over_delay_1--;
4975
4976         return;
4977       }
4978
4979       int time_to_go = ABS(time_final - time);
4980       int time_count_dir = (time < time_final ? +1 : -1);
4981
4982       if (time_to_go < time_count_steps)
4983         time_count_steps = 1;
4984
4985       time  += time_count_steps * time_count_dir;
4986       score += time_count_steps * time_score;
4987
4988       // set final score to correct rounding differences after counting score
4989       if (time == time_final)
4990         score = score_final;
4991
4992       LevelSolved_DisplayFinalGameValues(time, score, health);
4993
4994       if (time == time_final)
4995         StopSound(SND_GAME_LEVELTIME_BONUS);
4996       else if (setup.sound_loops)
4997         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4998       else
4999         PlaySound(SND_GAME_LEVELTIME_BONUS);
5000
5001       return;
5002     }
5003
5004     if (health != health_final)
5005     {
5006       if (game_over_delay_2 > 0)
5007       {
5008         game_over_delay_2--;
5009
5010         return;
5011       }
5012
5013       int health_count_dir = (health < health_final ? +1 : -1);
5014
5015       health += health_count_dir;
5016       score  += time_score;
5017
5018       LevelSolved_DisplayFinalGameValues(time, score, health);
5019
5020       if (health == health_final)
5021         StopSound(SND_GAME_LEVELTIME_BONUS);
5022       else if (setup.sound_loops)
5023         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5024       else
5025         PlaySound(SND_GAME_LEVELTIME_BONUS);
5026
5027       return;
5028     }
5029   }
5030
5031   game.panel.active = FALSE;
5032
5033   if (game_over_delay_3 > 0)
5034   {
5035     game_over_delay_3--;
5036
5037     return;
5038   }
5039
5040   GameEnd();
5041 }
5042
5043 void GameEnd(void)
5044 {
5045   // used instead of "level_nr" (needed for network games)
5046   int last_level_nr = levelset.level_nr;
5047   boolean tape_saved = FALSE;
5048
5049   game.LevelSolved_GameEnd = TRUE;
5050
5051   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5052   {
5053     // make sure that request dialog to save tape does not open door again
5054     if (!global.use_envelope_request)
5055       CloseDoor(DOOR_CLOSE_1);
5056
5057     // ask to save tape
5058     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5059
5060     // set unique basename for score tape (also saved in high score table)
5061     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5062   }
5063
5064   // if no tape is to be saved, close both doors simultaneously
5065   CloseDoor(DOOR_CLOSE_ALL);
5066
5067   if (level_editor_test_game || score_info_tape_play)
5068   {
5069     SetGameStatus(GAME_MODE_MAIN);
5070
5071     DrawMainMenu();
5072
5073     return;
5074   }
5075
5076   if (!game.LevelSolved_SaveScore)
5077   {
5078     SetGameStatus(GAME_MODE_MAIN);
5079
5080     DrawMainMenu();
5081
5082     return;
5083   }
5084
5085   if (level_nr == leveldir_current->handicap_level)
5086   {
5087     leveldir_current->handicap_level++;
5088
5089     SaveLevelSetup_SeriesInfo();
5090   }
5091
5092   // save score and score tape before potentially erasing tape below
5093   NewHighScore(last_level_nr, tape_saved);
5094
5095   if (setup.increment_levels &&
5096       level_nr < leveldir_current->last_level &&
5097       !network_playing)
5098   {
5099     level_nr++;         // advance to next level
5100     TapeErase();        // start with empty tape
5101
5102     if (setup.auto_play_next_level)
5103     {
5104       scores.continue_playing = TRUE;
5105       scores.next_level_nr = level_nr;
5106
5107       LoadLevel(level_nr);
5108
5109       SaveLevelSetup_SeriesInfo();
5110     }
5111   }
5112
5113   if (scores.last_added >= 0 && setup.show_scores_after_game)
5114   {
5115     SetGameStatus(GAME_MODE_SCORES);
5116
5117     DrawHallOfFame(last_level_nr);
5118   }
5119   else if (scores.continue_playing)
5120   {
5121     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5122   }
5123   else
5124   {
5125     SetGameStatus(GAME_MODE_MAIN);
5126
5127     DrawMainMenu();
5128   }
5129 }
5130
5131 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5132                          boolean one_score_entry_per_name)
5133 {
5134   int i;
5135
5136   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5137     return -1;
5138
5139   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5140   {
5141     struct ScoreEntry *entry = &list->entry[i];
5142     boolean score_is_better = (new_entry->score >  entry->score);
5143     boolean score_is_equal  = (new_entry->score == entry->score);
5144     boolean time_is_better  = (new_entry->time  <  entry->time);
5145     boolean time_is_equal   = (new_entry->time  == entry->time);
5146     boolean better_by_score = (score_is_better ||
5147                                (score_is_equal && time_is_better));
5148     boolean better_by_time  = (time_is_better ||
5149                                (time_is_equal && score_is_better));
5150     boolean is_better = (level.rate_time_over_score ? better_by_time :
5151                          better_by_score);
5152     boolean entry_is_empty = (entry->score == 0 &&
5153                               entry->time == 0);
5154
5155     // prevent adding server score entries if also existing in local score file
5156     // (special case: historic score entries have an empty tape basename entry)
5157     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5158         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5159     {
5160       // add fields from server score entry not stored in local score entry
5161       // (currently, this means setting platform, version and country fields;
5162       // in rare cases, this may also correct an invalid score value, as
5163       // historic scores might have been truncated to 16-bit values locally)
5164       *entry = *new_entry;
5165
5166       return -1;
5167     }
5168
5169     if (is_better || entry_is_empty)
5170     {
5171       // player has made it to the hall of fame
5172
5173       if (i < MAX_SCORE_ENTRIES - 1)
5174       {
5175         int m = MAX_SCORE_ENTRIES - 1;
5176         int l;
5177
5178         if (one_score_entry_per_name)
5179         {
5180           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5181             if (strEqual(list->entry[l].name, new_entry->name))
5182               m = l;
5183
5184           if (m == i)   // player's new highscore overwrites his old one
5185             goto put_into_list;
5186         }
5187
5188         for (l = m; l > i; l--)
5189           list->entry[l] = list->entry[l - 1];
5190       }
5191
5192       put_into_list:
5193
5194       *entry = *new_entry;
5195
5196       return i;
5197     }
5198     else if (one_score_entry_per_name &&
5199              strEqual(entry->name, new_entry->name))
5200     {
5201       // player already in high score list with better score or time
5202
5203       return -1;
5204     }
5205   }
5206
5207   // special case: new score is beyond the last high score list position
5208   return MAX_SCORE_ENTRIES;
5209 }
5210
5211 void NewHighScore(int level_nr, boolean tape_saved)
5212 {
5213   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5214   boolean one_per_name = FALSE;
5215
5216   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5217   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5218
5219   new_entry.score = game.score_final;
5220   new_entry.time = game.score_time_final;
5221
5222   LoadScore(level_nr);
5223
5224   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5225
5226   if (scores.last_added >= MAX_SCORE_ENTRIES)
5227   {
5228     scores.last_added = MAX_SCORE_ENTRIES - 1;
5229     scores.force_last_added = TRUE;
5230
5231     scores.entry[scores.last_added] = new_entry;
5232
5233     // store last added local score entry (before merging server scores)
5234     scores.last_added_local = scores.last_added;
5235
5236     return;
5237   }
5238
5239   if (scores.last_added < 0)
5240     return;
5241
5242   SaveScore(level_nr);
5243
5244   // store last added local score entry (before merging server scores)
5245   scores.last_added_local = scores.last_added;
5246
5247   if (!game.LevelSolved_SaveTape)
5248     return;
5249
5250   SaveScoreTape(level_nr);
5251
5252   if (setup.ask_for_using_api_server)
5253   {
5254     setup.use_api_server =
5255       Request("Upload your score and tape to the high score server?", REQ_ASK);
5256
5257     if (!setup.use_api_server)
5258       Request("Not using high score server! Use setup menu to enable again!",
5259               REQ_CONFIRM);
5260
5261     runtime.use_api_server = setup.use_api_server;
5262
5263     // after asking for using API server once, do not ask again
5264     setup.ask_for_using_api_server = FALSE;
5265
5266     SaveSetup_ServerSetup();
5267   }
5268
5269   SaveServerScore(level_nr, tape_saved);
5270 }
5271
5272 void MergeServerScore(void)
5273 {
5274   struct ScoreEntry last_added_entry;
5275   boolean one_per_name = FALSE;
5276   int i;
5277
5278   if (scores.last_added >= 0)
5279     last_added_entry = scores.entry[scores.last_added];
5280
5281   for (i = 0; i < server_scores.num_entries; i++)
5282   {
5283     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5284
5285     if (pos >= 0 && pos <= scores.last_added)
5286       scores.last_added++;
5287   }
5288
5289   if (scores.last_added >= MAX_SCORE_ENTRIES)
5290   {
5291     scores.last_added = MAX_SCORE_ENTRIES - 1;
5292     scores.force_last_added = TRUE;
5293
5294     scores.entry[scores.last_added] = last_added_entry;
5295   }
5296 }
5297
5298 static int getElementMoveStepsizeExt(int x, int y, int direction)
5299 {
5300   int element = Tile[x][y];
5301   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5302   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5303   int horiz_move = (dx != 0);
5304   int sign = (horiz_move ? dx : dy);
5305   int step = sign * element_info[element].move_stepsize;
5306
5307   // special values for move stepsize for spring and things on conveyor belt
5308   if (horiz_move)
5309   {
5310     if (CAN_FALL(element) &&
5311         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5312       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5313     else if (element == EL_SPRING)
5314       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5315   }
5316
5317   return step;
5318 }
5319
5320 static int getElementMoveStepsize(int x, int y)
5321 {
5322   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5323 }
5324
5325 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5326 {
5327   if (player->GfxAction != action || player->GfxDir != dir)
5328   {
5329     player->GfxAction = action;
5330     player->GfxDir = dir;
5331     player->Frame = 0;
5332     player->StepFrame = 0;
5333   }
5334 }
5335
5336 static void ResetGfxFrame(int x, int y)
5337 {
5338   // profiling showed that "autotest" spends 10~20% of its time in this function
5339   if (DrawingDeactivatedField())
5340     return;
5341
5342   int element = Tile[x][y];
5343   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5344
5345   if (graphic_info[graphic].anim_global_sync)
5346     GfxFrame[x][y] = FrameCounter;
5347   else if (graphic_info[graphic].anim_global_anim_sync)
5348     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5349   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5350     GfxFrame[x][y] = CustomValue[x][y];
5351   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5352     GfxFrame[x][y] = element_info[element].collect_score;
5353   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5354     GfxFrame[x][y] = ChangeDelay[x][y];
5355 }
5356
5357 static void ResetGfxAnimation(int x, int y)
5358 {
5359   GfxAction[x][y] = ACTION_DEFAULT;
5360   GfxDir[x][y] = MovDir[x][y];
5361   GfxFrame[x][y] = 0;
5362
5363   ResetGfxFrame(x, y);
5364 }
5365
5366 static void ResetRandomAnimationValue(int x, int y)
5367 {
5368   GfxRandom[x][y] = INIT_GFX_RANDOM();
5369 }
5370
5371 static void InitMovingField(int x, int y, int direction)
5372 {
5373   int element = Tile[x][y];
5374   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5375   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5376   int newx = x + dx;
5377   int newy = y + dy;
5378   boolean is_moving_before, is_moving_after;
5379
5380   // check if element was/is moving or being moved before/after mode change
5381   is_moving_before = (WasJustMoving[x][y] != 0);
5382   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5383
5384   // reset animation only for moving elements which change direction of moving
5385   // or which just started or stopped moving
5386   // (else CEs with property "can move" / "not moving" are reset each frame)
5387   if (is_moving_before != is_moving_after ||
5388       direction != MovDir[x][y])
5389     ResetGfxAnimation(x, y);
5390
5391   MovDir[x][y] = direction;
5392   GfxDir[x][y] = direction;
5393
5394   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5395                      direction == MV_DOWN && CAN_FALL(element) ?
5396                      ACTION_FALLING : ACTION_MOVING);
5397
5398   // this is needed for CEs with property "can move" / "not moving"
5399
5400   if (is_moving_after)
5401   {
5402     if (Tile[newx][newy] == EL_EMPTY)
5403       Tile[newx][newy] = EL_BLOCKED;
5404
5405     MovDir[newx][newy] = MovDir[x][y];
5406
5407     CustomValue[newx][newy] = CustomValue[x][y];
5408
5409     GfxFrame[newx][newy] = GfxFrame[x][y];
5410     GfxRandom[newx][newy] = GfxRandom[x][y];
5411     GfxAction[newx][newy] = GfxAction[x][y];
5412     GfxDir[newx][newy] = GfxDir[x][y];
5413   }
5414 }
5415
5416 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5417 {
5418   int direction = MovDir[x][y];
5419   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5420   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5421
5422   *goes_to_x = newx;
5423   *goes_to_y = newy;
5424 }
5425
5426 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5427 {
5428   int direction = MovDir[x][y];
5429   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5430   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5431
5432   *comes_from_x = oldx;
5433   *comes_from_y = oldy;
5434 }
5435
5436 static int MovingOrBlocked2Element(int x, int y)
5437 {
5438   int element = Tile[x][y];
5439
5440   if (element == EL_BLOCKED)
5441   {
5442     int oldx, oldy;
5443
5444     Blocked2Moving(x, y, &oldx, &oldy);
5445
5446     return Tile[oldx][oldy];
5447   }
5448
5449   return element;
5450 }
5451
5452 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5453 {
5454   // like MovingOrBlocked2Element(), but if element is moving
5455   // and (x, y) is the field the moving element is just leaving,
5456   // return EL_BLOCKED instead of the element value
5457   int element = Tile[x][y];
5458
5459   if (IS_MOVING(x, y))
5460   {
5461     if (element == EL_BLOCKED)
5462     {
5463       int oldx, oldy;
5464
5465       Blocked2Moving(x, y, &oldx, &oldy);
5466       return Tile[oldx][oldy];
5467     }
5468     else
5469       return EL_BLOCKED;
5470   }
5471   else
5472     return element;
5473 }
5474
5475 static void RemoveField(int x, int y)
5476 {
5477   Tile[x][y] = EL_EMPTY;
5478
5479   MovPos[x][y] = 0;
5480   MovDir[x][y] = 0;
5481   MovDelay[x][y] = 0;
5482
5483   CustomValue[x][y] = 0;
5484
5485   AmoebaNr[x][y] = 0;
5486   ChangeDelay[x][y] = 0;
5487   ChangePage[x][y] = -1;
5488   Pushed[x][y] = FALSE;
5489
5490   GfxElement[x][y] = EL_UNDEFINED;
5491   GfxAction[x][y] = ACTION_DEFAULT;
5492   GfxDir[x][y] = MV_NONE;
5493 }
5494
5495 static void RemoveMovingField(int x, int y)
5496 {
5497   int oldx = x, oldy = y, newx = x, newy = y;
5498   int element = Tile[x][y];
5499   int next_element = EL_UNDEFINED;
5500
5501   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5502     return;
5503
5504   if (IS_MOVING(x, y))
5505   {
5506     Moving2Blocked(x, y, &newx, &newy);
5507
5508     if (Tile[newx][newy] != EL_BLOCKED)
5509     {
5510       // element is moving, but target field is not free (blocked), but
5511       // already occupied by something different (example: acid pool);
5512       // in this case, only remove the moving field, but not the target
5513
5514       RemoveField(oldx, oldy);
5515
5516       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5517
5518       TEST_DrawLevelField(oldx, oldy);
5519
5520       return;
5521     }
5522   }
5523   else if (element == EL_BLOCKED)
5524   {
5525     Blocked2Moving(x, y, &oldx, &oldy);
5526     if (!IS_MOVING(oldx, oldy))
5527       return;
5528   }
5529
5530   if (element == EL_BLOCKED &&
5531       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5532        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5533        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5534        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5535        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5536        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5537     next_element = get_next_element(Tile[oldx][oldy]);
5538
5539   RemoveField(oldx, oldy);
5540   RemoveField(newx, newy);
5541
5542   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5543
5544   if (next_element != EL_UNDEFINED)
5545     Tile[oldx][oldy] = next_element;
5546
5547   TEST_DrawLevelField(oldx, oldy);
5548   TEST_DrawLevelField(newx, newy);
5549 }
5550
5551 void DrawDynamite(int x, int y)
5552 {
5553   int sx = SCREENX(x), sy = SCREENY(y);
5554   int graphic = el2img(Tile[x][y]);
5555   int frame;
5556
5557   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5558     return;
5559
5560   if (IS_WALKABLE_INSIDE(Back[x][y]))
5561     return;
5562
5563   if (Back[x][y])
5564     DrawLevelElement(x, y, Back[x][y]);
5565   else if (Store[x][y])
5566     DrawLevelElement(x, y, Store[x][y]);
5567   else if (game.use_masked_elements)
5568     DrawLevelElement(x, y, EL_EMPTY);
5569
5570   frame = getGraphicAnimationFrameXY(graphic, x, y);
5571
5572   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5573     DrawGraphicThruMask(sx, sy, graphic, frame);
5574   else
5575     DrawGraphic(sx, sy, graphic, frame);
5576 }
5577
5578 static void CheckDynamite(int x, int y)
5579 {
5580   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5581   {
5582     MovDelay[x][y]--;
5583
5584     if (MovDelay[x][y] != 0)
5585     {
5586       DrawDynamite(x, y);
5587       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5588
5589       return;
5590     }
5591   }
5592
5593   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5594
5595   Bang(x, y);
5596 }
5597
5598 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5599 {
5600   boolean num_checked_players = 0;
5601   int i;
5602
5603   for (i = 0; i < MAX_PLAYERS; i++)
5604   {
5605     if (stored_player[i].active)
5606     {
5607       int sx = stored_player[i].jx;
5608       int sy = stored_player[i].jy;
5609
5610       if (num_checked_players == 0)
5611       {
5612         *sx1 = *sx2 = sx;
5613         *sy1 = *sy2 = sy;
5614       }
5615       else
5616       {
5617         *sx1 = MIN(*sx1, sx);
5618         *sy1 = MIN(*sy1, sy);
5619         *sx2 = MAX(*sx2, sx);
5620         *sy2 = MAX(*sy2, sy);
5621       }
5622
5623       num_checked_players++;
5624     }
5625   }
5626 }
5627
5628 static boolean checkIfAllPlayersFitToScreen_RND(void)
5629 {
5630   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5631
5632   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5633
5634   return (sx2 - sx1 < SCR_FIELDX &&
5635           sy2 - sy1 < SCR_FIELDY);
5636 }
5637
5638 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5639 {
5640   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5641
5642   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5643
5644   *sx = (sx1 + sx2) / 2;
5645   *sy = (sy1 + sy2) / 2;
5646 }
5647
5648 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5649                                boolean center_screen, boolean quick_relocation)
5650 {
5651   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5652   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5653   boolean no_delay = (tape.warp_forward);
5654   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5655   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5656   int new_scroll_x, new_scroll_y;
5657
5658   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5659   {
5660     // case 1: quick relocation inside visible screen (without scrolling)
5661
5662     RedrawPlayfield();
5663
5664     return;
5665   }
5666
5667   if (!level.shifted_relocation || center_screen)
5668   {
5669     // relocation _with_ centering of screen
5670
5671     new_scroll_x = SCROLL_POSITION_X(x);
5672     new_scroll_y = SCROLL_POSITION_Y(y);
5673   }
5674   else
5675   {
5676     // relocation _without_ centering of screen
5677
5678     // apply distance between old and new player position to scroll position
5679     int shifted_scroll_x = scroll_x + (x - old_x);
5680     int shifted_scroll_y = scroll_y + (y - old_y);
5681
5682     // make sure that shifted scroll position does not scroll beyond screen
5683     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5684     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5685   }
5686
5687   if (quick_relocation)
5688   {
5689     // case 2: quick relocation (redraw without visible scrolling)
5690
5691     scroll_x = new_scroll_x;
5692     scroll_y = new_scroll_y;
5693
5694     RedrawPlayfield();
5695
5696     return;
5697   }
5698
5699   // case 3: visible relocation (with scrolling to new position)
5700
5701   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5702
5703   SetVideoFrameDelay(wait_delay_value);
5704
5705   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5706   {
5707     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5708     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5709
5710     if (dx == 0 && dy == 0)             // no scrolling needed at all
5711       break;
5712
5713     scroll_x -= dx;
5714     scroll_y -= dy;
5715
5716     // set values for horizontal/vertical screen scrolling (half tile size)
5717     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5718     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5719     int pos_x = dx * TILEX / 2;
5720     int pos_y = dy * TILEY / 2;
5721     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5722     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5723
5724     ScrollLevel(dx, dy);
5725     DrawAllPlayers();
5726
5727     // scroll in two steps of half tile size to make things smoother
5728     BlitScreenToBitmapExt_RND(window, fx, fy);
5729
5730     // scroll second step to align at full tile size
5731     BlitScreenToBitmap(window);
5732   }
5733
5734   DrawAllPlayers();
5735   BackToFront();
5736
5737   SetVideoFrameDelay(frame_delay_value_old);
5738 }
5739
5740 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5741 {
5742   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5743   int player_nr = GET_PLAYER_NR(el_player);
5744   struct PlayerInfo *player = &stored_player[player_nr];
5745   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5746   boolean no_delay = (tape.warp_forward);
5747   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5748   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5749   int old_jx = player->jx;
5750   int old_jy = player->jy;
5751   int old_element = Tile[old_jx][old_jy];
5752   int element = Tile[jx][jy];
5753   boolean player_relocated = (old_jx != jx || old_jy != jy);
5754
5755   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5756   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5757   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5758   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5759   int leave_side_horiz = move_dir_horiz;
5760   int leave_side_vert  = move_dir_vert;
5761   int enter_side = enter_side_horiz | enter_side_vert;
5762   int leave_side = leave_side_horiz | leave_side_vert;
5763
5764   if (player->buried)           // do not reanimate dead player
5765     return;
5766
5767   if (!player_relocated)        // no need to relocate the player
5768     return;
5769
5770   if (IS_PLAYER(jx, jy))        // player already placed at new position
5771   {
5772     RemoveField(jx, jy);        // temporarily remove newly placed player
5773     DrawLevelField(jx, jy);
5774   }
5775
5776   if (player->present)
5777   {
5778     while (player->MovPos)
5779     {
5780       ScrollPlayer(player, SCROLL_GO_ON);
5781       ScrollScreen(NULL, SCROLL_GO_ON);
5782
5783       AdvanceFrameAndPlayerCounters(player->index_nr);
5784
5785       DrawPlayer(player);
5786
5787       BackToFront_WithFrameDelay(wait_delay_value);
5788     }
5789
5790     DrawPlayer(player);         // needed here only to cleanup last field
5791     DrawLevelField(player->jx, player->jy);     // remove player graphic
5792
5793     player->is_moving = FALSE;
5794   }
5795
5796   if (IS_CUSTOM_ELEMENT(old_element))
5797     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5798                                CE_LEFT_BY_PLAYER,
5799                                player->index_bit, leave_side);
5800
5801   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5802                                       CE_PLAYER_LEAVES_X,
5803                                       player->index_bit, leave_side);
5804
5805   Tile[jx][jy] = el_player;
5806   InitPlayerField(jx, jy, el_player, TRUE);
5807
5808   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5809      possible that the relocation target field did not contain a player element,
5810      but a walkable element, to which the new player was relocated -- in this
5811      case, restore that (already initialized!) element on the player field */
5812   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5813   {
5814     Tile[jx][jy] = element;     // restore previously existing element
5815   }
5816
5817   // only visually relocate centered player
5818   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5819                      FALSE, level.instant_relocation);
5820
5821   TestIfPlayerTouchesBadThing(jx, jy);
5822   TestIfPlayerTouchesCustomElement(jx, jy);
5823
5824   if (IS_CUSTOM_ELEMENT(element))
5825     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5826                                player->index_bit, enter_side);
5827
5828   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5829                                       player->index_bit, enter_side);
5830
5831   if (player->is_switching)
5832   {
5833     /* ensure that relocation while still switching an element does not cause
5834        a new element to be treated as also switched directly after relocation
5835        (this is important for teleporter switches that teleport the player to
5836        a place where another teleporter switch is in the same direction, which
5837        would then incorrectly be treated as immediately switched before the
5838        direction key that caused the switch was released) */
5839
5840     player->switch_x += jx - old_jx;
5841     player->switch_y += jy - old_jy;
5842   }
5843 }
5844
5845 static void Explode(int ex, int ey, int phase, int mode)
5846 {
5847   int x, y;
5848   int last_phase;
5849   int border_element;
5850
5851   if (game.explosions_delayed)
5852   {
5853     ExplodeField[ex][ey] = mode;
5854     return;
5855   }
5856
5857   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5858   {
5859     int center_element = Tile[ex][ey];
5860     int ce_value = CustomValue[ex][ey];
5861     int ce_score = element_info[center_element].collect_score;
5862     int artwork_element, explosion_element;     // set these values later
5863
5864     // remove things displayed in background while burning dynamite
5865     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5866       Back[ex][ey] = 0;
5867
5868     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5869     {
5870       // put moving element to center field (and let it explode there)
5871       center_element = MovingOrBlocked2Element(ex, ey);
5872       RemoveMovingField(ex, ey);
5873       Tile[ex][ey] = center_element;
5874     }
5875
5876     // now "center_element" is finally determined -- set related values now
5877     artwork_element = center_element;           // for custom player artwork
5878     explosion_element = center_element;         // for custom player artwork
5879
5880     if (IS_PLAYER(ex, ey))
5881     {
5882       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5883
5884       artwork_element = stored_player[player_nr].artwork_element;
5885
5886       if (level.use_explosion_element[player_nr])
5887       {
5888         explosion_element = level.explosion_element[player_nr];
5889         artwork_element = explosion_element;
5890       }
5891     }
5892
5893     if (mode == EX_TYPE_NORMAL ||
5894         mode == EX_TYPE_CENTER ||
5895         mode == EX_TYPE_CROSS)
5896       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5897
5898     last_phase = element_info[explosion_element].explosion_delay + 1;
5899
5900     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5901     {
5902       int xx = x - ex + 1;
5903       int yy = y - ey + 1;
5904       int element;
5905
5906       if (!IN_LEV_FIELD(x, y) ||
5907           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5908           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5909         continue;
5910
5911       element = Tile[x][y];
5912
5913       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5914       {
5915         element = MovingOrBlocked2Element(x, y);
5916
5917         if (!IS_EXPLOSION_PROOF(element))
5918           RemoveMovingField(x, y);
5919       }
5920
5921       // indestructible elements can only explode in center (but not flames)
5922       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5923                                            mode == EX_TYPE_BORDER)) ||
5924           element == EL_FLAMES)
5925         continue;
5926
5927       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5928          behaviour, for example when touching a yamyam that explodes to rocks
5929          with active deadly shield, a rock is created under the player !!! */
5930       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5931 #if 0
5932       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5933           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5934            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5935 #else
5936       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5937 #endif
5938       {
5939         if (IS_ACTIVE_BOMB(element))
5940         {
5941           // re-activate things under the bomb like gate or penguin
5942           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5943           Back[x][y] = 0;
5944         }
5945
5946         continue;
5947       }
5948
5949       // save walkable background elements while explosion on same tile
5950       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5951           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5952         Back[x][y] = element;
5953
5954       // ignite explodable elements reached by other explosion
5955       if (element == EL_EXPLOSION)
5956         element = Store2[x][y];
5957
5958       if (AmoebaNr[x][y] &&
5959           (element == EL_AMOEBA_FULL ||
5960            element == EL_BD_AMOEBA ||
5961            element == EL_AMOEBA_GROWING))
5962       {
5963         AmoebaCnt[AmoebaNr[x][y]]--;
5964         AmoebaCnt2[AmoebaNr[x][y]]--;
5965       }
5966
5967       RemoveField(x, y);
5968
5969       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5970       {
5971         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5972
5973         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5974
5975         if (PLAYERINFO(ex, ey)->use_murphy)
5976           Store[x][y] = EL_EMPTY;
5977       }
5978
5979       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5980       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5981       else if (IS_PLAYER_ELEMENT(center_element))
5982         Store[x][y] = EL_EMPTY;
5983       else if (center_element == EL_YAMYAM)
5984         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5985       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5986         Store[x][y] = element_info[center_element].content.e[xx][yy];
5987 #if 1
5988       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5989       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5990       // otherwise) -- FIX THIS !!!
5991       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5992         Store[x][y] = element_info[element].content.e[1][1];
5993 #else
5994       else if (!CAN_EXPLODE(element))
5995         Store[x][y] = element_info[element].content.e[1][1];
5996 #endif
5997       else
5998         Store[x][y] = EL_EMPTY;
5999
6000       if (IS_CUSTOM_ELEMENT(center_element))
6001         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6002                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6003                        Store[x][y] >= EL_PREV_CE_8 &&
6004                        Store[x][y] <= EL_NEXT_CE_8 ?
6005                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6006                        Store[x][y]);
6007
6008       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6009           center_element == EL_AMOEBA_TO_DIAMOND)
6010         Store2[x][y] = element;
6011
6012       Tile[x][y] = EL_EXPLOSION;
6013       GfxElement[x][y] = artwork_element;
6014
6015       ExplodePhase[x][y] = 1;
6016       ExplodeDelay[x][y] = last_phase;
6017
6018       Stop[x][y] = TRUE;
6019     }
6020
6021     if (center_element == EL_YAMYAM)
6022       game.yamyam_content_nr =
6023         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6024
6025     return;
6026   }
6027
6028   if (Stop[ex][ey])
6029     return;
6030
6031   x = ex;
6032   y = ey;
6033
6034   if (phase == 1)
6035     GfxFrame[x][y] = 0;         // restart explosion animation
6036
6037   last_phase = ExplodeDelay[x][y];
6038
6039   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6040
6041   // this can happen if the player leaves an explosion just in time
6042   if (GfxElement[x][y] == EL_UNDEFINED)
6043     GfxElement[x][y] = EL_EMPTY;
6044
6045   border_element = Store2[x][y];
6046   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047     border_element = StorePlayer[x][y];
6048
6049   if (phase == element_info[border_element].ignition_delay ||
6050       phase == last_phase)
6051   {
6052     boolean border_explosion = FALSE;
6053
6054     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055         !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     {
6057       KillPlayerUnlessExplosionProtected(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6061     {
6062       Tile[x][y] = Store2[x][y];
6063       Store2[x][y] = 0;
6064       Bang(x, y);
6065       border_explosion = TRUE;
6066     }
6067     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6068     {
6069       AmoebaToDiamond(x, y);
6070       Store2[x][y] = 0;
6071       border_explosion = TRUE;
6072     }
6073
6074     // if an element just explodes due to another explosion (chain-reaction),
6075     // do not immediately end the new explosion when it was the last frame of
6076     // the explosion (as it would be done in the following "if"-statement!)
6077     if (border_explosion && phase == last_phase)
6078       return;
6079   }
6080
6081   // this can happen if the player was just killed by an explosion
6082   if (GfxElement[x][y] == EL_UNDEFINED)
6083     GfxElement[x][y] = EL_EMPTY;
6084
6085   if (phase == last_phase)
6086   {
6087     int element;
6088
6089     element = Tile[x][y] = Store[x][y];
6090     Store[x][y] = Store2[x][y] = 0;
6091     GfxElement[x][y] = EL_UNDEFINED;
6092
6093     // player can escape from explosions and might therefore be still alive
6094     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6095         element <= EL_PLAYER_IS_EXPLODING_4)
6096     {
6097       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6098       int explosion_element = EL_PLAYER_1 + player_nr;
6099       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6100       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6101
6102       if (level.use_explosion_element[player_nr])
6103         explosion_element = level.explosion_element[player_nr];
6104
6105       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6106                     element_info[explosion_element].content.e[xx][yy]);
6107     }
6108
6109     // restore probably existing indestructible background element
6110     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6111       element = Tile[x][y] = Back[x][y];
6112     Back[x][y] = 0;
6113
6114     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6115     GfxDir[x][y] = MV_NONE;
6116     ChangeDelay[x][y] = 0;
6117     ChangePage[x][y] = -1;
6118
6119     CustomValue[x][y] = 0;
6120
6121     InitField_WithBug2(x, y, FALSE);
6122
6123     TEST_DrawLevelField(x, y);
6124
6125     TestIfElementTouchesCustomElement(x, y);
6126
6127     if (GFX_CRUMBLED(element))
6128       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6129
6130     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6131       StorePlayer[x][y] = 0;
6132
6133     if (IS_PLAYER_ELEMENT(element))
6134       RelocatePlayer(x, y, element);
6135   }
6136   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6137   {
6138     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6139     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6140
6141     if (phase == 1)
6142       TEST_DrawLevelFieldCrumbled(x, y);
6143
6144     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6145     {
6146       DrawLevelElement(x, y, Back[x][y]);
6147       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6148     }
6149     else if (IS_WALKABLE_UNDER(Back[x][y]))
6150     {
6151       DrawLevelGraphic(x, y, graphic, frame);
6152       DrawLevelElementThruMask(x, y, Back[x][y]);
6153     }
6154     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6155       DrawLevelGraphic(x, y, graphic, frame);
6156   }
6157 }
6158
6159 static void DynaExplode(int ex, int ey)
6160 {
6161   int i, j;
6162   int dynabomb_element = Tile[ex][ey];
6163   int dynabomb_size = 1;
6164   boolean dynabomb_xl = FALSE;
6165   struct PlayerInfo *player;
6166   struct XY *xy = xy_topdown;
6167
6168   if (IS_ACTIVE_BOMB(dynabomb_element))
6169   {
6170     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6171     dynabomb_size = player->dynabomb_size;
6172     dynabomb_xl = player->dynabomb_xl;
6173     player->dynabombs_left++;
6174   }
6175
6176   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6177
6178   for (i = 0; i < NUM_DIRECTIONS; i++)
6179   {
6180     for (j = 1; j <= dynabomb_size; j++)
6181     {
6182       int x = ex + j * xy[i].x;
6183       int y = ey + j * xy[i].y;
6184       int element;
6185
6186       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6187         break;
6188
6189       element = Tile[x][y];
6190
6191       // do not restart explosions of fields with active bombs
6192       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6193         continue;
6194
6195       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6196
6197       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6198           !IS_DIGGABLE(element) && !dynabomb_xl)
6199         break;
6200     }
6201   }
6202 }
6203
6204 void Bang(int x, int y)
6205 {
6206   int element = MovingOrBlocked2Element(x, y);
6207   int explosion_type = EX_TYPE_NORMAL;
6208
6209   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6210   {
6211     struct PlayerInfo *player = PLAYERINFO(x, y);
6212
6213     element = Tile[x][y] = player->initial_element;
6214
6215     if (level.use_explosion_element[player->index_nr])
6216     {
6217       int explosion_element = level.explosion_element[player->index_nr];
6218
6219       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6220         explosion_type = EX_TYPE_CROSS;
6221       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6222         explosion_type = EX_TYPE_CENTER;
6223     }
6224   }
6225
6226   switch (element)
6227   {
6228     case EL_BUG:
6229     case EL_SPACESHIP:
6230     case EL_BD_BUTTERFLY:
6231     case EL_BD_FIREFLY:
6232     case EL_YAMYAM:
6233     case EL_DARK_YAMYAM:
6234     case EL_ROBOT:
6235     case EL_PACMAN:
6236     case EL_MOLE:
6237       RaiseScoreElement(element);
6238       break;
6239
6240     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6241     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6242     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6243     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6244     case EL_DYNABOMB_INCREASE_NUMBER:
6245     case EL_DYNABOMB_INCREASE_SIZE:
6246     case EL_DYNABOMB_INCREASE_POWER:
6247       explosion_type = EX_TYPE_DYNA;
6248       break;
6249
6250     case EL_DC_LANDMINE:
6251       explosion_type = EX_TYPE_CENTER;
6252       break;
6253
6254     case EL_PENGUIN:
6255     case EL_LAMP:
6256     case EL_LAMP_ACTIVE:
6257     case EL_AMOEBA_TO_DIAMOND:
6258       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6259         explosion_type = EX_TYPE_CENTER;
6260       break;
6261
6262     default:
6263       if (element_info[element].explosion_type == EXPLODES_CROSS)
6264         explosion_type = EX_TYPE_CROSS;
6265       else if (element_info[element].explosion_type == EXPLODES_1X1)
6266         explosion_type = EX_TYPE_CENTER;
6267       break;
6268   }
6269
6270   if (explosion_type == EX_TYPE_DYNA)
6271     DynaExplode(x, y);
6272   else
6273     Explode(x, y, EX_PHASE_START, explosion_type);
6274
6275   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6276 }
6277
6278 static void SplashAcid(int x, int y)
6279 {
6280   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6281       (!IN_LEV_FIELD(x - 1, y - 2) ||
6282        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6283     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6284
6285   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6286       (!IN_LEV_FIELD(x + 1, y - 2) ||
6287        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6288     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6289
6290   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6291 }
6292
6293 static void InitBeltMovement(void)
6294 {
6295   static int belt_base_element[4] =
6296   {
6297     EL_CONVEYOR_BELT_1_LEFT,
6298     EL_CONVEYOR_BELT_2_LEFT,
6299     EL_CONVEYOR_BELT_3_LEFT,
6300     EL_CONVEYOR_BELT_4_LEFT
6301   };
6302   static int belt_base_active_element[4] =
6303   {
6304     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6305     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6306     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6307     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6308   };
6309
6310   int x, y, i, j;
6311
6312   // set frame order for belt animation graphic according to belt direction
6313   for (i = 0; i < NUM_BELTS; i++)
6314   {
6315     int belt_nr = i;
6316
6317     for (j = 0; j < NUM_BELT_PARTS; j++)
6318     {
6319       int element = belt_base_active_element[belt_nr] + j;
6320       int graphic_1 = el2img(element);
6321       int graphic_2 = el2panelimg(element);
6322
6323       if (game.belt_dir[i] == MV_LEFT)
6324       {
6325         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6326         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6327       }
6328       else
6329       {
6330         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6331         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6332       }
6333     }
6334   }
6335
6336   SCAN_PLAYFIELD(x, y)
6337   {
6338     int element = Tile[x][y];
6339
6340     for (i = 0; i < NUM_BELTS; i++)
6341     {
6342       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6343       {
6344         int e_belt_nr = getBeltNrFromBeltElement(element);
6345         int belt_nr = i;
6346
6347         if (e_belt_nr == belt_nr)
6348         {
6349           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6350
6351           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6352         }
6353       }
6354     }
6355   }
6356 }
6357
6358 static void ToggleBeltSwitch(int x, int y)
6359 {
6360   static int belt_base_element[4] =
6361   {
6362     EL_CONVEYOR_BELT_1_LEFT,
6363     EL_CONVEYOR_BELT_2_LEFT,
6364     EL_CONVEYOR_BELT_3_LEFT,
6365     EL_CONVEYOR_BELT_4_LEFT
6366   };
6367   static int belt_base_active_element[4] =
6368   {
6369     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6370     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6371     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6372     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6373   };
6374   static int belt_base_switch_element[4] =
6375   {
6376     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6377     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6378     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6379     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6380   };
6381   static int belt_move_dir[4] =
6382   {
6383     MV_LEFT,
6384     MV_NONE,
6385     MV_RIGHT,
6386     MV_NONE,
6387   };
6388
6389   int element = Tile[x][y];
6390   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6391   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6392   int belt_dir = belt_move_dir[belt_dir_nr];
6393   int xx, yy, i;
6394
6395   if (!IS_BELT_SWITCH(element))
6396     return;
6397
6398   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6399   game.belt_dir[belt_nr] = belt_dir;
6400
6401   if (belt_dir_nr == 3)
6402     belt_dir_nr = 1;
6403
6404   // set frame order for belt animation graphic according to belt direction
6405   for (i = 0; i < NUM_BELT_PARTS; i++)
6406   {
6407     int element = belt_base_active_element[belt_nr] + i;
6408     int graphic_1 = el2img(element);
6409     int graphic_2 = el2panelimg(element);
6410
6411     if (belt_dir == MV_LEFT)
6412     {
6413       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6414       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6415     }
6416     else
6417     {
6418       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6419       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6420     }
6421   }
6422
6423   SCAN_PLAYFIELD(xx, yy)
6424   {
6425     int element = Tile[xx][yy];
6426
6427     if (IS_BELT_SWITCH(element))
6428     {
6429       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6430
6431       if (e_belt_nr == belt_nr)
6432       {
6433         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6434         TEST_DrawLevelField(xx, yy);
6435       }
6436     }
6437     else if (IS_BELT(element) && belt_dir != MV_NONE)
6438     {
6439       int e_belt_nr = getBeltNrFromBeltElement(element);
6440
6441       if (e_belt_nr == belt_nr)
6442       {
6443         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6444
6445         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6446         TEST_DrawLevelField(xx, yy);
6447       }
6448     }
6449     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6450     {
6451       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6452
6453       if (e_belt_nr == belt_nr)
6454       {
6455         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6456
6457         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6458         TEST_DrawLevelField(xx, yy);
6459       }
6460     }
6461   }
6462 }
6463
6464 static void ToggleSwitchgateSwitch(void)
6465 {
6466   int xx, yy;
6467
6468   game.switchgate_pos = !game.switchgate_pos;
6469
6470   SCAN_PLAYFIELD(xx, yy)
6471   {
6472     int element = Tile[xx][yy];
6473
6474     if (element == EL_SWITCHGATE_SWITCH_UP)
6475     {
6476       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6477       TEST_DrawLevelField(xx, yy);
6478     }
6479     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6480     {
6481       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6482       TEST_DrawLevelField(xx, yy);
6483     }
6484     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6485     {
6486       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6487       TEST_DrawLevelField(xx, yy);
6488     }
6489     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6490     {
6491       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_SWITCHGATE_OPEN ||
6495              element == EL_SWITCHGATE_OPENING)
6496     {
6497       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6498
6499       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6500     }
6501     else if (element == EL_SWITCHGATE_CLOSED ||
6502              element == EL_SWITCHGATE_CLOSING)
6503     {
6504       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6505
6506       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6507     }
6508   }
6509 }
6510
6511 static int getInvisibleActiveFromInvisibleElement(int element)
6512 {
6513   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6514           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6515           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6516           element);
6517 }
6518
6519 static int getInvisibleFromInvisibleActiveElement(int element)
6520 {
6521   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6522           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6523           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6524           element);
6525 }
6526
6527 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6528 {
6529   int x, y;
6530
6531   SCAN_PLAYFIELD(x, y)
6532   {
6533     int element = Tile[x][y];
6534
6535     if (element == EL_LIGHT_SWITCH &&
6536         game.light_time_left > 0)
6537     {
6538       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6539       TEST_DrawLevelField(x, y);
6540     }
6541     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6542              game.light_time_left == 0)
6543     {
6544       Tile[x][y] = EL_LIGHT_SWITCH;
6545       TEST_DrawLevelField(x, y);
6546     }
6547     else if (element == EL_EMC_DRIPPER &&
6548              game.light_time_left > 0)
6549     {
6550       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6551       TEST_DrawLevelField(x, y);
6552     }
6553     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6554              game.light_time_left == 0)
6555     {
6556       Tile[x][y] = EL_EMC_DRIPPER;
6557       TEST_DrawLevelField(x, y);
6558     }
6559     else if (element == EL_INVISIBLE_STEELWALL ||
6560              element == EL_INVISIBLE_WALL ||
6561              element == EL_INVISIBLE_SAND)
6562     {
6563       if (game.light_time_left > 0)
6564         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6565
6566       TEST_DrawLevelField(x, y);
6567
6568       // uncrumble neighbour fields, if needed
6569       if (element == EL_INVISIBLE_SAND)
6570         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6571     }
6572     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6573              element == EL_INVISIBLE_WALL_ACTIVE ||
6574              element == EL_INVISIBLE_SAND_ACTIVE)
6575     {
6576       if (game.light_time_left == 0)
6577         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6578
6579       TEST_DrawLevelField(x, y);
6580
6581       // re-crumble neighbour fields, if needed
6582       if (element == EL_INVISIBLE_SAND)
6583         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6584     }
6585   }
6586 }
6587
6588 static void RedrawAllInvisibleElementsForLenses(void)
6589 {
6590   int x, y;
6591
6592   SCAN_PLAYFIELD(x, y)
6593   {
6594     int element = Tile[x][y];
6595
6596     if (element == EL_EMC_DRIPPER &&
6597         game.lenses_time_left > 0)
6598     {
6599       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6600       TEST_DrawLevelField(x, y);
6601     }
6602     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6603              game.lenses_time_left == 0)
6604     {
6605       Tile[x][y] = EL_EMC_DRIPPER;
6606       TEST_DrawLevelField(x, y);
6607     }
6608     else if (element == EL_INVISIBLE_STEELWALL ||
6609              element == EL_INVISIBLE_WALL ||
6610              element == EL_INVISIBLE_SAND)
6611     {
6612       if (game.lenses_time_left > 0)
6613         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6614
6615       TEST_DrawLevelField(x, y);
6616
6617       // uncrumble neighbour fields, if needed
6618       if (element == EL_INVISIBLE_SAND)
6619         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6620     }
6621     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6622              element == EL_INVISIBLE_WALL_ACTIVE ||
6623              element == EL_INVISIBLE_SAND_ACTIVE)
6624     {
6625       if (game.lenses_time_left == 0)
6626         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6627
6628       TEST_DrawLevelField(x, y);
6629
6630       // re-crumble neighbour fields, if needed
6631       if (element == EL_INVISIBLE_SAND)
6632         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6633     }
6634   }
6635 }
6636
6637 static void RedrawAllInvisibleElementsForMagnifier(void)
6638 {
6639   int x, y;
6640
6641   SCAN_PLAYFIELD(x, y)
6642   {
6643     int element = Tile[x][y];
6644
6645     if (element == EL_EMC_FAKE_GRASS &&
6646         game.magnify_time_left > 0)
6647     {
6648       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6649       TEST_DrawLevelField(x, y);
6650     }
6651     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6652              game.magnify_time_left == 0)
6653     {
6654       Tile[x][y] = EL_EMC_FAKE_GRASS;
6655       TEST_DrawLevelField(x, y);
6656     }
6657     else if (IS_GATE_GRAY(element) &&
6658              game.magnify_time_left > 0)
6659     {
6660       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6661                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6662                     IS_EM_GATE_GRAY(element) ?
6663                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6664                     IS_EMC_GATE_GRAY(element) ?
6665                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6666                     IS_DC_GATE_GRAY(element) ?
6667                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6668                     element);
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (IS_GATE_GRAY_ACTIVE(element) &&
6672              game.magnify_time_left == 0)
6673     {
6674       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6675                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6676                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6677                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6678                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6679                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6680                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6681                     EL_DC_GATE_WHITE_GRAY :
6682                     element);
6683       TEST_DrawLevelField(x, y);
6684     }
6685   }
6686 }
6687
6688 static void ToggleLightSwitch(int x, int y)
6689 {
6690   int element = Tile[x][y];
6691
6692   game.light_time_left =
6693     (element == EL_LIGHT_SWITCH ?
6694      level.time_light * FRAMES_PER_SECOND : 0);
6695
6696   RedrawAllLightSwitchesAndInvisibleElements();
6697 }
6698
6699 static void ActivateTimegateSwitch(int x, int y)
6700 {
6701   int xx, yy;
6702
6703   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6704
6705   SCAN_PLAYFIELD(xx, yy)
6706   {
6707     int element = Tile[xx][yy];
6708
6709     if (element == EL_TIMEGATE_CLOSED ||
6710         element == EL_TIMEGATE_CLOSING)
6711     {
6712       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6713       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6714     }
6715
6716     /*
6717     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6718     {
6719       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6720       TEST_DrawLevelField(xx, yy);
6721     }
6722     */
6723
6724   }
6725
6726   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6727                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6728 }
6729
6730 static void Impact(int x, int y)
6731 {
6732   boolean last_line = (y == lev_fieldy - 1);
6733   boolean object_hit = FALSE;
6734   boolean impact = (last_line || object_hit);
6735   int element = Tile[x][y];
6736   int smashed = EL_STEELWALL;
6737
6738   if (!last_line)       // check if element below was hit
6739   {
6740     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6741       return;
6742
6743     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6744                                          MovDir[x][y + 1] != MV_DOWN ||
6745                                          MovPos[x][y + 1] <= TILEY / 2));
6746
6747     // do not smash moving elements that left the smashed field in time
6748     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6749         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6750       object_hit = FALSE;
6751
6752 #if USE_QUICKSAND_IMPACT_BUGFIX
6753     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6754     {
6755       RemoveMovingField(x, y + 1);
6756       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6757       Tile[x][y + 2] = EL_ROCK;
6758       TEST_DrawLevelField(x, y + 2);
6759
6760       object_hit = TRUE;
6761     }
6762
6763     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6764     {
6765       RemoveMovingField(x, y + 1);
6766       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6767       Tile[x][y + 2] = EL_ROCK;
6768       TEST_DrawLevelField(x, y + 2);
6769
6770       object_hit = TRUE;
6771     }
6772 #endif
6773
6774     if (object_hit)
6775       smashed = MovingOrBlocked2Element(x, y + 1);
6776
6777     impact = (last_line || object_hit);
6778   }
6779
6780   if (!last_line && smashed == EL_ACID) // element falls into acid
6781   {
6782     SplashAcid(x, y + 1);
6783     return;
6784   }
6785
6786   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6787   // only reset graphic animation if graphic really changes after impact
6788   if (impact &&
6789       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6790   {
6791     ResetGfxAnimation(x, y);
6792     TEST_DrawLevelField(x, y);
6793   }
6794
6795   if (impact && CAN_EXPLODE_IMPACT(element))
6796   {
6797     Bang(x, y);
6798     return;
6799   }
6800   else if (impact && element == EL_PEARL &&
6801            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6802   {
6803     ResetGfxAnimation(x, y);
6804
6805     Tile[x][y] = EL_PEARL_BREAKING;
6806     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6807     return;
6808   }
6809   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6810   {
6811     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6812
6813     return;
6814   }
6815
6816   if (impact && element == EL_AMOEBA_DROP)
6817   {
6818     if (object_hit && IS_PLAYER(x, y + 1))
6819       KillPlayerUnlessEnemyProtected(x, y + 1);
6820     else if (object_hit && smashed == EL_PENGUIN)
6821       Bang(x, y + 1);
6822     else
6823     {
6824       Tile[x][y] = EL_AMOEBA_GROWING;
6825       Store[x][y] = EL_AMOEBA_WET;
6826
6827       ResetRandomAnimationValue(x, y);
6828     }
6829     return;
6830   }
6831
6832   if (object_hit)               // check which object was hit
6833   {
6834     if ((CAN_PASS_MAGIC_WALL(element) && 
6835          (smashed == EL_MAGIC_WALL ||
6836           smashed == EL_BD_MAGIC_WALL)) ||
6837         (CAN_PASS_DC_MAGIC_WALL(element) &&
6838          smashed == EL_DC_MAGIC_WALL))
6839     {
6840       int xx, yy;
6841       int activated_magic_wall =
6842         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6843          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6844          EL_DC_MAGIC_WALL_ACTIVE);
6845
6846       // activate magic wall / mill
6847       SCAN_PLAYFIELD(xx, yy)
6848       {
6849         if (Tile[xx][yy] == smashed)
6850           Tile[xx][yy] = activated_magic_wall;
6851       }
6852
6853       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6854       game.magic_wall_active = TRUE;
6855
6856       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6857                             SND_MAGIC_WALL_ACTIVATING :
6858                             smashed == EL_BD_MAGIC_WALL ?
6859                             SND_BD_MAGIC_WALL_ACTIVATING :
6860                             SND_DC_MAGIC_WALL_ACTIVATING));
6861     }
6862
6863     if (IS_PLAYER(x, y + 1))
6864     {
6865       if (CAN_SMASH_PLAYER(element))
6866       {
6867         KillPlayerUnlessEnemyProtected(x, y + 1);
6868         return;
6869       }
6870     }
6871     else if (smashed == EL_PENGUIN)
6872     {
6873       if (CAN_SMASH_PLAYER(element))
6874       {
6875         Bang(x, y + 1);
6876         return;
6877       }
6878     }
6879     else if (element == EL_BD_DIAMOND)
6880     {
6881       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6882       {
6883         Bang(x, y + 1);
6884         return;
6885       }
6886     }
6887     else if (((element == EL_SP_INFOTRON ||
6888                element == EL_SP_ZONK) &&
6889               (smashed == EL_SP_SNIKSNAK ||
6890                smashed == EL_SP_ELECTRON ||
6891                smashed == EL_SP_DISK_ORANGE)) ||
6892              (element == EL_SP_INFOTRON &&
6893               smashed == EL_SP_DISK_YELLOW))
6894     {
6895       Bang(x, y + 1);
6896       return;
6897     }
6898     else if (CAN_SMASH_EVERYTHING(element))
6899     {
6900       if (IS_CLASSIC_ENEMY(smashed) ||
6901           CAN_EXPLODE_SMASHED(smashed))
6902       {
6903         Bang(x, y + 1);
6904         return;
6905       }
6906       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6907       {
6908         if (smashed == EL_LAMP ||
6909             smashed == EL_LAMP_ACTIVE)
6910         {
6911           Bang(x, y + 1);
6912           return;
6913         }
6914         else if (smashed == EL_NUT)
6915         {
6916           Tile[x][y + 1] = EL_NUT_BREAKING;
6917           PlayLevelSound(x, y, SND_NUT_BREAKING);
6918           RaiseScoreElement(EL_NUT);
6919           return;
6920         }
6921         else if (smashed == EL_PEARL)
6922         {
6923           ResetGfxAnimation(x, y);
6924
6925           Tile[x][y + 1] = EL_PEARL_BREAKING;
6926           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6927           return;
6928         }
6929         else if (smashed == EL_DIAMOND)
6930         {
6931           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6932           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6933           return;
6934         }
6935         else if (IS_BELT_SWITCH(smashed))
6936         {
6937           ToggleBeltSwitch(x, y + 1);
6938         }
6939         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6940                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6941                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6942                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6943         {
6944           ToggleSwitchgateSwitch();
6945         }
6946         else if (smashed == EL_LIGHT_SWITCH ||
6947                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6948         {
6949           ToggleLightSwitch(x, y + 1);
6950         }
6951         else
6952         {
6953           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6954
6955           CheckElementChangeBySide(x, y + 1, smashed, element,
6956                                    CE_SWITCHED, CH_SIDE_TOP);
6957           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6958                                             CH_SIDE_TOP);
6959         }
6960       }
6961       else
6962       {
6963         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6964       }
6965     }
6966   }
6967
6968   // play sound of magic wall / mill
6969   if (!last_line &&
6970       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6971        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6972        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6973   {
6974     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6975       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6976     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6977       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6978     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6979       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6980
6981     return;
6982   }
6983
6984   // play sound of object that hits the ground
6985   if (last_line || object_hit)
6986     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6987 }
6988
6989 static void TurnRoundExt(int x, int y)
6990 {
6991   static struct
6992   {
6993     int dx, dy;
6994   } move_xy[] =
6995   {
6996     {  0,  0 },
6997     { -1,  0 },
6998     { +1,  0 },
6999     {  0,  0 },
7000     {  0, -1 },
7001     {  0,  0 }, { 0, 0 }, { 0, 0 },
7002     {  0, +1 }
7003   };
7004   static struct
7005   {
7006     int left, right, back;
7007   } turn[] =
7008   {
7009     { 0,        0,              0        },
7010     { MV_DOWN,  MV_UP,          MV_RIGHT },
7011     { MV_UP,    MV_DOWN,        MV_LEFT  },
7012     { 0,        0,              0        },
7013     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7014     { 0,        0,              0        },
7015     { 0,        0,              0        },
7016     { 0,        0,              0        },
7017     { MV_RIGHT, MV_LEFT,        MV_UP    }
7018   };
7019
7020   int element = Tile[x][y];
7021   int move_pattern = element_info[element].move_pattern;
7022
7023   int old_move_dir = MovDir[x][y];
7024   int left_dir  = turn[old_move_dir].left;
7025   int right_dir = turn[old_move_dir].right;
7026   int back_dir  = turn[old_move_dir].back;
7027
7028   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7029   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7030   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7031   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7032
7033   int left_x  = x + left_dx,  left_y  = y + left_dy;
7034   int right_x = x + right_dx, right_y = y + right_dy;
7035   int move_x  = x + move_dx,  move_y  = y + move_dy;
7036
7037   int xx, yy;
7038
7039   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7040   {
7041     TestIfBadThingTouchesOtherBadThing(x, y);
7042
7043     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7044       MovDir[x][y] = right_dir;
7045     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7046       MovDir[x][y] = left_dir;
7047
7048     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7049       MovDelay[x][y] = 9;
7050     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7051       MovDelay[x][y] = 1;
7052   }
7053   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7054   {
7055     TestIfBadThingTouchesOtherBadThing(x, y);
7056
7057     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7058       MovDir[x][y] = left_dir;
7059     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7060       MovDir[x][y] = right_dir;
7061
7062     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7063       MovDelay[x][y] = 9;
7064     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7065       MovDelay[x][y] = 1;
7066   }
7067   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7068   {
7069     TestIfBadThingTouchesOtherBadThing(x, y);
7070
7071     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7072       MovDir[x][y] = left_dir;
7073     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7074       MovDir[x][y] = right_dir;
7075
7076     if (MovDir[x][y] != old_move_dir)
7077       MovDelay[x][y] = 9;
7078   }
7079   else if (element == EL_YAMYAM)
7080   {
7081     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7082     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7083
7084     if (can_turn_left && can_turn_right)
7085       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7086     else if (can_turn_left)
7087       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7088     else if (can_turn_right)
7089       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7090     else
7091       MovDir[x][y] = back_dir;
7092
7093     MovDelay[x][y] = 16 + 16 * RND(3);
7094   }
7095   else if (element == EL_DARK_YAMYAM)
7096   {
7097     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7098                                                          left_x, left_y);
7099     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7100                                                          right_x, right_y);
7101
7102     if (can_turn_left && can_turn_right)
7103       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7104     else if (can_turn_left)
7105       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7106     else if (can_turn_right)
7107       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7108     else
7109       MovDir[x][y] = back_dir;
7110
7111     MovDelay[x][y] = 16 + 16 * RND(3);
7112   }
7113   else if (element == EL_PACMAN)
7114   {
7115     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7116     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7117
7118     if (can_turn_left && can_turn_right)
7119       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7120     else if (can_turn_left)
7121       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7122     else if (can_turn_right)
7123       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7124     else
7125       MovDir[x][y] = back_dir;
7126
7127     MovDelay[x][y] = 6 + RND(40);
7128   }
7129   else if (element == EL_PIG)
7130   {
7131     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7132     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7133     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7134     boolean should_turn_left, should_turn_right, should_move_on;
7135     int rnd_value = 24;
7136     int rnd = RND(rnd_value);
7137
7138     should_turn_left = (can_turn_left &&
7139                         (!can_move_on ||
7140                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7141                                                    y + back_dy + left_dy)));
7142     should_turn_right = (can_turn_right &&
7143                          (!can_move_on ||
7144                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7145                                                     y + back_dy + right_dy)));
7146     should_move_on = (can_move_on &&
7147                       (!can_turn_left ||
7148                        !can_turn_right ||
7149                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7150                                                  y + move_dy + left_dy) ||
7151                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7152                                                  y + move_dy + right_dy)));
7153
7154     if (should_turn_left || should_turn_right || should_move_on)
7155     {
7156       if (should_turn_left && should_turn_right && should_move_on)
7157         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7158                         rnd < 2 * rnd_value / 3 ? right_dir :
7159                         old_move_dir);
7160       else if (should_turn_left && should_turn_right)
7161         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7162       else if (should_turn_left && should_move_on)
7163         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7164       else if (should_turn_right && should_move_on)
7165         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7166       else if (should_turn_left)
7167         MovDir[x][y] = left_dir;
7168       else if (should_turn_right)
7169         MovDir[x][y] = right_dir;
7170       else if (should_move_on)
7171         MovDir[x][y] = old_move_dir;
7172     }
7173     else if (can_move_on && rnd > rnd_value / 8)
7174       MovDir[x][y] = old_move_dir;
7175     else if (can_turn_left && can_turn_right)
7176       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7177     else if (can_turn_left && rnd > rnd_value / 8)
7178       MovDir[x][y] = left_dir;
7179     else if (can_turn_right && rnd > rnd_value/8)
7180       MovDir[x][y] = right_dir;
7181     else
7182       MovDir[x][y] = back_dir;
7183
7184     xx = x + move_xy[MovDir[x][y]].dx;
7185     yy = y + move_xy[MovDir[x][y]].dy;
7186
7187     if (!IN_LEV_FIELD(xx, yy) ||
7188         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7189       MovDir[x][y] = old_move_dir;
7190
7191     MovDelay[x][y] = 0;
7192   }
7193   else if (element == EL_DRAGON)
7194   {
7195     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7196     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7197     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7198     int rnd_value = 24;
7199     int rnd = RND(rnd_value);
7200
7201     if (can_move_on && rnd > rnd_value / 8)
7202       MovDir[x][y] = old_move_dir;
7203     else if (can_turn_left && can_turn_right)
7204       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7205     else if (can_turn_left && rnd > rnd_value / 8)
7206       MovDir[x][y] = left_dir;
7207     else if (can_turn_right && rnd > rnd_value / 8)
7208       MovDir[x][y] = right_dir;
7209     else
7210       MovDir[x][y] = back_dir;
7211
7212     xx = x + move_xy[MovDir[x][y]].dx;
7213     yy = y + move_xy[MovDir[x][y]].dy;
7214
7215     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7216       MovDir[x][y] = old_move_dir;
7217
7218     MovDelay[x][y] = 0;
7219   }
7220   else if (element == EL_MOLE)
7221   {
7222     boolean can_move_on =
7223       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7224                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7225                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7226     if (!can_move_on)
7227     {
7228       boolean can_turn_left =
7229         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7230                               IS_AMOEBOID(Tile[left_x][left_y])));
7231
7232       boolean can_turn_right =
7233         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7234                               IS_AMOEBOID(Tile[right_x][right_y])));
7235
7236       if (can_turn_left && can_turn_right)
7237         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7238       else if (can_turn_left)
7239         MovDir[x][y] = left_dir;
7240       else
7241         MovDir[x][y] = right_dir;
7242     }
7243
7244     if (MovDir[x][y] != old_move_dir)
7245       MovDelay[x][y] = 9;
7246   }
7247   else if (element == EL_BALLOON)
7248   {
7249     MovDir[x][y] = game.wind_direction;
7250     MovDelay[x][y] = 0;
7251   }
7252   else if (element == EL_SPRING)
7253   {
7254     if (MovDir[x][y] & MV_HORIZONTAL)
7255     {
7256       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7257           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7258       {
7259         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7260         ResetGfxAnimation(move_x, move_y);
7261         TEST_DrawLevelField(move_x, move_y);
7262
7263         MovDir[x][y] = back_dir;
7264       }
7265       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7266                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7267         MovDir[x][y] = MV_NONE;
7268     }
7269
7270     MovDelay[x][y] = 0;
7271   }
7272   else if (element == EL_ROBOT ||
7273            element == EL_SATELLITE ||
7274            element == EL_PENGUIN ||
7275            element == EL_EMC_ANDROID)
7276   {
7277     int attr_x = -1, attr_y = -1;
7278
7279     if (game.all_players_gone)
7280     {
7281       attr_x = game.exit_x;
7282       attr_y = game.exit_y;
7283     }
7284     else
7285     {
7286       int i;
7287
7288       for (i = 0; i < MAX_PLAYERS; i++)
7289       {
7290         struct PlayerInfo *player = &stored_player[i];
7291         int jx = player->jx, jy = player->jy;
7292
7293         if (!player->active)
7294           continue;
7295
7296         if (attr_x == -1 ||
7297             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7298         {
7299           attr_x = jx;
7300           attr_y = jy;
7301         }
7302       }
7303     }
7304
7305     if (element == EL_ROBOT &&
7306         game.robot_wheel_x >= 0 &&
7307         game.robot_wheel_y >= 0 &&
7308         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7309          game.engine_version < VERSION_IDENT(3,1,0,0)))
7310     {
7311       attr_x = game.robot_wheel_x;
7312       attr_y = game.robot_wheel_y;
7313     }
7314
7315     if (element == EL_PENGUIN)
7316     {
7317       int i;
7318       struct XY *xy = xy_topdown;
7319
7320       for (i = 0; i < NUM_DIRECTIONS; i++)
7321       {
7322         int ex = x + xy[i].x;
7323         int ey = y + xy[i].y;
7324
7325         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7326                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7327                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7328                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7329         {
7330           attr_x = ex;
7331           attr_y = ey;
7332           break;
7333         }
7334       }
7335     }
7336
7337     MovDir[x][y] = MV_NONE;
7338     if (attr_x < x)
7339       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7340     else if (attr_x > x)
7341       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7342     if (attr_y < y)
7343       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7344     else if (attr_y > y)
7345       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7346
7347     if (element == EL_ROBOT)
7348     {
7349       int newx, newy;
7350
7351       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7352         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7353       Moving2Blocked(x, y, &newx, &newy);
7354
7355       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7356         MovDelay[x][y] = 8 + 8 * !RND(3);
7357       else
7358         MovDelay[x][y] = 16;
7359     }
7360     else if (element == EL_PENGUIN)
7361     {
7362       int newx, newy;
7363
7364       MovDelay[x][y] = 1;
7365
7366       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7367       {
7368         boolean first_horiz = RND(2);
7369         int new_move_dir = MovDir[x][y];
7370
7371         MovDir[x][y] =
7372           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7373         Moving2Blocked(x, y, &newx, &newy);
7374
7375         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7376           return;
7377
7378         MovDir[x][y] =
7379           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7380         Moving2Blocked(x, y, &newx, &newy);
7381
7382         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7383           return;
7384
7385         MovDir[x][y] = old_move_dir;
7386         return;
7387       }
7388     }
7389     else if (element == EL_SATELLITE)
7390     {
7391       int newx, newy;
7392
7393       MovDelay[x][y] = 1;
7394
7395       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7396       {
7397         boolean first_horiz = RND(2);
7398         int new_move_dir = MovDir[x][y];
7399
7400         MovDir[x][y] =
7401           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7402         Moving2Blocked(x, y, &newx, &newy);
7403
7404         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7405           return;
7406
7407         MovDir[x][y] =
7408           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7409         Moving2Blocked(x, y, &newx, &newy);
7410
7411         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7412           return;
7413
7414         MovDir[x][y] = old_move_dir;
7415         return;
7416       }
7417     }
7418     else if (element == EL_EMC_ANDROID)
7419     {
7420       static int check_pos[16] =
7421       {
7422         -1,             //  0 => (invalid)
7423         7,              //  1 => MV_LEFT
7424         3,              //  2 => MV_RIGHT
7425         -1,             //  3 => (invalid)
7426         1,              //  4 =>            MV_UP
7427         0,              //  5 => MV_LEFT  | MV_UP
7428         2,              //  6 => MV_RIGHT | MV_UP
7429         -1,             //  7 => (invalid)
7430         5,              //  8 =>            MV_DOWN
7431         6,              //  9 => MV_LEFT  | MV_DOWN
7432         4,              // 10 => MV_RIGHT | MV_DOWN
7433         -1,             // 11 => (invalid)
7434         -1,             // 12 => (invalid)
7435         -1,             // 13 => (invalid)
7436         -1,             // 14 => (invalid)
7437         -1,             // 15 => (invalid)
7438       };
7439       static struct
7440       {
7441         int dx, dy;
7442         int dir;
7443       } check_xy[8] =
7444       {
7445         { -1, -1,       MV_LEFT  | MV_UP   },
7446         {  0, -1,                  MV_UP   },
7447         { +1, -1,       MV_RIGHT | MV_UP   },
7448         { +1,  0,       MV_RIGHT           },
7449         { +1, +1,       MV_RIGHT | MV_DOWN },
7450         {  0, +1,                  MV_DOWN },
7451         { -1, +1,       MV_LEFT  | MV_DOWN },
7452         { -1,  0,       MV_LEFT            },
7453       };
7454       int start_pos, check_order;
7455       boolean can_clone = FALSE;
7456       int i;
7457
7458       // check if there is any free field around current position
7459       for (i = 0; i < 8; i++)
7460       {
7461         int newx = x + check_xy[i].dx;
7462         int newy = y + check_xy[i].dy;
7463
7464         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7465         {
7466           can_clone = TRUE;
7467
7468           break;
7469         }
7470       }
7471
7472       if (can_clone)            // randomly find an element to clone
7473       {
7474         can_clone = FALSE;
7475
7476         start_pos = check_pos[RND(8)];
7477         check_order = (RND(2) ? -1 : +1);
7478
7479         for (i = 0; i < 8; i++)
7480         {
7481           int pos_raw = start_pos + i * check_order;
7482           int pos = (pos_raw + 8) % 8;
7483           int newx = x + check_xy[pos].dx;
7484           int newy = y + check_xy[pos].dy;
7485
7486           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7487           {
7488             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7489             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7490
7491             Store[x][y] = Tile[newx][newy];
7492
7493             can_clone = TRUE;
7494
7495             break;
7496           }
7497         }
7498       }
7499
7500       if (can_clone)            // randomly find a direction to move
7501       {
7502         can_clone = FALSE;
7503
7504         start_pos = check_pos[RND(8)];
7505         check_order = (RND(2) ? -1 : +1);
7506
7507         for (i = 0; i < 8; i++)
7508         {
7509           int pos_raw = start_pos + i * check_order;
7510           int pos = (pos_raw + 8) % 8;
7511           int newx = x + check_xy[pos].dx;
7512           int newy = y + check_xy[pos].dy;
7513           int new_move_dir = check_xy[pos].dir;
7514
7515           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7516           {
7517             MovDir[x][y] = new_move_dir;
7518             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7519
7520             can_clone = TRUE;
7521
7522             break;
7523           }
7524         }
7525       }
7526
7527       if (can_clone)            // cloning and moving successful
7528         return;
7529
7530       // cannot clone -- try to move towards player
7531
7532       start_pos = check_pos[MovDir[x][y] & 0x0f];
7533       check_order = (RND(2) ? -1 : +1);
7534
7535       for (i = 0; i < 3; i++)
7536       {
7537         // first check start_pos, then previous/next or (next/previous) pos
7538         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7539         int pos = (pos_raw + 8) % 8;
7540         int newx = x + check_xy[pos].dx;
7541         int newy = y + check_xy[pos].dy;
7542         int new_move_dir = check_xy[pos].dir;
7543
7544         if (IS_PLAYER(newx, newy))
7545           break;
7546
7547         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7548         {
7549           MovDir[x][y] = new_move_dir;
7550           MovDelay[x][y] = level.android_move_time * 8 + 1;
7551
7552           break;
7553         }
7554       }
7555     }
7556   }
7557   else if (move_pattern == MV_TURNING_LEFT ||
7558            move_pattern == MV_TURNING_RIGHT ||
7559            move_pattern == MV_TURNING_LEFT_RIGHT ||
7560            move_pattern == MV_TURNING_RIGHT_LEFT ||
7561            move_pattern == MV_TURNING_RANDOM ||
7562            move_pattern == MV_ALL_DIRECTIONS)
7563   {
7564     boolean can_turn_left =
7565       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7566     boolean can_turn_right =
7567       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7568
7569     if (element_info[element].move_stepsize == 0)       // "not moving"
7570       return;
7571
7572     if (move_pattern == MV_TURNING_LEFT)
7573       MovDir[x][y] = left_dir;
7574     else if (move_pattern == MV_TURNING_RIGHT)
7575       MovDir[x][y] = right_dir;
7576     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7577       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7578     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7579       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7580     else if (move_pattern == MV_TURNING_RANDOM)
7581       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7582                       can_turn_right && !can_turn_left ? right_dir :
7583                       RND(2) ? left_dir : right_dir);
7584     else if (can_turn_left && can_turn_right)
7585       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7586     else if (can_turn_left)
7587       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7588     else if (can_turn_right)
7589       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7590     else
7591       MovDir[x][y] = back_dir;
7592
7593     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595   else if (move_pattern == MV_HORIZONTAL ||
7596            move_pattern == MV_VERTICAL)
7597   {
7598     if (move_pattern & old_move_dir)
7599       MovDir[x][y] = back_dir;
7600     else if (move_pattern == MV_HORIZONTAL)
7601       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7602     else if (move_pattern == MV_VERTICAL)
7603       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7604
7605     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7606   }
7607   else if (move_pattern & MV_ANY_DIRECTION)
7608   {
7609     MovDir[x][y] = move_pattern;
7610     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7611   }
7612   else if (move_pattern & MV_WIND_DIRECTION)
7613   {
7614     MovDir[x][y] = game.wind_direction;
7615     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7616   }
7617   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7618   {
7619     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7620       MovDir[x][y] = left_dir;
7621     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7622       MovDir[x][y] = right_dir;
7623
7624     if (MovDir[x][y] != old_move_dir)
7625       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7626   }
7627   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7628   {
7629     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7630       MovDir[x][y] = right_dir;
7631     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7632       MovDir[x][y] = left_dir;
7633
7634     if (MovDir[x][y] != old_move_dir)
7635       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7636   }
7637   else if (move_pattern == MV_TOWARDS_PLAYER ||
7638            move_pattern == MV_AWAY_FROM_PLAYER)
7639   {
7640     int attr_x = -1, attr_y = -1;
7641     int newx, newy;
7642     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7643
7644     if (game.all_players_gone)
7645     {
7646       attr_x = game.exit_x;
7647       attr_y = game.exit_y;
7648     }
7649     else
7650     {
7651       int i;
7652
7653       for (i = 0; i < MAX_PLAYERS; i++)
7654       {
7655         struct PlayerInfo *player = &stored_player[i];
7656         int jx = player->jx, jy = player->jy;
7657
7658         if (!player->active)
7659           continue;
7660
7661         if (attr_x == -1 ||
7662             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7663         {
7664           attr_x = jx;
7665           attr_y = jy;
7666         }
7667       }
7668     }
7669
7670     MovDir[x][y] = MV_NONE;
7671     if (attr_x < x)
7672       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7673     else if (attr_x > x)
7674       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7675     if (attr_y < y)
7676       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7677     else if (attr_y > y)
7678       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7679
7680     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7681
7682     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7683     {
7684       boolean first_horiz = RND(2);
7685       int new_move_dir = MovDir[x][y];
7686
7687       if (element_info[element].move_stepsize == 0)     // "not moving"
7688       {
7689         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7690         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7691
7692         return;
7693       }
7694
7695       MovDir[x][y] =
7696         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7697       Moving2Blocked(x, y, &newx, &newy);
7698
7699       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7700         return;
7701
7702       MovDir[x][y] =
7703         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7704       Moving2Blocked(x, y, &newx, &newy);
7705
7706       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7707         return;
7708
7709       MovDir[x][y] = old_move_dir;
7710     }
7711   }
7712   else if (move_pattern == MV_WHEN_PUSHED ||
7713            move_pattern == MV_WHEN_DROPPED)
7714   {
7715     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7716       MovDir[x][y] = MV_NONE;
7717
7718     MovDelay[x][y] = 0;
7719   }
7720   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7721   {
7722     struct XY *test_xy = xy_topdown;
7723     static int test_dir[4] =
7724     {
7725       MV_UP,
7726       MV_LEFT,
7727       MV_RIGHT,
7728       MV_DOWN
7729     };
7730     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7731     int move_preference = -1000000;     // start with very low preference
7732     int new_move_dir = MV_NONE;
7733     int start_test = RND(4);
7734     int i;
7735
7736     for (i = 0; i < NUM_DIRECTIONS; i++)
7737     {
7738       int j = (start_test + i) % 4;
7739       int move_dir = test_dir[j];
7740       int move_dir_preference;
7741
7742       xx = x + test_xy[j].x;
7743       yy = y + test_xy[j].y;
7744
7745       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7746           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7747       {
7748         new_move_dir = move_dir;
7749
7750         break;
7751       }
7752
7753       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7754         continue;
7755
7756       move_dir_preference = -1 * RunnerVisit[xx][yy];
7757       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7758         move_dir_preference = PlayerVisit[xx][yy];
7759
7760       if (move_dir_preference > move_preference)
7761       {
7762         // prefer field that has not been visited for the longest time
7763         move_preference = move_dir_preference;
7764         new_move_dir = move_dir;
7765       }
7766       else if (move_dir_preference == move_preference &&
7767                move_dir == old_move_dir)
7768       {
7769         // prefer last direction when all directions are preferred equally
7770         move_preference = move_dir_preference;
7771         new_move_dir = move_dir;
7772       }
7773     }
7774
7775     MovDir[x][y] = new_move_dir;
7776     if (old_move_dir != new_move_dir)
7777       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7778   }
7779 }
7780
7781 static void TurnRound(int x, int y)
7782 {
7783   int direction = MovDir[x][y];
7784
7785   TurnRoundExt(x, y);
7786
7787   GfxDir[x][y] = MovDir[x][y];
7788
7789   if (direction != MovDir[x][y])
7790     GfxFrame[x][y] = 0;
7791
7792   if (MovDelay[x][y])
7793     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7794
7795   ResetGfxFrame(x, y);
7796 }
7797
7798 static boolean JustBeingPushed(int x, int y)
7799 {
7800   int i;
7801
7802   for (i = 0; i < MAX_PLAYERS; i++)
7803   {
7804     struct PlayerInfo *player = &stored_player[i];
7805
7806     if (player->active && player->is_pushing && player->MovPos)
7807     {
7808       int next_jx = player->jx + (player->jx - player->last_jx);
7809       int next_jy = player->jy + (player->jy - player->last_jy);
7810
7811       if (x == next_jx && y == next_jy)
7812         return TRUE;
7813     }
7814   }
7815
7816   return FALSE;
7817 }
7818
7819 static void StartMoving(int x, int y)
7820 {
7821   boolean started_moving = FALSE;       // some elements can fall _and_ move
7822   int element = Tile[x][y];
7823
7824   if (Stop[x][y])
7825     return;
7826
7827   if (MovDelay[x][y] == 0)
7828     GfxAction[x][y] = ACTION_DEFAULT;
7829
7830   if (CAN_FALL(element) && y < lev_fieldy - 1)
7831   {
7832     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7833         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7834       if (JustBeingPushed(x, y))
7835         return;
7836
7837     if (element == EL_QUICKSAND_FULL)
7838     {
7839       if (IS_FREE(x, y + 1))
7840       {
7841         InitMovingField(x, y, MV_DOWN);
7842         started_moving = TRUE;
7843
7844         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7845 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7846         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7847           Store[x][y] = EL_ROCK;
7848 #else
7849         Store[x][y] = EL_ROCK;
7850 #endif
7851
7852         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7853       }
7854       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7855       {
7856         if (!MovDelay[x][y])
7857         {
7858           MovDelay[x][y] = TILEY + 1;
7859
7860           ResetGfxAnimation(x, y);
7861           ResetGfxAnimation(x, y + 1);
7862         }
7863
7864         if (MovDelay[x][y])
7865         {
7866           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7867           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7868
7869           MovDelay[x][y]--;
7870           if (MovDelay[x][y])
7871             return;
7872         }
7873
7874         Tile[x][y] = EL_QUICKSAND_EMPTY;
7875         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7876         Store[x][y + 1] = Store[x][y];
7877         Store[x][y] = 0;
7878
7879         PlayLevelSoundAction(x, y, ACTION_FILLING);
7880       }
7881       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7882       {
7883         if (!MovDelay[x][y])
7884         {
7885           MovDelay[x][y] = TILEY + 1;
7886
7887           ResetGfxAnimation(x, y);
7888           ResetGfxAnimation(x, y + 1);
7889         }
7890
7891         if (MovDelay[x][y])
7892         {
7893           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7894           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7895
7896           MovDelay[x][y]--;
7897           if (MovDelay[x][y])
7898             return;
7899         }
7900
7901         Tile[x][y] = EL_QUICKSAND_EMPTY;
7902         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7903         Store[x][y + 1] = Store[x][y];
7904         Store[x][y] = 0;
7905
7906         PlayLevelSoundAction(x, y, ACTION_FILLING);
7907       }
7908     }
7909     else if (element == EL_QUICKSAND_FAST_FULL)
7910     {
7911       if (IS_FREE(x, y + 1))
7912       {
7913         InitMovingField(x, y, MV_DOWN);
7914         started_moving = TRUE;
7915
7916         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7917 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7918         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7919           Store[x][y] = EL_ROCK;
7920 #else
7921         Store[x][y] = EL_ROCK;
7922 #endif
7923
7924         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7925       }
7926       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7927       {
7928         if (!MovDelay[x][y])
7929         {
7930           MovDelay[x][y] = TILEY + 1;
7931
7932           ResetGfxAnimation(x, y);
7933           ResetGfxAnimation(x, y + 1);
7934         }
7935
7936         if (MovDelay[x][y])
7937         {
7938           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7939           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7940
7941           MovDelay[x][y]--;
7942           if (MovDelay[x][y])
7943             return;
7944         }
7945
7946         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7947         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7948         Store[x][y + 1] = Store[x][y];
7949         Store[x][y] = 0;
7950
7951         PlayLevelSoundAction(x, y, ACTION_FILLING);
7952       }
7953       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7954       {
7955         if (!MovDelay[x][y])
7956         {
7957           MovDelay[x][y] = TILEY + 1;
7958
7959           ResetGfxAnimation(x, y);
7960           ResetGfxAnimation(x, y + 1);
7961         }
7962
7963         if (MovDelay[x][y])
7964         {
7965           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7966           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7967
7968           MovDelay[x][y]--;
7969           if (MovDelay[x][y])
7970             return;
7971         }
7972
7973         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7974         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7975         Store[x][y + 1] = Store[x][y];
7976         Store[x][y] = 0;
7977
7978         PlayLevelSoundAction(x, y, ACTION_FILLING);
7979       }
7980     }
7981     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7982              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7983     {
7984       InitMovingField(x, y, MV_DOWN);
7985       started_moving = TRUE;
7986
7987       Tile[x][y] = EL_QUICKSAND_FILLING;
7988       Store[x][y] = element;
7989
7990       PlayLevelSoundAction(x, y, ACTION_FILLING);
7991     }
7992     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7993              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7994     {
7995       InitMovingField(x, y, MV_DOWN);
7996       started_moving = TRUE;
7997
7998       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7999       Store[x][y] = element;
8000
8001       PlayLevelSoundAction(x, y, ACTION_FILLING);
8002     }
8003     else if (element == EL_MAGIC_WALL_FULL)
8004     {
8005       if (IS_FREE(x, y + 1))
8006       {
8007         InitMovingField(x, y, MV_DOWN);
8008         started_moving = TRUE;
8009
8010         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8011         Store[x][y] = EL_CHANGED(Store[x][y]);
8012       }
8013       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8014       {
8015         if (!MovDelay[x][y])
8016           MovDelay[x][y] = TILEY / 4 + 1;
8017
8018         if (MovDelay[x][y])
8019         {
8020           MovDelay[x][y]--;
8021           if (MovDelay[x][y])
8022             return;
8023         }
8024
8025         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8026         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8027         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8028         Store[x][y] = 0;
8029       }
8030     }
8031     else if (element == EL_BD_MAGIC_WALL_FULL)
8032     {
8033       if (IS_FREE(x, y + 1))
8034       {
8035         InitMovingField(x, y, MV_DOWN);
8036         started_moving = TRUE;
8037
8038         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8039         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8040       }
8041       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8042       {
8043         if (!MovDelay[x][y])
8044           MovDelay[x][y] = TILEY / 4 + 1;
8045
8046         if (MovDelay[x][y])
8047         {
8048           MovDelay[x][y]--;
8049           if (MovDelay[x][y])
8050             return;
8051         }
8052
8053         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8054         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8055         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8056         Store[x][y] = 0;
8057       }
8058     }
8059     else if (element == EL_DC_MAGIC_WALL_FULL)
8060     {
8061       if (IS_FREE(x, y + 1))
8062       {
8063         InitMovingField(x, y, MV_DOWN);
8064         started_moving = TRUE;
8065
8066         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8067         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8068       }
8069       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8070       {
8071         if (!MovDelay[x][y])
8072           MovDelay[x][y] = TILEY / 4 + 1;
8073
8074         if (MovDelay[x][y])
8075         {
8076           MovDelay[x][y]--;
8077           if (MovDelay[x][y])
8078             return;
8079         }
8080
8081         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8082         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8083         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8084         Store[x][y] = 0;
8085       }
8086     }
8087     else if ((CAN_PASS_MAGIC_WALL(element) &&
8088               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8089                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8090              (CAN_PASS_DC_MAGIC_WALL(element) &&
8091               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8092
8093     {
8094       InitMovingField(x, y, MV_DOWN);
8095       started_moving = TRUE;
8096
8097       Tile[x][y] =
8098         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8099          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8100          EL_DC_MAGIC_WALL_FILLING);
8101       Store[x][y] = element;
8102     }
8103     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8104     {
8105       SplashAcid(x, y + 1);
8106
8107       InitMovingField(x, y, MV_DOWN);
8108       started_moving = TRUE;
8109
8110       Store[x][y] = EL_ACID;
8111     }
8112     else if (
8113              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8114               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8115              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8116               CAN_FALL(element) && WasJustFalling[x][y] &&
8117               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8118
8119              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8120               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8121               (Tile[x][y + 1] == EL_BLOCKED)))
8122     {
8123       /* this is needed for a special case not covered by calling "Impact()"
8124          from "ContinueMoving()": if an element moves to a tile directly below
8125          another element which was just falling on that tile (which was empty
8126          in the previous frame), the falling element above would just stop
8127          instead of smashing the element below (in previous version, the above
8128          element was just checked for "moving" instead of "falling", resulting
8129          in incorrect smashes caused by horizontal movement of the above
8130          element; also, the case of the player being the element to smash was
8131          simply not covered here... :-/ ) */
8132
8133       CheckCollision[x][y] = 0;
8134       CheckImpact[x][y] = 0;
8135
8136       Impact(x, y);
8137     }
8138     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8139     {
8140       if (MovDir[x][y] == MV_NONE)
8141       {
8142         InitMovingField(x, y, MV_DOWN);
8143         started_moving = TRUE;
8144       }
8145     }
8146     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8147     {
8148       if (WasJustFalling[x][y]) // prevent animation from being restarted
8149         MovDir[x][y] = MV_DOWN;
8150
8151       InitMovingField(x, y, MV_DOWN);
8152       started_moving = TRUE;
8153     }
8154     else if (element == EL_AMOEBA_DROP)
8155     {
8156       Tile[x][y] = EL_AMOEBA_GROWING;
8157       Store[x][y] = EL_AMOEBA_WET;
8158     }
8159     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8160               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8161              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8162              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8163     {
8164       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8165                                 (IS_FREE(x - 1, y + 1) ||
8166                                  Tile[x - 1][y + 1] == EL_ACID));
8167       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8168                                 (IS_FREE(x + 1, y + 1) ||
8169                                  Tile[x + 1][y + 1] == EL_ACID));
8170       boolean can_fall_any  = (can_fall_left || can_fall_right);
8171       boolean can_fall_both = (can_fall_left && can_fall_right);
8172       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8173
8174       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8175       {
8176         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8177           can_fall_right = FALSE;
8178         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8179           can_fall_left = FALSE;
8180         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8181           can_fall_right = FALSE;
8182         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8183           can_fall_left = FALSE;
8184
8185         can_fall_any  = (can_fall_left || can_fall_right);
8186         can_fall_both = FALSE;
8187       }
8188
8189       if (can_fall_both)
8190       {
8191         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8192           can_fall_right = FALSE;       // slip down on left side
8193         else
8194           can_fall_left = !(can_fall_right = RND(2));
8195
8196         can_fall_both = FALSE;
8197       }
8198
8199       if (can_fall_any)
8200       {
8201         // if not determined otherwise, prefer left side for slipping down
8202         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8203         started_moving = TRUE;
8204       }
8205     }
8206     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8207     {
8208       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8209       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8210       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8211       int belt_dir = game.belt_dir[belt_nr];
8212
8213       if ((belt_dir == MV_LEFT  && left_is_free) ||
8214           (belt_dir == MV_RIGHT && right_is_free))
8215       {
8216         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8217
8218         InitMovingField(x, y, belt_dir);
8219         started_moving = TRUE;
8220
8221         Pushed[x][y] = TRUE;
8222         Pushed[nextx][y] = TRUE;
8223
8224         GfxAction[x][y] = ACTION_DEFAULT;
8225       }
8226       else
8227       {
8228         MovDir[x][y] = 0;       // if element was moving, stop it
8229       }
8230     }
8231   }
8232
8233   // not "else if" because of elements that can fall and move (EL_SPRING)
8234   if (CAN_MOVE(element) && !started_moving)
8235   {
8236     int move_pattern = element_info[element].move_pattern;
8237     int newx, newy;
8238
8239     Moving2Blocked(x, y, &newx, &newy);
8240
8241     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8242       return;
8243
8244     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8245         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8246     {
8247       WasJustMoving[x][y] = 0;
8248       CheckCollision[x][y] = 0;
8249
8250       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8251
8252       if (Tile[x][y] != element)        // element has changed
8253         return;
8254     }
8255
8256     if (!MovDelay[x][y])        // start new movement phase
8257     {
8258       // all objects that can change their move direction after each step
8259       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8260
8261       if (element != EL_YAMYAM &&
8262           element != EL_DARK_YAMYAM &&
8263           element != EL_PACMAN &&
8264           !(move_pattern & MV_ANY_DIRECTION) &&
8265           move_pattern != MV_TURNING_LEFT &&
8266           move_pattern != MV_TURNING_RIGHT &&
8267           move_pattern != MV_TURNING_LEFT_RIGHT &&
8268           move_pattern != MV_TURNING_RIGHT_LEFT &&
8269           move_pattern != MV_TURNING_RANDOM)
8270       {
8271         TurnRound(x, y);
8272
8273         if (MovDelay[x][y] && (element == EL_BUG ||
8274                                element == EL_SPACESHIP ||
8275                                element == EL_SP_SNIKSNAK ||
8276                                element == EL_SP_ELECTRON ||
8277                                element == EL_MOLE))
8278           TEST_DrawLevelField(x, y);
8279       }
8280     }
8281
8282     if (MovDelay[x][y])         // wait some time before next movement
8283     {
8284       MovDelay[x][y]--;
8285
8286       if (element == EL_ROBOT ||
8287           element == EL_YAMYAM ||
8288           element == EL_DARK_YAMYAM)
8289       {
8290         DrawLevelElementAnimationIfNeeded(x, y, element);
8291         PlayLevelSoundAction(x, y, ACTION_WAITING);
8292       }
8293       else if (element == EL_SP_ELECTRON)
8294         DrawLevelElementAnimationIfNeeded(x, y, element);
8295       else if (element == EL_DRAGON)
8296       {
8297         int i;
8298         int dir = MovDir[x][y];
8299         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8300         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8301         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8302                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8303                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8304                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8305         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8306
8307         GfxAction[x][y] = ACTION_ATTACKING;
8308
8309         if (IS_PLAYER(x, y))
8310           DrawPlayerField(x, y);
8311         else
8312           TEST_DrawLevelField(x, y);
8313
8314         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8315
8316         for (i = 1; i <= 3; i++)
8317         {
8318           int xx = x + i * dx;
8319           int yy = y + i * dy;
8320           int sx = SCREENX(xx);
8321           int sy = SCREENY(yy);
8322           int flame_graphic = graphic + (i - 1);
8323
8324           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8325             break;
8326
8327           if (MovDelay[x][y])
8328           {
8329             int flamed = MovingOrBlocked2Element(xx, yy);
8330
8331             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8332               Bang(xx, yy);
8333             else
8334               RemoveMovingField(xx, yy);
8335
8336             ChangeDelay[xx][yy] = 0;
8337
8338             Tile[xx][yy] = EL_FLAMES;
8339
8340             if (IN_SCR_FIELD(sx, sy))
8341             {
8342               TEST_DrawLevelFieldCrumbled(xx, yy);
8343               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8344             }
8345           }
8346           else
8347           {
8348             if (Tile[xx][yy] == EL_FLAMES)
8349               Tile[xx][yy] = EL_EMPTY;
8350             TEST_DrawLevelField(xx, yy);
8351           }
8352         }
8353       }
8354
8355       if (MovDelay[x][y])       // element still has to wait some time
8356       {
8357         PlayLevelSoundAction(x, y, ACTION_WAITING);
8358
8359         return;
8360       }
8361     }
8362
8363     // now make next step
8364
8365     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8366
8367     if (DONT_COLLIDE_WITH(element) &&
8368         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8369         !PLAYER_ENEMY_PROTECTED(newx, newy))
8370     {
8371       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8372
8373       return;
8374     }
8375
8376     else if (CAN_MOVE_INTO_ACID(element) &&
8377              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8378              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8379              (MovDir[x][y] == MV_DOWN ||
8380               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8381     {
8382       SplashAcid(newx, newy);
8383       Store[x][y] = EL_ACID;
8384     }
8385     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8386     {
8387       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8388           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8389           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8390           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8391       {
8392         RemoveField(x, y);
8393         TEST_DrawLevelField(x, y);
8394
8395         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8396         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8397           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8398
8399         game.friends_still_needed--;
8400         if (!game.friends_still_needed &&
8401             !game.GameOver &&
8402             game.all_players_gone)
8403           LevelSolved();
8404
8405         return;
8406       }
8407       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8408       {
8409         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8410           TEST_DrawLevelField(newx, newy);
8411         else
8412           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8413       }
8414       else if (!IS_FREE(newx, newy))
8415       {
8416         GfxAction[x][y] = ACTION_WAITING;
8417
8418         if (IS_PLAYER(x, y))
8419           DrawPlayerField(x, y);
8420         else
8421           TEST_DrawLevelField(x, y);
8422
8423         return;
8424       }
8425     }
8426     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8427     {
8428       if (IS_FOOD_PIG(Tile[newx][newy]))
8429       {
8430         if (IS_MOVING(newx, newy))
8431           RemoveMovingField(newx, newy);
8432         else
8433         {
8434           Tile[newx][newy] = EL_EMPTY;
8435           TEST_DrawLevelField(newx, newy);
8436         }
8437
8438         PlayLevelSound(x, y, SND_PIG_DIGGING);
8439       }
8440       else if (!IS_FREE(newx, newy))
8441       {
8442         if (IS_PLAYER(x, y))
8443           DrawPlayerField(x, y);
8444         else
8445           TEST_DrawLevelField(x, y);
8446
8447         return;
8448       }
8449     }
8450     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8451     {
8452       if (Store[x][y] != EL_EMPTY)
8453       {
8454         boolean can_clone = FALSE;
8455         int xx, yy;
8456
8457         // check if element to clone is still there
8458         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8459         {
8460           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8461           {
8462             can_clone = TRUE;
8463
8464             break;
8465           }
8466         }
8467
8468         // cannot clone or target field not free anymore -- do not clone
8469         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8470           Store[x][y] = EL_EMPTY;
8471       }
8472
8473       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8474       {
8475         if (IS_MV_DIAGONAL(MovDir[x][y]))
8476         {
8477           int diagonal_move_dir = MovDir[x][y];
8478           int stored = Store[x][y];
8479           int change_delay = 8;
8480           int graphic;
8481
8482           // android is moving diagonally
8483
8484           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8485
8486           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8487           GfxElement[x][y] = EL_EMC_ANDROID;
8488           GfxAction[x][y] = ACTION_SHRINKING;
8489           GfxDir[x][y] = diagonal_move_dir;
8490           ChangeDelay[x][y] = change_delay;
8491
8492           if (Store[x][y] == EL_EMPTY)
8493             Store[x][y] = GfxElementEmpty[x][y];
8494
8495           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8496                                    GfxDir[x][y]);
8497
8498           DrawLevelGraphicAnimation(x, y, graphic);
8499           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8500
8501           if (Tile[newx][newy] == EL_ACID)
8502           {
8503             SplashAcid(newx, newy);
8504
8505             return;
8506           }
8507
8508           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8509
8510           Store[newx][newy] = EL_EMC_ANDROID;
8511           GfxElement[newx][newy] = EL_EMC_ANDROID;
8512           GfxAction[newx][newy] = ACTION_GROWING;
8513           GfxDir[newx][newy] = diagonal_move_dir;
8514           ChangeDelay[newx][newy] = change_delay;
8515
8516           graphic = el_act_dir2img(GfxElement[newx][newy],
8517                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8518
8519           DrawLevelGraphicAnimation(newx, newy, graphic);
8520           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8521
8522           return;
8523         }
8524         else
8525         {
8526           Tile[newx][newy] = EL_EMPTY;
8527           TEST_DrawLevelField(newx, newy);
8528
8529           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8530         }
8531       }
8532       else if (!IS_FREE(newx, newy))
8533       {
8534         return;
8535       }
8536     }
8537     else if (IS_CUSTOM_ELEMENT(element) &&
8538              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8539     {
8540       if (!DigFieldByCE(newx, newy, element))
8541         return;
8542
8543       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8544       {
8545         RunnerVisit[x][y] = FrameCounter;
8546         PlayerVisit[x][y] /= 8;         // expire player visit path
8547       }
8548     }
8549     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8550     {
8551       if (!IS_FREE(newx, newy))
8552       {
8553         if (IS_PLAYER(x, y))
8554           DrawPlayerField(x, y);
8555         else
8556           TEST_DrawLevelField(x, y);
8557
8558         return;
8559       }
8560       else
8561       {
8562         boolean wanna_flame = !RND(10);
8563         int dx = newx - x, dy = newy - y;
8564         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8565         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8566         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8567                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8568         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8569                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8570
8571         if ((wanna_flame ||
8572              IS_CLASSIC_ENEMY(element1) ||
8573              IS_CLASSIC_ENEMY(element2)) &&
8574             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8575             element1 != EL_FLAMES && element2 != EL_FLAMES)
8576         {
8577           ResetGfxAnimation(x, y);
8578           GfxAction[x][y] = ACTION_ATTACKING;
8579
8580           if (IS_PLAYER(x, y))
8581             DrawPlayerField(x, y);
8582           else
8583             TEST_DrawLevelField(x, y);
8584
8585           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8586
8587           MovDelay[x][y] = 50;
8588
8589           Tile[newx][newy] = EL_FLAMES;
8590           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8591             Tile[newx1][newy1] = EL_FLAMES;
8592           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8593             Tile[newx2][newy2] = EL_FLAMES;
8594
8595           return;
8596         }
8597       }
8598     }
8599     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8600              Tile[newx][newy] == EL_DIAMOND)
8601     {
8602       if (IS_MOVING(newx, newy))
8603         RemoveMovingField(newx, newy);
8604       else
8605       {
8606         Tile[newx][newy] = EL_EMPTY;
8607         TEST_DrawLevelField(newx, newy);
8608       }
8609
8610       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8611     }
8612     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8613              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8614     {
8615       if (AmoebaNr[newx][newy])
8616       {
8617         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8618         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8619             Tile[newx][newy] == EL_BD_AMOEBA)
8620           AmoebaCnt[AmoebaNr[newx][newy]]--;
8621       }
8622
8623       if (IS_MOVING(newx, newy))
8624       {
8625         RemoveMovingField(newx, newy);
8626       }
8627       else
8628       {
8629         Tile[newx][newy] = EL_EMPTY;
8630         TEST_DrawLevelField(newx, newy);
8631       }
8632
8633       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8634     }
8635     else if ((element == EL_PACMAN || element == EL_MOLE)
8636              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8637     {
8638       if (AmoebaNr[newx][newy])
8639       {
8640         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8641         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8642             Tile[newx][newy] == EL_BD_AMOEBA)
8643           AmoebaCnt[AmoebaNr[newx][newy]]--;
8644       }
8645
8646       if (element == EL_MOLE)
8647       {
8648         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8649         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8650
8651         ResetGfxAnimation(x, y);
8652         GfxAction[x][y] = ACTION_DIGGING;
8653         TEST_DrawLevelField(x, y);
8654
8655         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8656
8657         return;                         // wait for shrinking amoeba
8658       }
8659       else      // element == EL_PACMAN
8660       {
8661         Tile[newx][newy] = EL_EMPTY;
8662         TEST_DrawLevelField(newx, newy);
8663         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8664       }
8665     }
8666     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8667              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8668               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8669     {
8670       // wait for shrinking amoeba to completely disappear
8671       return;
8672     }
8673     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8674     {
8675       // object was running against a wall
8676
8677       TurnRound(x, y);
8678
8679       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8680         DrawLevelElementAnimation(x, y, element);
8681
8682       if (DONT_TOUCH(element))
8683         TestIfBadThingTouchesPlayer(x, y);
8684
8685       return;
8686     }
8687
8688     InitMovingField(x, y, MovDir[x][y]);
8689
8690     PlayLevelSoundAction(x, y, ACTION_MOVING);
8691   }
8692
8693   if (MovDir[x][y])
8694     ContinueMoving(x, y);
8695 }
8696
8697 void ContinueMoving(int x, int y)
8698 {
8699   int element = Tile[x][y];
8700   struct ElementInfo *ei = &element_info[element];
8701   int direction = MovDir[x][y];
8702   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8703   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8704   int newx = x + dx, newy = y + dy;
8705   int stored = Store[x][y];
8706   int stored_new = Store[newx][newy];
8707   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8708   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8709   boolean last_line = (newy == lev_fieldy - 1);
8710   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8711
8712   if (pushed_by_player)         // special case: moving object pushed by player
8713   {
8714     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8715   }
8716   else if (use_step_delay)      // special case: moving object has step delay
8717   {
8718     if (!MovDelay[x][y])
8719       MovPos[x][y] += getElementMoveStepsize(x, y);
8720
8721     if (MovDelay[x][y])
8722       MovDelay[x][y]--;
8723     else
8724       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8725
8726     if (MovDelay[x][y])
8727     {
8728       TEST_DrawLevelField(x, y);
8729
8730       return;   // element is still waiting
8731     }
8732   }
8733   else                          // normal case: generically moving object
8734   {
8735     MovPos[x][y] += getElementMoveStepsize(x, y);
8736   }
8737
8738   if (ABS(MovPos[x][y]) < TILEX)
8739   {
8740     TEST_DrawLevelField(x, y);
8741
8742     return;     // element is still moving
8743   }
8744
8745   // element reached destination field
8746
8747   Tile[x][y] = EL_EMPTY;
8748   Tile[newx][newy] = element;
8749   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8750
8751   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8752   {
8753     element = Tile[newx][newy] = EL_ACID;
8754   }
8755   else if (element == EL_MOLE)
8756   {
8757     Tile[x][y] = EL_SAND;
8758
8759     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8760   }
8761   else if (element == EL_QUICKSAND_FILLING)
8762   {
8763     element = Tile[newx][newy] = get_next_element(element);
8764     Store[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_QUICKSAND_EMPTYING)
8767   {
8768     Tile[x][y] = get_next_element(element);
8769     element = Tile[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_QUICKSAND_FAST_FILLING)
8772   {
8773     element = Tile[newx][newy] = get_next_element(element);
8774     Store[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8777   {
8778     Tile[x][y] = get_next_element(element);
8779     element = Tile[newx][newy] = Store[x][y];
8780   }
8781   else if (element == EL_MAGIC_WALL_FILLING)
8782   {
8783     element = Tile[newx][newy] = get_next_element(element);
8784     if (!game.magic_wall_active)
8785       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8786     Store[newx][newy] = Store[x][y];
8787   }
8788   else if (element == EL_MAGIC_WALL_EMPTYING)
8789   {
8790     Tile[x][y] = get_next_element(element);
8791     if (!game.magic_wall_active)
8792       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8793     element = Tile[newx][newy] = Store[x][y];
8794
8795     InitField(newx, newy, FALSE);
8796   }
8797   else if (element == EL_BD_MAGIC_WALL_FILLING)
8798   {
8799     element = Tile[newx][newy] = get_next_element(element);
8800     if (!game.magic_wall_active)
8801       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8802     Store[newx][newy] = Store[x][y];
8803   }
8804   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8805   {
8806     Tile[x][y] = get_next_element(element);
8807     if (!game.magic_wall_active)
8808       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8809     element = Tile[newx][newy] = Store[x][y];
8810
8811     InitField(newx, newy, FALSE);
8812   }
8813   else if (element == EL_DC_MAGIC_WALL_FILLING)
8814   {
8815     element = Tile[newx][newy] = get_next_element(element);
8816     if (!game.magic_wall_active)
8817       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8818     Store[newx][newy] = Store[x][y];
8819   }
8820   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8821   {
8822     Tile[x][y] = get_next_element(element);
8823     if (!game.magic_wall_active)
8824       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8825     element = Tile[newx][newy] = Store[x][y];
8826
8827     InitField(newx, newy, FALSE);
8828   }
8829   else if (element == EL_AMOEBA_DROPPING)
8830   {
8831     Tile[x][y] = get_next_element(element);
8832     element = Tile[newx][newy] = Store[x][y];
8833   }
8834   else if (element == EL_SOKOBAN_OBJECT)
8835   {
8836     if (Back[x][y])
8837       Tile[x][y] = Back[x][y];
8838
8839     if (Back[newx][newy])
8840       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8841
8842     Back[x][y] = Back[newx][newy] = 0;
8843   }
8844
8845   Store[x][y] = EL_EMPTY;
8846   MovPos[x][y] = 0;
8847   MovDir[x][y] = 0;
8848   MovDelay[x][y] = 0;
8849
8850   MovDelay[newx][newy] = 0;
8851
8852   if (CAN_CHANGE_OR_HAS_ACTION(element))
8853   {
8854     // copy element change control values to new field
8855     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8856     ChangePage[newx][newy]  = ChangePage[x][y];
8857     ChangeCount[newx][newy] = ChangeCount[x][y];
8858     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8859   }
8860
8861   CustomValue[newx][newy] = CustomValue[x][y];
8862
8863   ChangeDelay[x][y] = 0;
8864   ChangePage[x][y] = -1;
8865   ChangeCount[x][y] = 0;
8866   ChangeEvent[x][y] = -1;
8867
8868   CustomValue[x][y] = 0;
8869
8870   // copy animation control values to new field
8871   GfxFrame[newx][newy]  = GfxFrame[x][y];
8872   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8873   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8874   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8875
8876   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8877
8878   // some elements can leave other elements behind after moving
8879   if (ei->move_leave_element != EL_EMPTY &&
8880       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8881       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8882   {
8883     int move_leave_element = ei->move_leave_element;
8884
8885     // this makes it possible to leave the removed element again
8886     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8887       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8888
8889     Tile[x][y] = move_leave_element;
8890
8891     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8892       MovDir[x][y] = direction;
8893
8894     InitField(x, y, FALSE);
8895
8896     if (GFX_CRUMBLED(Tile[x][y]))
8897       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8898
8899     if (IS_PLAYER_ELEMENT(move_leave_element))
8900       RelocatePlayer(x, y, move_leave_element);
8901   }
8902
8903   // do this after checking for left-behind element
8904   ResetGfxAnimation(x, y);      // reset animation values for old field
8905
8906   if (!CAN_MOVE(element) ||
8907       (CAN_FALL(element) && direction == MV_DOWN &&
8908        (element == EL_SPRING ||
8909         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8910         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8911     GfxDir[x][y] = MovDir[newx][newy] = 0;
8912
8913   TEST_DrawLevelField(x, y);
8914   TEST_DrawLevelField(newx, newy);
8915
8916   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8917
8918   // prevent pushed element from moving on in pushed direction
8919   if (pushed_by_player && CAN_MOVE(element) &&
8920       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8921       !(element_info[element].move_pattern & direction))
8922     TurnRound(newx, newy);
8923
8924   // prevent elements on conveyor belt from moving on in last direction
8925   if (pushed_by_conveyor && CAN_FALL(element) &&
8926       direction & MV_HORIZONTAL)
8927     MovDir[newx][newy] = 0;
8928
8929   if (!pushed_by_player)
8930   {
8931     int nextx = newx + dx, nexty = newy + dy;
8932     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8933
8934     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8935
8936     if (CAN_FALL(element) && direction == MV_DOWN)
8937       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8938
8939     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8940       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8941
8942     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8943       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8944   }
8945
8946   if (DONT_TOUCH(element))      // object may be nasty to player or others
8947   {
8948     TestIfBadThingTouchesPlayer(newx, newy);
8949     TestIfBadThingTouchesFriend(newx, newy);
8950
8951     if (!IS_CUSTOM_ELEMENT(element))
8952       TestIfBadThingTouchesOtherBadThing(newx, newy);
8953   }
8954   else if (element == EL_PENGUIN)
8955     TestIfFriendTouchesBadThing(newx, newy);
8956
8957   if (DONT_GET_HIT_BY(element))
8958   {
8959     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8960   }
8961
8962   // give the player one last chance (one more frame) to move away
8963   if (CAN_FALL(element) && direction == MV_DOWN &&
8964       (last_line || (!IS_FREE(x, newy + 1) &&
8965                      (!IS_PLAYER(x, newy + 1) ||
8966                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8967     Impact(x, newy);
8968
8969   if (pushed_by_player && !game.use_change_when_pushing_bug)
8970   {
8971     int push_side = MV_DIR_OPPOSITE(direction);
8972     struct PlayerInfo *player = PLAYERINFO(x, y);
8973
8974     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8975                                player->index_bit, push_side);
8976     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8977                                         player->index_bit, push_side);
8978   }
8979
8980   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8981     MovDelay[newx][newy] = 1;
8982
8983   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8984
8985   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8986   TestIfElementHitsCustomElement(newx, newy, direction);
8987   TestIfPlayerTouchesCustomElement(newx, newy);
8988   TestIfElementTouchesCustomElement(newx, newy);
8989
8990   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8991       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8992     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8993                              MV_DIR_OPPOSITE(direction));
8994 }
8995
8996 int AmoebaNeighbourNr(int ax, int ay)
8997 {
8998   int i;
8999   int element = Tile[ax][ay];
9000   int group_nr = 0;
9001   struct XY *xy = xy_topdown;
9002
9003   for (i = 0; i < NUM_DIRECTIONS; i++)
9004   {
9005     int x = ax + xy[i].x;
9006     int y = ay + xy[i].y;
9007
9008     if (!IN_LEV_FIELD(x, y))
9009       continue;
9010
9011     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9012       group_nr = AmoebaNr[x][y];
9013   }
9014
9015   return group_nr;
9016 }
9017
9018 static void AmoebaMerge(int ax, int ay)
9019 {
9020   int i, x, y, xx, yy;
9021   int new_group_nr = AmoebaNr[ax][ay];
9022   struct XY *xy = xy_topdown;
9023
9024   if (new_group_nr == 0)
9025     return;
9026
9027   for (i = 0; i < NUM_DIRECTIONS; i++)
9028   {
9029     x = ax + xy[i].x;
9030     y = ay + xy[i].y;
9031
9032     if (!IN_LEV_FIELD(x, y))
9033       continue;
9034
9035     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9036          Tile[x][y] == EL_BD_AMOEBA ||
9037          Tile[x][y] == EL_AMOEBA_DEAD) &&
9038         AmoebaNr[x][y] != new_group_nr)
9039     {
9040       int old_group_nr = AmoebaNr[x][y];
9041
9042       if (old_group_nr == 0)
9043         return;
9044
9045       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9046       AmoebaCnt[old_group_nr] = 0;
9047       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9048       AmoebaCnt2[old_group_nr] = 0;
9049
9050       SCAN_PLAYFIELD(xx, yy)
9051       {
9052         if (AmoebaNr[xx][yy] == old_group_nr)
9053           AmoebaNr[xx][yy] = new_group_nr;
9054       }
9055     }
9056   }
9057 }
9058
9059 void AmoebaToDiamond(int ax, int ay)
9060 {
9061   int i, x, y;
9062
9063   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9064   {
9065     int group_nr = AmoebaNr[ax][ay];
9066
9067 #ifdef DEBUG
9068     if (group_nr == 0)
9069     {
9070       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9071       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9072
9073       return;
9074     }
9075 #endif
9076
9077     SCAN_PLAYFIELD(x, y)
9078     {
9079       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9080       {
9081         AmoebaNr[x][y] = 0;
9082         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9083       }
9084     }
9085
9086     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9087                             SND_AMOEBA_TURNING_TO_GEM :
9088                             SND_AMOEBA_TURNING_TO_ROCK));
9089     Bang(ax, ay);
9090   }
9091   else
9092   {
9093     struct XY *xy = xy_topdown;
9094
9095     for (i = 0; i < NUM_DIRECTIONS; i++)
9096     {
9097       x = ax + xy[i].x;
9098       y = ay + xy[i].y;
9099
9100       if (!IN_LEV_FIELD(x, y))
9101         continue;
9102
9103       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9104       {
9105         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9106                               SND_AMOEBA_TURNING_TO_GEM :
9107                               SND_AMOEBA_TURNING_TO_ROCK));
9108         Bang(x, y);
9109       }
9110     }
9111   }
9112 }
9113
9114 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9115 {
9116   int x, y;
9117   int group_nr = AmoebaNr[ax][ay];
9118   boolean done = FALSE;
9119
9120 #ifdef DEBUG
9121   if (group_nr == 0)
9122   {
9123     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9124     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9125
9126     return;
9127   }
9128 #endif
9129
9130   SCAN_PLAYFIELD(x, y)
9131   {
9132     if (AmoebaNr[x][y] == group_nr &&
9133         (Tile[x][y] == EL_AMOEBA_DEAD ||
9134          Tile[x][y] == EL_BD_AMOEBA ||
9135          Tile[x][y] == EL_AMOEBA_GROWING))
9136     {
9137       AmoebaNr[x][y] = 0;
9138       Tile[x][y] = new_element;
9139       InitField(x, y, FALSE);
9140       TEST_DrawLevelField(x, y);
9141       done = TRUE;
9142     }
9143   }
9144
9145   if (done)
9146     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9147                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9148                             SND_BD_AMOEBA_TURNING_TO_GEM));
9149 }
9150
9151 static void AmoebaGrowing(int x, int y)
9152 {
9153   static DelayCounter sound_delay = { 0 };
9154
9155   if (!MovDelay[x][y])          // start new growing cycle
9156   {
9157     MovDelay[x][y] = 7;
9158
9159     if (DelayReached(&sound_delay))
9160     {
9161       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9162       sound_delay.value = 30;
9163     }
9164   }
9165
9166   if (MovDelay[x][y])           // wait some time before growing bigger
9167   {
9168     MovDelay[x][y]--;
9169     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9170     {
9171       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9172                                            6 - MovDelay[x][y]);
9173
9174       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9175     }
9176
9177     if (!MovDelay[x][y])
9178     {
9179       Tile[x][y] = Store[x][y];
9180       Store[x][y] = 0;
9181       TEST_DrawLevelField(x, y);
9182     }
9183   }
9184 }
9185
9186 static void AmoebaShrinking(int x, int y)
9187 {
9188   static DelayCounter sound_delay = { 0 };
9189
9190   if (!MovDelay[x][y])          // start new shrinking cycle
9191   {
9192     MovDelay[x][y] = 7;
9193
9194     if (DelayReached(&sound_delay))
9195       sound_delay.value = 30;
9196   }
9197
9198   if (MovDelay[x][y])           // wait some time before shrinking
9199   {
9200     MovDelay[x][y]--;
9201     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9202     {
9203       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9204                                            6 - MovDelay[x][y]);
9205
9206       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9207     }
9208
9209     if (!MovDelay[x][y])
9210     {
9211       Tile[x][y] = EL_EMPTY;
9212       TEST_DrawLevelField(x, y);
9213
9214       // don't let mole enter this field in this cycle;
9215       // (give priority to objects falling to this field from above)
9216       Stop[x][y] = TRUE;
9217     }
9218   }
9219 }
9220
9221 static void AmoebaReproduce(int ax, int ay)
9222 {
9223   int i;
9224   int element = Tile[ax][ay];
9225   int graphic = el2img(element);
9226   int newax = ax, neway = ay;
9227   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9228   struct XY *xy = xy_topdown;
9229
9230   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9231   {
9232     Tile[ax][ay] = EL_AMOEBA_DEAD;
9233     TEST_DrawLevelField(ax, ay);
9234     return;
9235   }
9236
9237   if (IS_ANIMATED(graphic))
9238     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9239
9240   if (!MovDelay[ax][ay])        // start making new amoeba field
9241     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9242
9243   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9244   {
9245     MovDelay[ax][ay]--;
9246     if (MovDelay[ax][ay])
9247       return;
9248   }
9249
9250   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9251   {
9252     int start = RND(4);
9253     int x = ax + xy[start].x;
9254     int y = ay + xy[start].y;
9255
9256     if (!IN_LEV_FIELD(x, y))
9257       return;
9258
9259     if (IS_FREE(x, y) ||
9260         CAN_GROW_INTO(Tile[x][y]) ||
9261         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9262         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9263     {
9264       newax = x;
9265       neway = y;
9266     }
9267
9268     if (newax == ax && neway == ay)
9269       return;
9270   }
9271   else                          // normal or "filled" (BD style) amoeba
9272   {
9273     int start = RND(4);
9274     boolean waiting_for_player = FALSE;
9275
9276     for (i = 0; i < NUM_DIRECTIONS; i++)
9277     {
9278       int j = (start + i) % 4;
9279       int x = ax + xy[j].x;
9280       int y = ay + xy[j].y;
9281
9282       if (!IN_LEV_FIELD(x, y))
9283         continue;
9284
9285       if (IS_FREE(x, y) ||
9286           CAN_GROW_INTO(Tile[x][y]) ||
9287           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9288           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9289       {
9290         newax = x;
9291         neway = y;
9292         break;
9293       }
9294       else if (IS_PLAYER(x, y))
9295         waiting_for_player = TRUE;
9296     }
9297
9298     if (newax == ax && neway == ay)             // amoeba cannot grow
9299     {
9300       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9301       {
9302         Tile[ax][ay] = EL_AMOEBA_DEAD;
9303         TEST_DrawLevelField(ax, ay);
9304         AmoebaCnt[AmoebaNr[ax][ay]]--;
9305
9306         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9307         {
9308           if (element == EL_AMOEBA_FULL)
9309             AmoebaToDiamond(ax, ay);
9310           else if (element == EL_BD_AMOEBA)
9311             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9312         }
9313       }
9314       return;
9315     }
9316     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9317     {
9318       // amoeba gets larger by growing in some direction
9319
9320       int new_group_nr = AmoebaNr[ax][ay];
9321
9322 #ifdef DEBUG
9323   if (new_group_nr == 0)
9324   {
9325     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9326           newax, neway);
9327     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9328
9329     return;
9330   }
9331 #endif
9332
9333       AmoebaNr[newax][neway] = new_group_nr;
9334       AmoebaCnt[new_group_nr]++;
9335       AmoebaCnt2[new_group_nr]++;
9336
9337       // if amoeba touches other amoeba(s) after growing, unify them
9338       AmoebaMerge(newax, neway);
9339
9340       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9341       {
9342         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9343         return;
9344       }
9345     }
9346   }
9347
9348   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9349       (neway == lev_fieldy - 1 && newax != ax))
9350   {
9351     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9352     Store[newax][neway] = element;
9353   }
9354   else if (neway == ay || element == EL_EMC_DRIPPER)
9355   {
9356     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9357
9358     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9359   }
9360   else
9361   {
9362     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9363     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9364     Store[ax][ay] = EL_AMOEBA_DROP;
9365     ContinueMoving(ax, ay);
9366     return;
9367   }
9368
9369   TEST_DrawLevelField(newax, neway);
9370 }
9371
9372 static void Life(int ax, int ay)
9373 {
9374   int x1, y1, x2, y2;
9375   int life_time = 40;
9376   int element = Tile[ax][ay];
9377   int graphic = el2img(element);
9378   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9379                          level.biomaze);
9380   boolean changed = FALSE;
9381
9382   if (IS_ANIMATED(graphic))
9383     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9384
9385   if (Stop[ax][ay])
9386     return;
9387
9388   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9389     MovDelay[ax][ay] = life_time;
9390
9391   if (MovDelay[ax][ay])         // wait some time before next cycle
9392   {
9393     MovDelay[ax][ay]--;
9394     if (MovDelay[ax][ay])
9395       return;
9396   }
9397
9398   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9399   {
9400     int xx = ax+x1, yy = ay+y1;
9401     int old_element = Tile[xx][yy];
9402     int num_neighbours = 0;
9403
9404     if (!IN_LEV_FIELD(xx, yy))
9405       continue;
9406
9407     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9408     {
9409       int x = xx+x2, y = yy+y2;
9410
9411       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9412         continue;
9413
9414       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9415       boolean is_neighbour = FALSE;
9416
9417       if (level.use_life_bugs)
9418         is_neighbour =
9419           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9420            (IS_FREE(x, y)                             &&  Stop[x][y]));
9421       else
9422         is_neighbour =
9423           (Last[x][y] == element || is_player_cell);
9424
9425       if (is_neighbour)
9426         num_neighbours++;
9427     }
9428
9429     boolean is_free = FALSE;
9430
9431     if (level.use_life_bugs)
9432       is_free = (IS_FREE(xx, yy));
9433     else
9434       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9435
9436     if (xx == ax && yy == ay)           // field in the middle
9437     {
9438       if (num_neighbours < life_parameter[0] ||
9439           num_neighbours > life_parameter[1])
9440       {
9441         Tile[xx][yy] = EL_EMPTY;
9442         if (Tile[xx][yy] != old_element)
9443           TEST_DrawLevelField(xx, yy);
9444         Stop[xx][yy] = TRUE;
9445         changed = TRUE;
9446       }
9447     }
9448     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9449     {                                   // free border field
9450       if (num_neighbours >= life_parameter[2] &&
9451           num_neighbours <= life_parameter[3])
9452       {
9453         Tile[xx][yy] = element;
9454         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9455         if (Tile[xx][yy] != old_element)
9456           TEST_DrawLevelField(xx, yy);
9457         Stop[xx][yy] = TRUE;
9458         changed = TRUE;
9459       }
9460     }
9461   }
9462
9463   if (changed)
9464     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9465                    SND_GAME_OF_LIFE_GROWING);
9466 }
9467
9468 static void InitRobotWheel(int x, int y)
9469 {
9470   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9471 }
9472
9473 static void RunRobotWheel(int x, int y)
9474 {
9475   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9476 }
9477
9478 static void StopRobotWheel(int x, int y)
9479 {
9480   if (game.robot_wheel_x == x &&
9481       game.robot_wheel_y == y)
9482   {
9483     game.robot_wheel_x = -1;
9484     game.robot_wheel_y = -1;
9485     game.robot_wheel_active = FALSE;
9486   }
9487 }
9488
9489 static void InitTimegateWheel(int x, int y)
9490 {
9491   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9492 }
9493
9494 static void RunTimegateWheel(int x, int y)
9495 {
9496   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9497 }
9498
9499 static void InitMagicBallDelay(int x, int y)
9500 {
9501   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9502 }
9503
9504 static void ActivateMagicBall(int bx, int by)
9505 {
9506   int x, y;
9507
9508   if (level.ball_random)
9509   {
9510     int pos_border = RND(8);    // select one of the eight border elements
9511     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9512     int xx = pos_content % 3;
9513     int yy = pos_content / 3;
9514
9515     x = bx - 1 + xx;
9516     y = by - 1 + yy;
9517
9518     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9519       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9520   }
9521   else
9522   {
9523     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9524     {
9525       int xx = x - bx + 1;
9526       int yy = y - by + 1;
9527
9528       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9529         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9530     }
9531   }
9532
9533   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9534 }
9535
9536 static void CheckExit(int x, int y)
9537 {
9538   if (game.gems_still_needed > 0 ||
9539       game.sokoban_fields_still_needed > 0 ||
9540       game.sokoban_objects_still_needed > 0 ||
9541       game.lights_still_needed > 0)
9542   {
9543     int element = Tile[x][y];
9544     int graphic = el2img(element);
9545
9546     if (IS_ANIMATED(graphic))
9547       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548
9549     return;
9550   }
9551
9552   // do not re-open exit door closed after last player
9553   if (game.all_players_gone)
9554     return;
9555
9556   Tile[x][y] = EL_EXIT_OPENING;
9557
9558   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9559 }
9560
9561 static void CheckExitEM(int x, int y)
9562 {
9563   if (game.gems_still_needed > 0 ||
9564       game.sokoban_fields_still_needed > 0 ||
9565       game.sokoban_objects_still_needed > 0 ||
9566       game.lights_still_needed > 0)
9567   {
9568     int element = Tile[x][y];
9569     int graphic = el2img(element);
9570
9571     if (IS_ANIMATED(graphic))
9572       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9573
9574     return;
9575   }
9576
9577   // do not re-open exit door closed after last player
9578   if (game.all_players_gone)
9579     return;
9580
9581   Tile[x][y] = EL_EM_EXIT_OPENING;
9582
9583   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9584 }
9585
9586 static void CheckExitSteel(int x, int y)
9587 {
9588   if (game.gems_still_needed > 0 ||
9589       game.sokoban_fields_still_needed > 0 ||
9590       game.sokoban_objects_still_needed > 0 ||
9591       game.lights_still_needed > 0)
9592   {
9593     int element = Tile[x][y];
9594     int graphic = el2img(element);
9595
9596     if (IS_ANIMATED(graphic))
9597       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9598
9599     return;
9600   }
9601
9602   // do not re-open exit door closed after last player
9603   if (game.all_players_gone)
9604     return;
9605
9606   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9607
9608   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9609 }
9610
9611 static void CheckExitSteelEM(int x, int y)
9612 {
9613   if (game.gems_still_needed > 0 ||
9614       game.sokoban_fields_still_needed > 0 ||
9615       game.sokoban_objects_still_needed > 0 ||
9616       game.lights_still_needed > 0)
9617   {
9618     int element = Tile[x][y];
9619     int graphic = el2img(element);
9620
9621     if (IS_ANIMATED(graphic))
9622       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9623
9624     return;
9625   }
9626
9627   // do not re-open exit door closed after last player
9628   if (game.all_players_gone)
9629     return;
9630
9631   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9632
9633   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9634 }
9635
9636 static void CheckExitSP(int x, int y)
9637 {
9638   if (game.gems_still_needed > 0)
9639   {
9640     int element = Tile[x][y];
9641     int graphic = el2img(element);
9642
9643     if (IS_ANIMATED(graphic))
9644       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9645
9646     return;
9647   }
9648
9649   // do not re-open exit door closed after last player
9650   if (game.all_players_gone)
9651     return;
9652
9653   Tile[x][y] = EL_SP_EXIT_OPENING;
9654
9655   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9656 }
9657
9658 static void CloseAllOpenTimegates(void)
9659 {
9660   int x, y;
9661
9662   SCAN_PLAYFIELD(x, y)
9663   {
9664     int element = Tile[x][y];
9665
9666     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9667     {
9668       Tile[x][y] = EL_TIMEGATE_CLOSING;
9669
9670       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9671     }
9672   }
9673 }
9674
9675 static void DrawTwinkleOnField(int x, int y)
9676 {
9677   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9678     return;
9679
9680   if (Tile[x][y] == EL_BD_DIAMOND)
9681     return;
9682
9683   if (MovDelay[x][y] == 0)      // next animation frame
9684     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9685
9686   if (MovDelay[x][y] != 0)      // wait some time before next frame
9687   {
9688     MovDelay[x][y]--;
9689
9690     DrawLevelElementAnimation(x, y, Tile[x][y]);
9691
9692     if (MovDelay[x][y] != 0)
9693     {
9694       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9695                                            10 - MovDelay[x][y]);
9696
9697       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9698     }
9699   }
9700 }
9701
9702 static void WallGrowing(int x, int y)
9703 {
9704   int delay = 6;
9705
9706   if (!MovDelay[x][y])          // next animation frame
9707     MovDelay[x][y] = 3 * delay;
9708
9709   if (MovDelay[x][y])           // wait some time before next frame
9710   {
9711     MovDelay[x][y]--;
9712
9713     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9714     {
9715       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9716       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9717
9718       DrawLevelGraphic(x, y, graphic, frame);
9719     }
9720
9721     if (!MovDelay[x][y])
9722     {
9723       if (MovDir[x][y] == MV_LEFT)
9724       {
9725         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9726           TEST_DrawLevelField(x - 1, y);
9727       }
9728       else if (MovDir[x][y] == MV_RIGHT)
9729       {
9730         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9731           TEST_DrawLevelField(x + 1, y);
9732       }
9733       else if (MovDir[x][y] == MV_UP)
9734       {
9735         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9736           TEST_DrawLevelField(x, y - 1);
9737       }
9738       else
9739       {
9740         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9741           TEST_DrawLevelField(x, y + 1);
9742       }
9743
9744       Tile[x][y] = Store[x][y];
9745       Store[x][y] = 0;
9746       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9747       TEST_DrawLevelField(x, y);
9748     }
9749   }
9750 }
9751
9752 static void CheckWallGrowing(int ax, int ay)
9753 {
9754   int element = Tile[ax][ay];
9755   int graphic = el2img(element);
9756   boolean free_top    = FALSE;
9757   boolean free_bottom = FALSE;
9758   boolean free_left   = FALSE;
9759   boolean free_right  = FALSE;
9760   boolean stop_top    = FALSE;
9761   boolean stop_bottom = FALSE;
9762   boolean stop_left   = FALSE;
9763   boolean stop_right  = FALSE;
9764   boolean new_wall    = FALSE;
9765
9766   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9767                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9768                            element == EL_EXPANDABLE_STEELWALL_ANY);
9769
9770   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9771                              element == EL_EXPANDABLE_WALL_ANY ||
9772                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9773                              element == EL_EXPANDABLE_STEELWALL_ANY);
9774
9775   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9776                              element == EL_EXPANDABLE_WALL_ANY ||
9777                              element == EL_EXPANDABLE_WALL ||
9778                              element == EL_BD_EXPANDABLE_WALL ||
9779                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9780                              element == EL_EXPANDABLE_STEELWALL_ANY);
9781
9782   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9783                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9784
9785   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9786                              element == EL_EXPANDABLE_WALL ||
9787                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9788
9789   int wall_growing = (is_steelwall ?
9790                       EL_EXPANDABLE_STEELWALL_GROWING :
9791                       EL_EXPANDABLE_WALL_GROWING);
9792
9793   int gfx_wall_growing_up    = (is_steelwall ?
9794                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9795                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9796   int gfx_wall_growing_down  = (is_steelwall ?
9797                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9798                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9799   int gfx_wall_growing_left  = (is_steelwall ?
9800                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9801                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9802   int gfx_wall_growing_right = (is_steelwall ?
9803                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9804                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9805
9806   if (IS_ANIMATED(graphic))
9807     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9808
9809   if (!MovDelay[ax][ay])        // start building new wall
9810     MovDelay[ax][ay] = 6;
9811
9812   if (MovDelay[ax][ay])         // wait some time before building new wall
9813   {
9814     MovDelay[ax][ay]--;
9815     if (MovDelay[ax][ay])
9816       return;
9817   }
9818
9819   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9820     free_top = TRUE;
9821   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9822     free_bottom = TRUE;
9823   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9824     free_left = TRUE;
9825   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9826     free_right = TRUE;
9827
9828   if (grow_vertical)
9829   {
9830     if (free_top)
9831     {
9832       Tile[ax][ay - 1] = wall_growing;
9833       Store[ax][ay - 1] = element;
9834       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9835
9836       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9837         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9838
9839       new_wall = TRUE;
9840     }
9841
9842     if (free_bottom)
9843     {
9844       Tile[ax][ay + 1] = wall_growing;
9845       Store[ax][ay + 1] = element;
9846       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9847
9848       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9849         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9850
9851       new_wall = TRUE;
9852     }
9853   }
9854
9855   if (grow_horizontal)
9856   {
9857     if (free_left)
9858     {
9859       Tile[ax - 1][ay] = wall_growing;
9860       Store[ax - 1][ay] = element;
9861       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9862
9863       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9864         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9865
9866       new_wall = TRUE;
9867     }
9868
9869     if (free_right)
9870     {
9871       Tile[ax + 1][ay] = wall_growing;
9872       Store[ax + 1][ay] = element;
9873       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9874
9875       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9876         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9877
9878       new_wall = TRUE;
9879     }
9880   }
9881
9882   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9883     TEST_DrawLevelField(ax, ay);
9884
9885   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9886     stop_top = TRUE;
9887   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9888     stop_bottom = TRUE;
9889   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9890     stop_left = TRUE;
9891   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9892     stop_right = TRUE;
9893
9894   if (((stop_top && stop_bottom) || stop_horizontal) &&
9895       ((stop_left && stop_right) || stop_vertical))
9896     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9897
9898   if (new_wall)
9899     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9900 }
9901
9902 static void CheckForDragon(int x, int y)
9903 {
9904   int i, j;
9905   boolean dragon_found = FALSE;
9906   struct XY *xy = xy_topdown;
9907
9908   for (i = 0; i < NUM_DIRECTIONS; i++)
9909   {
9910     for (j = 0; j < 4; j++)
9911     {
9912       int xx = x + j * xy[i].x;
9913       int yy = y + j * xy[i].y;
9914
9915       if (IN_LEV_FIELD(xx, yy) &&
9916           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9917       {
9918         if (Tile[xx][yy] == EL_DRAGON)
9919           dragon_found = TRUE;
9920       }
9921       else
9922         break;
9923     }
9924   }
9925
9926   if (!dragon_found)
9927   {
9928     for (i = 0; i < NUM_DIRECTIONS; i++)
9929     {
9930       for (j = 0; j < 3; j++)
9931       {
9932         int xx = x + j * xy[i].x;
9933         int yy = y + j * xy[i].y;
9934
9935         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9936         {
9937           Tile[xx][yy] = EL_EMPTY;
9938           TEST_DrawLevelField(xx, yy);
9939         }
9940         else
9941           break;
9942       }
9943     }
9944   }
9945 }
9946
9947 static void InitBuggyBase(int x, int y)
9948 {
9949   int element = Tile[x][y];
9950   int activating_delay = FRAMES_PER_SECOND / 4;
9951
9952   ChangeDelay[x][y] =
9953     (element == EL_SP_BUGGY_BASE ?
9954      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9955      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9956      activating_delay :
9957      element == EL_SP_BUGGY_BASE_ACTIVE ?
9958      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9959 }
9960
9961 static void WarnBuggyBase(int x, int y)
9962 {
9963   int i;
9964   struct XY *xy = xy_topdown;
9965
9966   for (i = 0; i < NUM_DIRECTIONS; i++)
9967   {
9968     int xx = x + xy[i].x;
9969     int yy = y + xy[i].y;
9970
9971     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9972     {
9973       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9974
9975       break;
9976     }
9977   }
9978 }
9979
9980 static void InitTrap(int x, int y)
9981 {
9982   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9983 }
9984
9985 static void ActivateTrap(int x, int y)
9986 {
9987   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9988 }
9989
9990 static void ChangeActiveTrap(int x, int y)
9991 {
9992   int graphic = IMG_TRAP_ACTIVE;
9993
9994   // if new animation frame was drawn, correct crumbled sand border
9995   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9996     TEST_DrawLevelFieldCrumbled(x, y);
9997 }
9998
9999 static int getSpecialActionElement(int element, int number, int base_element)
10000 {
10001   return (element != EL_EMPTY ? element :
10002           number != -1 ? base_element + number - 1 :
10003           EL_EMPTY);
10004 }
10005
10006 static int getModifiedActionNumber(int value_old, int operator, int operand,
10007                                    int value_min, int value_max)
10008 {
10009   int value_new = (operator == CA_MODE_SET      ? operand :
10010                    operator == CA_MODE_ADD      ? value_old + operand :
10011                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10012                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10013                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10014                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10015                    value_old);
10016
10017   return (value_new < value_min ? value_min :
10018           value_new > value_max ? value_max :
10019           value_new);
10020 }
10021
10022 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10023 {
10024   struct ElementInfo *ei = &element_info[element];
10025   struct ElementChangeInfo *change = &ei->change_page[page];
10026   int target_element = change->target_element;
10027   int action_type = change->action_type;
10028   int action_mode = change->action_mode;
10029   int action_arg = change->action_arg;
10030   int action_element = change->action_element;
10031   int i;
10032
10033   if (!change->has_action)
10034     return;
10035
10036   // ---------- determine action paramater values -----------------------------
10037
10038   int level_time_value =
10039     (level.time > 0 ? TimeLeft :
10040      TimePlayed);
10041
10042   int action_arg_element_raw =
10043     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10044      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10045      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10046      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10047      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10048      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10049      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10050      EL_EMPTY);
10051   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10052
10053   int action_arg_direction =
10054     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10055      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10056      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10057      change->actual_trigger_side :
10058      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10059      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10060      MV_NONE);
10061
10062   int action_arg_number_min =
10063     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10064      CA_ARG_MIN);
10065
10066   int action_arg_number_max =
10067     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10068      action_type == CA_SET_LEVEL_GEMS ? 999 :
10069      action_type == CA_SET_LEVEL_TIME ? 9999 :
10070      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10071      action_type == CA_SET_CE_VALUE ? 9999 :
10072      action_type == CA_SET_CE_SCORE ? 9999 :
10073      CA_ARG_MAX);
10074
10075   int action_arg_number_reset =
10076     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10077      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10078      action_type == CA_SET_LEVEL_TIME ? level.time :
10079      action_type == CA_SET_LEVEL_SCORE ? 0 :
10080      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10081      action_type == CA_SET_CE_SCORE ? 0 :
10082      0);
10083
10084   int action_arg_number =
10085     (action_arg <= CA_ARG_MAX ? action_arg :
10086      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10087      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10088      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10089      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10090      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10091      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10092      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10093      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10094      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10095      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10096      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10097      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10098      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10099      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10100      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10101      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10102      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10103      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10104      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10105      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10106      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10107      -1);
10108
10109   int action_arg_number_old =
10110     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10111      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10112      action_type == CA_SET_LEVEL_SCORE ? game.score :
10113      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10114      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10115      0);
10116
10117   int action_arg_number_new =
10118     getModifiedActionNumber(action_arg_number_old,
10119                             action_mode, action_arg_number,
10120                             action_arg_number_min, action_arg_number_max);
10121
10122   int trigger_player_bits =
10123     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10124      change->actual_trigger_player_bits : change->trigger_player);
10125
10126   int action_arg_player_bits =
10127     (action_arg >= CA_ARG_PLAYER_1 &&
10128      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10129      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10130      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10131      PLAYER_BITS_ANY);
10132
10133   // ---------- execute action  -----------------------------------------------
10134
10135   switch (action_type)
10136   {
10137     case CA_NO_ACTION:
10138     {
10139       return;
10140     }
10141
10142     // ---------- level actions  ----------------------------------------------
10143
10144     case CA_RESTART_LEVEL:
10145     {
10146       game.restart_level = TRUE;
10147
10148       break;
10149     }
10150
10151     case CA_SHOW_ENVELOPE:
10152     {
10153       int element = getSpecialActionElement(action_arg_element,
10154                                             action_arg_number, EL_ENVELOPE_1);
10155
10156       if (IS_ENVELOPE(element))
10157         local_player->show_envelope = element;
10158
10159       break;
10160     }
10161
10162     case CA_SET_LEVEL_TIME:
10163     {
10164       if (level.time > 0)       // only modify limited time value
10165       {
10166         TimeLeft = action_arg_number_new;
10167
10168         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10169
10170         DisplayGameControlValues();
10171
10172         if (!TimeLeft && game.time_limit)
10173           for (i = 0; i < MAX_PLAYERS; i++)
10174             KillPlayer(&stored_player[i]);
10175       }
10176
10177       break;
10178     }
10179
10180     case CA_SET_LEVEL_SCORE:
10181     {
10182       game.score = action_arg_number_new;
10183
10184       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10185
10186       DisplayGameControlValues();
10187
10188       break;
10189     }
10190
10191     case CA_SET_LEVEL_GEMS:
10192     {
10193       game.gems_still_needed = action_arg_number_new;
10194
10195       game.snapshot.collected_item = TRUE;
10196
10197       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10198
10199       DisplayGameControlValues();
10200
10201       break;
10202     }
10203
10204     case CA_SET_LEVEL_WIND:
10205     {
10206       game.wind_direction = action_arg_direction;
10207
10208       break;
10209     }
10210
10211     case CA_SET_LEVEL_RANDOM_SEED:
10212     {
10213       // ensure that setting a new random seed while playing is predictable
10214       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10215
10216       break;
10217     }
10218
10219     // ---------- player actions  ---------------------------------------------
10220
10221     case CA_MOVE_PLAYER:
10222     case CA_MOVE_PLAYER_NEW:
10223     {
10224       // automatically move to the next field in specified direction
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226         if (trigger_player_bits & (1 << i))
10227           if (action_type == CA_MOVE_PLAYER ||
10228               stored_player[i].MovPos == 0)
10229             stored_player[i].programmed_action = action_arg_direction;
10230
10231       break;
10232     }
10233
10234     case CA_EXIT_PLAYER:
10235     {
10236       for (i = 0; i < MAX_PLAYERS; i++)
10237         if (action_arg_player_bits & (1 << i))
10238           ExitPlayer(&stored_player[i]);
10239
10240       if (game.players_still_needed == 0)
10241         LevelSolved();
10242
10243       break;
10244     }
10245
10246     case CA_KILL_PLAYER:
10247     {
10248       for (i = 0; i < MAX_PLAYERS; i++)
10249         if (action_arg_player_bits & (1 << i))
10250           KillPlayer(&stored_player[i]);
10251
10252       break;
10253     }
10254
10255     case CA_SET_PLAYER_KEYS:
10256     {
10257       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10258       int element = getSpecialActionElement(action_arg_element,
10259                                             action_arg_number, EL_KEY_1);
10260
10261       if (IS_KEY(element))
10262       {
10263         for (i = 0; i < MAX_PLAYERS; i++)
10264         {
10265           if (trigger_player_bits & (1 << i))
10266           {
10267             stored_player[i].key[KEY_NR(element)] = key_state;
10268
10269             DrawGameDoorValues();
10270           }
10271         }
10272       }
10273
10274       break;
10275     }
10276
10277     case CA_SET_PLAYER_SPEED:
10278     {
10279       for (i = 0; i < MAX_PLAYERS; i++)
10280       {
10281         if (trigger_player_bits & (1 << i))
10282         {
10283           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10284
10285           if (action_arg == CA_ARG_SPEED_FASTER &&
10286               stored_player[i].cannot_move)
10287           {
10288             action_arg_number = STEPSIZE_VERY_SLOW;
10289           }
10290           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10291                    action_arg == CA_ARG_SPEED_FASTER)
10292           {
10293             action_arg_number = 2;
10294             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10295                            CA_MODE_MULTIPLY);
10296           }
10297           else if (action_arg == CA_ARG_NUMBER_RESET)
10298           {
10299             action_arg_number = level.initial_player_stepsize[i];
10300           }
10301
10302           move_stepsize =
10303             getModifiedActionNumber(move_stepsize,
10304                                     action_mode,
10305                                     action_arg_number,
10306                                     action_arg_number_min,
10307                                     action_arg_number_max);
10308
10309           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10310         }
10311       }
10312
10313       break;
10314     }
10315
10316     case CA_SET_PLAYER_SHIELD:
10317     {
10318       for (i = 0; i < MAX_PLAYERS; i++)
10319       {
10320         if (trigger_player_bits & (1 << i))
10321         {
10322           if (action_arg == CA_ARG_SHIELD_OFF)
10323           {
10324             stored_player[i].shield_normal_time_left = 0;
10325             stored_player[i].shield_deadly_time_left = 0;
10326           }
10327           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10328           {
10329             stored_player[i].shield_normal_time_left = 999999;
10330           }
10331           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10332           {
10333             stored_player[i].shield_normal_time_left = 999999;
10334             stored_player[i].shield_deadly_time_left = 999999;
10335           }
10336         }
10337       }
10338
10339       break;
10340     }
10341
10342     case CA_SET_PLAYER_GRAVITY:
10343     {
10344       for (i = 0; i < MAX_PLAYERS; i++)
10345       {
10346         if (trigger_player_bits & (1 << i))
10347         {
10348           stored_player[i].gravity =
10349             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10350              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10351              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10352              stored_player[i].gravity);
10353         }
10354       }
10355
10356       break;
10357     }
10358
10359     case CA_SET_PLAYER_ARTWORK:
10360     {
10361       for (i = 0; i < MAX_PLAYERS; i++)
10362       {
10363         if (trigger_player_bits & (1 << i))
10364         {
10365           int artwork_element = action_arg_element;
10366
10367           if (action_arg == CA_ARG_ELEMENT_RESET)
10368             artwork_element =
10369               (level.use_artwork_element[i] ? level.artwork_element[i] :
10370                stored_player[i].element_nr);
10371
10372           if (stored_player[i].artwork_element != artwork_element)
10373             stored_player[i].Frame = 0;
10374
10375           stored_player[i].artwork_element = artwork_element;
10376
10377           SetPlayerWaiting(&stored_player[i], FALSE);
10378
10379           // set number of special actions for bored and sleeping animation
10380           stored_player[i].num_special_action_bored =
10381             get_num_special_action(artwork_element,
10382                                    ACTION_BORING_1, ACTION_BORING_LAST);
10383           stored_player[i].num_special_action_sleeping =
10384             get_num_special_action(artwork_element,
10385                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10386         }
10387       }
10388
10389       break;
10390     }
10391
10392     case CA_SET_PLAYER_INVENTORY:
10393     {
10394       for (i = 0; i < MAX_PLAYERS; i++)
10395       {
10396         struct PlayerInfo *player = &stored_player[i];
10397         int j, k;
10398
10399         if (trigger_player_bits & (1 << i))
10400         {
10401           int inventory_element = action_arg_element;
10402
10403           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10404               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10405               action_arg == CA_ARG_ELEMENT_ACTION)
10406           {
10407             int element = inventory_element;
10408             int collect_count = element_info[element].collect_count_initial;
10409
10410             if (!IS_CUSTOM_ELEMENT(element))
10411               collect_count = 1;
10412
10413             if (collect_count == 0)
10414               player->inventory_infinite_element = element;
10415             else
10416               for (k = 0; k < collect_count; k++)
10417                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10418                   player->inventory_element[player->inventory_size++] =
10419                     element;
10420           }
10421           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10422                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10423                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10424           {
10425             if (player->inventory_infinite_element != EL_UNDEFINED &&
10426                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10427                                      action_arg_element_raw))
10428               player->inventory_infinite_element = EL_UNDEFINED;
10429
10430             for (k = 0, j = 0; j < player->inventory_size; j++)
10431             {
10432               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10433                                         action_arg_element_raw))
10434                 player->inventory_element[k++] = player->inventory_element[j];
10435             }
10436
10437             player->inventory_size = k;
10438           }
10439           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10440           {
10441             if (player->inventory_size > 0)
10442             {
10443               for (j = 0; j < player->inventory_size - 1; j++)
10444                 player->inventory_element[j] = player->inventory_element[j + 1];
10445
10446               player->inventory_size--;
10447             }
10448           }
10449           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10450           {
10451             if (player->inventory_size > 0)
10452               player->inventory_size--;
10453           }
10454           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10455           {
10456             player->inventory_infinite_element = EL_UNDEFINED;
10457             player->inventory_size = 0;
10458           }
10459           else if (action_arg == CA_ARG_INVENTORY_RESET)
10460           {
10461             player->inventory_infinite_element = EL_UNDEFINED;
10462             player->inventory_size = 0;
10463
10464             if (level.use_initial_inventory[i])
10465             {
10466               for (j = 0; j < level.initial_inventory_size[i]; j++)
10467               {
10468                 int element = level.initial_inventory_content[i][j];
10469                 int collect_count = element_info[element].collect_count_initial;
10470
10471                 if (!IS_CUSTOM_ELEMENT(element))
10472                   collect_count = 1;
10473
10474                 if (collect_count == 0)
10475                   player->inventory_infinite_element = element;
10476                 else
10477                   for (k = 0; k < collect_count; k++)
10478                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10479                       player->inventory_element[player->inventory_size++] =
10480                         element;
10481               }
10482             }
10483           }
10484         }
10485       }
10486
10487       break;
10488     }
10489
10490     // ---------- CE actions  -------------------------------------------------
10491
10492     case CA_SET_CE_VALUE:
10493     {
10494       int last_ce_value = CustomValue[x][y];
10495
10496       CustomValue[x][y] = action_arg_number_new;
10497
10498       if (CustomValue[x][y] != last_ce_value)
10499       {
10500         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10501         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10502
10503         if (CustomValue[x][y] == 0)
10504         {
10505           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10506           ChangeCount[x][y] = 0;        // allow at least one more change
10507
10508           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10509           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10510         }
10511       }
10512
10513       break;
10514     }
10515
10516     case CA_SET_CE_SCORE:
10517     {
10518       int last_ce_score = ei->collect_score;
10519
10520       ei->collect_score = action_arg_number_new;
10521
10522       if (ei->collect_score != last_ce_score)
10523       {
10524         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10525         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10526
10527         if (ei->collect_score == 0)
10528         {
10529           int xx, yy;
10530
10531           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10532           ChangeCount[x][y] = 0;        // allow at least one more change
10533
10534           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10535           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10536
10537           /*
10538             This is a very special case that seems to be a mixture between
10539             CheckElementChange() and CheckTriggeredElementChange(): while
10540             the first one only affects single elements that are triggered
10541             directly, the second one affects multiple elements in the playfield
10542             that are triggered indirectly by another element. This is a third
10543             case: Changing the CE score always affects multiple identical CEs,
10544             so every affected CE must be checked, not only the single CE for
10545             which the CE score was changed in the first place (as every instance
10546             of that CE shares the same CE score, and therefore also can change)!
10547           */
10548           SCAN_PLAYFIELD(xx, yy)
10549           {
10550             if (Tile[xx][yy] == element)
10551               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10552                                  CE_SCORE_GETS_ZERO);
10553           }
10554         }
10555       }
10556
10557       break;
10558     }
10559
10560     case CA_SET_CE_ARTWORK:
10561     {
10562       int artwork_element = action_arg_element;
10563       boolean reset_frame = FALSE;
10564       int xx, yy;
10565
10566       if (action_arg == CA_ARG_ELEMENT_RESET)
10567         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10568                            element);
10569
10570       if (ei->gfx_element != artwork_element)
10571         reset_frame = TRUE;
10572
10573       ei->gfx_element = artwork_element;
10574
10575       SCAN_PLAYFIELD(xx, yy)
10576       {
10577         if (Tile[xx][yy] == element)
10578         {
10579           if (reset_frame)
10580           {
10581             ResetGfxAnimation(xx, yy);
10582             ResetRandomAnimationValue(xx, yy);
10583           }
10584
10585           TEST_DrawLevelField(xx, yy);
10586         }
10587       }
10588
10589       break;
10590     }
10591
10592     // ---------- engine actions  ---------------------------------------------
10593
10594     case CA_SET_ENGINE_SCAN_MODE:
10595     {
10596       InitPlayfieldScanMode(action_arg);
10597
10598       break;
10599     }
10600
10601     default:
10602       break;
10603   }
10604 }
10605
10606 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10607 {
10608   int old_element = Tile[x][y];
10609   int new_element = GetElementFromGroupElement(element);
10610   int previous_move_direction = MovDir[x][y];
10611   int last_ce_value = CustomValue[x][y];
10612   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10613   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10614   boolean add_player_onto_element = (new_element_is_player &&
10615                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10616                                      IS_WALKABLE(old_element));
10617
10618   if (!add_player_onto_element)
10619   {
10620     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10621       RemoveMovingField(x, y);
10622     else
10623       RemoveField(x, y);
10624
10625     Tile[x][y] = new_element;
10626
10627     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10628       MovDir[x][y] = previous_move_direction;
10629
10630     if (element_info[new_element].use_last_ce_value)
10631       CustomValue[x][y] = last_ce_value;
10632
10633     InitField_WithBug1(x, y, FALSE);
10634
10635     new_element = Tile[x][y];   // element may have changed
10636
10637     ResetGfxAnimation(x, y);
10638     ResetRandomAnimationValue(x, y);
10639
10640     TEST_DrawLevelField(x, y);
10641
10642     if (GFX_CRUMBLED(new_element))
10643       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10644
10645     if (old_element == EL_EXPLOSION)
10646     {
10647       Store[x][y] = Store2[x][y] = 0;
10648
10649       // check if new element replaces an exploding player, requiring cleanup
10650       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10651         StorePlayer[x][y] = 0;
10652     }
10653
10654     // check if element under the player changes from accessible to unaccessible
10655     // (needed for special case of dropping element which then changes)
10656     // (must be checked after creating new element for walkable group elements)
10657     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10658         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10659     {
10660       KillPlayer(PLAYERINFO(x, y));
10661
10662       return;
10663     }
10664   }
10665
10666   // "ChangeCount" not set yet to allow "entered by player" change one time
10667   if (new_element_is_player)
10668     RelocatePlayer(x, y, new_element);
10669
10670   if (is_change)
10671     ChangeCount[x][y]++;        // count number of changes in the same frame
10672
10673   TestIfBadThingTouchesPlayer(x, y);
10674   TestIfPlayerTouchesCustomElement(x, y);
10675   TestIfElementTouchesCustomElement(x, y);
10676 }
10677
10678 static void CreateField(int x, int y, int element)
10679 {
10680   CreateFieldExt(x, y, element, FALSE);
10681 }
10682
10683 static void CreateElementFromChange(int x, int y, int element)
10684 {
10685   element = GET_VALID_RUNTIME_ELEMENT(element);
10686
10687   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10688   {
10689     int old_element = Tile[x][y];
10690
10691     // prevent changed element from moving in same engine frame
10692     // unless both old and new element can either fall or move
10693     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10694         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10695       Stop[x][y] = TRUE;
10696   }
10697
10698   CreateFieldExt(x, y, element, TRUE);
10699 }
10700
10701 static boolean ChangeElement(int x, int y, int element, int page)
10702 {
10703   struct ElementInfo *ei = &element_info[element];
10704   struct ElementChangeInfo *change = &ei->change_page[page];
10705   int ce_value = CustomValue[x][y];
10706   int ce_score = ei->collect_score;
10707   int target_element;
10708   int old_element = Tile[x][y];
10709
10710   // always use default change event to prevent running into a loop
10711   if (ChangeEvent[x][y] == -1)
10712     ChangeEvent[x][y] = CE_DELAY;
10713
10714   if (ChangeEvent[x][y] == CE_DELAY)
10715   {
10716     // reset actual trigger element, trigger player and action element
10717     change->actual_trigger_element = EL_EMPTY;
10718     change->actual_trigger_player = EL_EMPTY;
10719     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10720     change->actual_trigger_side = CH_SIDE_NONE;
10721     change->actual_trigger_ce_value = 0;
10722     change->actual_trigger_ce_score = 0;
10723   }
10724
10725   // do not change elements more than a specified maximum number of changes
10726   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10727     return FALSE;
10728
10729   ChangeCount[x][y]++;          // count number of changes in the same frame
10730
10731   if (ei->has_anim_event)
10732     HandleGlobalAnimEventByElementChange(element, page, x, y);
10733
10734   if (change->explode)
10735   {
10736     Bang(x, y);
10737
10738     return TRUE;
10739   }
10740
10741   if (change->use_target_content)
10742   {
10743     boolean complete_replace = TRUE;
10744     boolean can_replace[3][3];
10745     int xx, yy;
10746
10747     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10748     {
10749       boolean is_empty;
10750       boolean is_walkable;
10751       boolean is_diggable;
10752       boolean is_collectible;
10753       boolean is_removable;
10754       boolean is_destructible;
10755       int ex = x + xx - 1;
10756       int ey = y + yy - 1;
10757       int content_element = change->target_content.e[xx][yy];
10758       int e;
10759
10760       can_replace[xx][yy] = TRUE;
10761
10762       if (ex == x && ey == y)   // do not check changing element itself
10763         continue;
10764
10765       if (content_element == EL_EMPTY_SPACE)
10766       {
10767         can_replace[xx][yy] = FALSE;    // do not replace border with space
10768
10769         continue;
10770       }
10771
10772       if (!IN_LEV_FIELD(ex, ey))
10773       {
10774         can_replace[xx][yy] = FALSE;
10775         complete_replace = FALSE;
10776
10777         continue;
10778       }
10779
10780       e = Tile[ex][ey];
10781
10782       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10783         e = MovingOrBlocked2Element(ex, ey);
10784
10785       is_empty = (IS_FREE(ex, ey) ||
10786                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10787
10788       is_walkable     = (is_empty || IS_WALKABLE(e));
10789       is_diggable     = (is_empty || IS_DIGGABLE(e));
10790       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10791       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10792       is_removable    = (is_diggable || is_collectible);
10793
10794       can_replace[xx][yy] =
10795         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10796           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10797           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10798           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10799           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10800           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10801          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10802
10803       if (!can_replace[xx][yy])
10804         complete_replace = FALSE;
10805     }
10806
10807     if (!change->only_if_complete || complete_replace)
10808     {
10809       boolean something_has_changed = FALSE;
10810
10811       if (change->only_if_complete && change->use_random_replace &&
10812           RND(100) < change->random_percentage)
10813         return FALSE;
10814
10815       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10816       {
10817         int ex = x + xx - 1;
10818         int ey = y + yy - 1;
10819         int content_element;
10820
10821         if (can_replace[xx][yy] && (!change->use_random_replace ||
10822                                     RND(100) < change->random_percentage))
10823         {
10824           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10825             RemoveMovingField(ex, ey);
10826
10827           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10828
10829           content_element = change->target_content.e[xx][yy];
10830           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10831                                               ce_value, ce_score);
10832
10833           CreateElementFromChange(ex, ey, target_element);
10834
10835           something_has_changed = TRUE;
10836
10837           // for symmetry reasons, freeze newly created border elements
10838           if (ex != x || ey != y)
10839             Stop[ex][ey] = TRUE;        // no more moving in this frame
10840         }
10841       }
10842
10843       if (something_has_changed)
10844       {
10845         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10846         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10847       }
10848     }
10849   }
10850   else
10851   {
10852     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10853                                         ce_value, ce_score);
10854
10855     if (element == EL_DIAGONAL_GROWING ||
10856         element == EL_DIAGONAL_SHRINKING)
10857     {
10858       target_element = Store[x][y];
10859
10860       Store[x][y] = EL_EMPTY;
10861     }
10862
10863     // special case: element changes to player (and may be kept if walkable)
10864     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10865       CreateElementFromChange(x, y, EL_EMPTY);
10866
10867     CreateElementFromChange(x, y, target_element);
10868
10869     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10870     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10871   }
10872
10873   // this uses direct change before indirect change
10874   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10875
10876   return TRUE;
10877 }
10878
10879 static void HandleElementChange(int x, int y, int page)
10880 {
10881   int element = MovingOrBlocked2Element(x, y);
10882   struct ElementInfo *ei = &element_info[element];
10883   struct ElementChangeInfo *change = &ei->change_page[page];
10884   boolean handle_action_before_change = FALSE;
10885
10886 #ifdef DEBUG
10887   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10888       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10889   {
10890     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10891           x, y, element, element_info[element].token_name);
10892     Debug("game:playing:HandleElementChange", "This should never happen!");
10893   }
10894 #endif
10895
10896   // this can happen with classic bombs on walkable, changing elements
10897   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10898   {
10899     return;
10900   }
10901
10902   if (ChangeDelay[x][y] == 0)           // initialize element change
10903   {
10904     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10905
10906     if (change->can_change)
10907     {
10908       // !!! not clear why graphic animation should be reset at all here !!!
10909       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10910       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10911
10912       /*
10913         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10914
10915         When using an animation frame delay of 1 (this only happens with
10916         "sp_zonk.moving.left/right" in the classic graphics), the default
10917         (non-moving) animation shows wrong animation frames (while the
10918         moving animation, like "sp_zonk.moving.left/right", is correct,
10919         so this graphical bug never shows up with the classic graphics).
10920         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10921         be drawn instead of the correct frames 0,1,2,3. This is caused by
10922         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10923         an element change: First when the change delay ("ChangeDelay[][]")
10924         counter has reached zero after decrementing, then a second time in
10925         the next frame (after "GfxFrame[][]" was already incremented) when
10926         "ChangeDelay[][]" is reset to the initial delay value again.
10927
10928         This causes frame 0 to be drawn twice, while the last frame won't
10929         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10930
10931         As some animations may already be cleverly designed around this bug
10932         (at least the "Snake Bite" snake tail animation does this), it cannot
10933         simply be fixed here without breaking such existing animations.
10934         Unfortunately, it cannot easily be detected if a graphics set was
10935         designed "before" or "after" the bug was fixed. As a workaround,
10936         a new graphics set option "game.graphics_engine_version" was added
10937         to be able to specify the game's major release version for which the
10938         graphics set was designed, which can then be used to decide if the
10939         bugfix should be used (version 4 and above) or not (version 3 or
10940         below, or if no version was specified at all, as with old sets).
10941
10942         (The wrong/fixed animation frames can be tested with the test level set
10943         "test_gfxframe" and level "000", which contains a specially prepared
10944         custom element at level position (x/y) == (11/9) which uses the zonk
10945         animation mentioned above. Using "game.graphics_engine_version: 4"
10946         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10947         This can also be seen from the debug output for this test element.)
10948       */
10949
10950       // when a custom element is about to change (for example by change delay),
10951       // do not reset graphic animation when the custom element is moving
10952       if (game.graphics_engine_version < 4 &&
10953           !IS_MOVING(x, y))
10954       {
10955         ResetGfxAnimation(x, y);
10956         ResetRandomAnimationValue(x, y);
10957       }
10958
10959       if (change->pre_change_function)
10960         change->pre_change_function(x, y);
10961     }
10962   }
10963
10964   ChangeDelay[x][y]--;
10965
10966   if (ChangeDelay[x][y] != 0)           // continue element change
10967   {
10968     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10969
10970     // also needed if CE can not change, but has CE delay with CE action
10971     if (IS_ANIMATED(graphic))
10972       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10973
10974     if (change->can_change)
10975     {
10976       if (change->change_function)
10977         change->change_function(x, y);
10978     }
10979   }
10980   else                                  // finish element change
10981   {
10982     if (ChangePage[x][y] != -1)         // remember page from delayed change
10983     {
10984       page = ChangePage[x][y];
10985       ChangePage[x][y] = -1;
10986
10987       change = &ei->change_page[page];
10988     }
10989
10990     if (IS_MOVING(x, y))                // never change a running system ;-)
10991     {
10992       ChangeDelay[x][y] = 1;            // try change after next move step
10993       ChangePage[x][y] = page;          // remember page to use for change
10994
10995       return;
10996     }
10997
10998     // special case: set new level random seed before changing element
10999     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11000       handle_action_before_change = TRUE;
11001
11002     if (change->has_action && handle_action_before_change)
11003       ExecuteCustomElementAction(x, y, element, page);
11004
11005     if (change->can_change)
11006     {
11007       if (ChangeElement(x, y, element, page))
11008       {
11009         if (change->post_change_function)
11010           change->post_change_function(x, y);
11011       }
11012     }
11013
11014     if (change->has_action && !handle_action_before_change)
11015       ExecuteCustomElementAction(x, y, element, page);
11016   }
11017 }
11018
11019 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11020                                               int trigger_element,
11021                                               int trigger_event,
11022                                               int trigger_player,
11023                                               int trigger_side,
11024                                               int trigger_page)
11025 {
11026   boolean change_done_any = FALSE;
11027   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11028   int i;
11029
11030   if (!(trigger_events[trigger_element][trigger_event]))
11031     return FALSE;
11032
11033   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11034
11035   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11036   {
11037     int element = EL_CUSTOM_START + i;
11038     boolean change_done = FALSE;
11039     int p;
11040
11041     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11042         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11043       continue;
11044
11045     for (p = 0; p < element_info[element].num_change_pages; p++)
11046     {
11047       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11048
11049       if (change->can_change_or_has_action &&
11050           change->has_event[trigger_event] &&
11051           change->trigger_side & trigger_side &&
11052           change->trigger_player & trigger_player &&
11053           change->trigger_page & trigger_page_bits &&
11054           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11055       {
11056         change->actual_trigger_element = trigger_element;
11057         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11058         change->actual_trigger_player_bits = trigger_player;
11059         change->actual_trigger_side = trigger_side;
11060         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11061         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11062
11063         if ((change->can_change && !change_done) || change->has_action)
11064         {
11065           int x, y;
11066
11067           SCAN_PLAYFIELD(x, y)
11068           {
11069             if (Tile[x][y] == element)
11070             {
11071               if (change->can_change && !change_done)
11072               {
11073                 // if element already changed in this frame, not only prevent
11074                 // another element change (checked in ChangeElement()), but
11075                 // also prevent additional element actions for this element
11076
11077                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11078                     !level.use_action_after_change_bug)
11079                   continue;
11080
11081                 ChangeDelay[x][y] = 1;
11082                 ChangeEvent[x][y] = trigger_event;
11083
11084                 HandleElementChange(x, y, p);
11085               }
11086               else if (change->has_action)
11087               {
11088                 // if element already changed in this frame, not only prevent
11089                 // another element change (checked in ChangeElement()), but
11090                 // also prevent additional element actions for this element
11091
11092                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11093                     !level.use_action_after_change_bug)
11094                   continue;
11095
11096                 ExecuteCustomElementAction(x, y, element, p);
11097                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11098               }
11099             }
11100           }
11101
11102           if (change->can_change)
11103           {
11104             change_done = TRUE;
11105             change_done_any = TRUE;
11106           }
11107         }
11108       }
11109     }
11110   }
11111
11112   RECURSION_LOOP_DETECTION_END();
11113
11114   return change_done_any;
11115 }
11116
11117 static boolean CheckElementChangeExt(int x, int y,
11118                                      int element,
11119                                      int trigger_element,
11120                                      int trigger_event,
11121                                      int trigger_player,
11122                                      int trigger_side)
11123 {
11124   boolean change_done = FALSE;
11125   int p;
11126
11127   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11128       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11129     return FALSE;
11130
11131   if (Tile[x][y] == EL_BLOCKED)
11132   {
11133     Blocked2Moving(x, y, &x, &y);
11134     element = Tile[x][y];
11135   }
11136
11137   // check if element has already changed or is about to change after moving
11138   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11139        Tile[x][y] != element) ||
11140
11141       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11142        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11143         ChangePage[x][y] != -1)))
11144     return FALSE;
11145
11146   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11147
11148   for (p = 0; p < element_info[element].num_change_pages; p++)
11149   {
11150     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11151
11152     /* check trigger element for all events where the element that is checked
11153        for changing interacts with a directly adjacent element -- this is
11154        different to element changes that affect other elements to change on the
11155        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11156     boolean check_trigger_element =
11157       (trigger_event == CE_NEXT_TO_X ||
11158        trigger_event == CE_TOUCHING_X ||
11159        trigger_event == CE_HITTING_X ||
11160        trigger_event == CE_HIT_BY_X ||
11161        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11162
11163     if (change->can_change_or_has_action &&
11164         change->has_event[trigger_event] &&
11165         change->trigger_side & trigger_side &&
11166         change->trigger_player & trigger_player &&
11167         (!check_trigger_element ||
11168          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11169     {
11170       change->actual_trigger_element = trigger_element;
11171       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11172       change->actual_trigger_player_bits = trigger_player;
11173       change->actual_trigger_side = trigger_side;
11174       change->actual_trigger_ce_value = CustomValue[x][y];
11175       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11176
11177       // special case: trigger element not at (x,y) position for some events
11178       if (check_trigger_element)
11179       {
11180         static struct
11181         {
11182           int dx, dy;
11183         } move_xy[] =
11184           {
11185             {  0,  0 },
11186             { -1,  0 },
11187             { +1,  0 },
11188             {  0,  0 },
11189             {  0, -1 },
11190             {  0,  0 }, { 0, 0 }, { 0, 0 },
11191             {  0, +1 }
11192           };
11193
11194         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11195         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11196
11197         change->actual_trigger_ce_value = CustomValue[xx][yy];
11198         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11199       }
11200
11201       if (change->can_change && !change_done)
11202       {
11203         ChangeDelay[x][y] = 1;
11204         ChangeEvent[x][y] = trigger_event;
11205
11206         HandleElementChange(x, y, p);
11207
11208         change_done = TRUE;
11209       }
11210       else if (change->has_action)
11211       {
11212         ExecuteCustomElementAction(x, y, element, p);
11213         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11214       }
11215     }
11216   }
11217
11218   RECURSION_LOOP_DETECTION_END();
11219
11220   return change_done;
11221 }
11222
11223 static void PlayPlayerSound(struct PlayerInfo *player)
11224 {
11225   int jx = player->jx, jy = player->jy;
11226   int sound_element = player->artwork_element;
11227   int last_action = player->last_action_waiting;
11228   int action = player->action_waiting;
11229
11230   if (player->is_waiting)
11231   {
11232     if (action != last_action)
11233       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11234     else
11235       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11236   }
11237   else
11238   {
11239     if (action != last_action)
11240       StopSound(element_info[sound_element].sound[last_action]);
11241
11242     if (last_action == ACTION_SLEEPING)
11243       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11244   }
11245 }
11246
11247 static void PlayAllPlayersSound(void)
11248 {
11249   int i;
11250
11251   for (i = 0; i < MAX_PLAYERS; i++)
11252     if (stored_player[i].active)
11253       PlayPlayerSound(&stored_player[i]);
11254 }
11255
11256 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11257 {
11258   boolean last_waiting = player->is_waiting;
11259   int move_dir = player->MovDir;
11260
11261   player->dir_waiting = move_dir;
11262   player->last_action_waiting = player->action_waiting;
11263
11264   if (is_waiting)
11265   {
11266     if (!last_waiting)          // not waiting -> waiting
11267     {
11268       player->is_waiting = TRUE;
11269
11270       player->frame_counter_bored =
11271         FrameCounter +
11272         game.player_boring_delay_fixed +
11273         GetSimpleRandom(game.player_boring_delay_random);
11274       player->frame_counter_sleeping =
11275         FrameCounter +
11276         game.player_sleeping_delay_fixed +
11277         GetSimpleRandom(game.player_sleeping_delay_random);
11278
11279       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11280     }
11281
11282     if (game.player_sleeping_delay_fixed +
11283         game.player_sleeping_delay_random > 0 &&
11284         player->anim_delay_counter == 0 &&
11285         player->post_delay_counter == 0 &&
11286         FrameCounter >= player->frame_counter_sleeping)
11287       player->is_sleeping = TRUE;
11288     else if (game.player_boring_delay_fixed +
11289              game.player_boring_delay_random > 0 &&
11290              FrameCounter >= player->frame_counter_bored)
11291       player->is_bored = TRUE;
11292
11293     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11294                               player->is_bored ? ACTION_BORING :
11295                               ACTION_WAITING);
11296
11297     if (player->is_sleeping && player->use_murphy)
11298     {
11299       // special case for sleeping Murphy when leaning against non-free tile
11300
11301       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11302           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11303            !IS_MOVING(player->jx - 1, player->jy)))
11304         move_dir = MV_LEFT;
11305       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11306                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11307                 !IS_MOVING(player->jx + 1, player->jy)))
11308         move_dir = MV_RIGHT;
11309       else
11310         player->is_sleeping = FALSE;
11311
11312       player->dir_waiting = move_dir;
11313     }
11314
11315     if (player->is_sleeping)
11316     {
11317       if (player->num_special_action_sleeping > 0)
11318       {
11319         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11320         {
11321           int last_special_action = player->special_action_sleeping;
11322           int num_special_action = player->num_special_action_sleeping;
11323           int special_action =
11324             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11325              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11326              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11327              last_special_action + 1 : ACTION_SLEEPING);
11328           int special_graphic =
11329             el_act_dir2img(player->artwork_element, special_action, move_dir);
11330
11331           player->anim_delay_counter =
11332             graphic_info[special_graphic].anim_delay_fixed +
11333             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11334           player->post_delay_counter =
11335             graphic_info[special_graphic].post_delay_fixed +
11336             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11337
11338           player->special_action_sleeping = special_action;
11339         }
11340
11341         if (player->anim_delay_counter > 0)
11342         {
11343           player->action_waiting = player->special_action_sleeping;
11344           player->anim_delay_counter--;
11345         }
11346         else if (player->post_delay_counter > 0)
11347         {
11348           player->post_delay_counter--;
11349         }
11350       }
11351     }
11352     else if (player->is_bored)
11353     {
11354       if (player->num_special_action_bored > 0)
11355       {
11356         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11357         {
11358           int special_action =
11359             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11360           int special_graphic =
11361             el_act_dir2img(player->artwork_element, special_action, move_dir);
11362
11363           player->anim_delay_counter =
11364             graphic_info[special_graphic].anim_delay_fixed +
11365             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11366           player->post_delay_counter =
11367             graphic_info[special_graphic].post_delay_fixed +
11368             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11369
11370           player->special_action_bored = special_action;
11371         }
11372
11373         if (player->anim_delay_counter > 0)
11374         {
11375           player->action_waiting = player->special_action_bored;
11376           player->anim_delay_counter--;
11377         }
11378         else if (player->post_delay_counter > 0)
11379         {
11380           player->post_delay_counter--;
11381         }
11382       }
11383     }
11384   }
11385   else if (last_waiting)        // waiting -> not waiting
11386   {
11387     player->is_waiting = FALSE;
11388     player->is_bored = FALSE;
11389     player->is_sleeping = FALSE;
11390
11391     player->frame_counter_bored = -1;
11392     player->frame_counter_sleeping = -1;
11393
11394     player->anim_delay_counter = 0;
11395     player->post_delay_counter = 0;
11396
11397     player->dir_waiting = player->MovDir;
11398     player->action_waiting = ACTION_DEFAULT;
11399
11400     player->special_action_bored = ACTION_DEFAULT;
11401     player->special_action_sleeping = ACTION_DEFAULT;
11402   }
11403 }
11404
11405 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11406 {
11407   if ((!player->is_moving  && player->was_moving) ||
11408       (player->MovPos == 0 && player->was_moving) ||
11409       (player->is_snapping && !player->was_snapping) ||
11410       (player->is_dropping && !player->was_dropping))
11411   {
11412     if (!CheckSaveEngineSnapshotToList())
11413       return;
11414
11415     player->was_moving = FALSE;
11416     player->was_snapping = TRUE;
11417     player->was_dropping = TRUE;
11418   }
11419   else
11420   {
11421     if (player->is_moving)
11422       player->was_moving = TRUE;
11423
11424     if (!player->is_snapping)
11425       player->was_snapping = FALSE;
11426
11427     if (!player->is_dropping)
11428       player->was_dropping = FALSE;
11429   }
11430
11431   static struct MouseActionInfo mouse_action_last = { 0 };
11432   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11433   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11434
11435   if (new_released)
11436     CheckSaveEngineSnapshotToList();
11437
11438   mouse_action_last = mouse_action;
11439 }
11440
11441 static void CheckSingleStepMode(struct PlayerInfo *player)
11442 {
11443   if (tape.single_step && tape.recording && !tape.pausing)
11444   {
11445     // as it is called "single step mode", just return to pause mode when the
11446     // player stopped moving after one tile (or never starts moving at all)
11447     // (reverse logic needed here in case single step mode used in team mode)
11448     if (player->is_moving ||
11449         player->is_pushing ||
11450         player->is_dropping_pressed ||
11451         player->effective_mouse_action.button)
11452       game.enter_single_step_mode = FALSE;
11453   }
11454
11455   CheckSaveEngineSnapshot(player);
11456 }
11457
11458 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11459 {
11460   int left      = player_action & JOY_LEFT;
11461   int right     = player_action & JOY_RIGHT;
11462   int up        = player_action & JOY_UP;
11463   int down      = player_action & JOY_DOWN;
11464   int button1   = player_action & JOY_BUTTON_1;
11465   int button2   = player_action & JOY_BUTTON_2;
11466   int dx        = (left ? -1 : right ? 1 : 0);
11467   int dy        = (up   ? -1 : down  ? 1 : 0);
11468
11469   if (!player->active || tape.pausing)
11470     return 0;
11471
11472   if (player_action)
11473   {
11474     if (button1)
11475       SnapField(player, dx, dy);
11476     else
11477     {
11478       if (button2)
11479         DropElement(player);
11480
11481       MovePlayer(player, dx, dy);
11482     }
11483
11484     CheckSingleStepMode(player);
11485
11486     SetPlayerWaiting(player, FALSE);
11487
11488     return player_action;
11489   }
11490   else
11491   {
11492     // no actions for this player (no input at player's configured device)
11493
11494     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11495     SnapField(player, 0, 0);
11496     CheckGravityMovementWhenNotMoving(player);
11497
11498     if (player->MovPos == 0)
11499       SetPlayerWaiting(player, TRUE);
11500
11501     if (player->MovPos == 0)    // needed for tape.playing
11502       player->is_moving = FALSE;
11503
11504     player->is_dropping = FALSE;
11505     player->is_dropping_pressed = FALSE;
11506     player->drop_pressed_delay = 0;
11507
11508     CheckSingleStepMode(player);
11509
11510     return 0;
11511   }
11512 }
11513
11514 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11515                                          byte *tape_action)
11516 {
11517   if (!tape.use_mouse_actions)
11518     return;
11519
11520   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11521   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11522   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11523 }
11524
11525 static void SetTapeActionFromMouseAction(byte *tape_action,
11526                                          struct MouseActionInfo *mouse_action)
11527 {
11528   if (!tape.use_mouse_actions)
11529     return;
11530
11531   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11532   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11533   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11534 }
11535
11536 static void CheckLevelSolved(void)
11537 {
11538   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11539   {
11540     if (game_em.level_solved &&
11541         !game_em.game_over)                             // game won
11542     {
11543       LevelSolved();
11544
11545       game_em.game_over = TRUE;
11546
11547       game.all_players_gone = TRUE;
11548     }
11549
11550     if (game_em.game_over)                              // game lost
11551       game.all_players_gone = TRUE;
11552   }
11553   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11554   {
11555     if (game_sp.level_solved &&
11556         !game_sp.game_over)                             // game won
11557     {
11558       LevelSolved();
11559
11560       game_sp.game_over = TRUE;
11561
11562       game.all_players_gone = TRUE;
11563     }
11564
11565     if (game_sp.game_over)                              // game lost
11566       game.all_players_gone = TRUE;
11567   }
11568   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11569   {
11570     if (game_mm.level_solved &&
11571         !game_mm.game_over)                             // game won
11572     {
11573       LevelSolved();
11574
11575       game_mm.game_over = TRUE;
11576
11577       game.all_players_gone = TRUE;
11578     }
11579
11580     if (game_mm.game_over)                              // game lost
11581       game.all_players_gone = TRUE;
11582   }
11583 }
11584
11585 static void CheckLevelTime_StepCounter(void)
11586 {
11587   int i;
11588
11589   TimePlayed++;
11590
11591   if (TimeLeft > 0)
11592   {
11593     TimeLeft--;
11594
11595     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11596       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11597
11598     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11599
11600     DisplayGameControlValues();
11601
11602     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11603       for (i = 0; i < MAX_PLAYERS; i++)
11604         KillPlayer(&stored_player[i]);
11605   }
11606   else if (game.no_level_time_limit && !game.all_players_gone)
11607   {
11608     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11609
11610     DisplayGameControlValues();
11611   }
11612 }
11613
11614 static void CheckLevelTime(void)
11615 {
11616   int i;
11617
11618   if (TimeFrames >= FRAMES_PER_SECOND)
11619   {
11620     TimeFrames = 0;
11621     TapeTime++;
11622
11623     for (i = 0; i < MAX_PLAYERS; i++)
11624     {
11625       struct PlayerInfo *player = &stored_player[i];
11626
11627       if (SHIELD_ON(player))
11628       {
11629         player->shield_normal_time_left--;
11630
11631         if (player->shield_deadly_time_left > 0)
11632           player->shield_deadly_time_left--;
11633       }
11634     }
11635
11636     if (!game.LevelSolved && !level.use_step_counter)
11637     {
11638       TimePlayed++;
11639
11640       if (TimeLeft > 0)
11641       {
11642         TimeLeft--;
11643
11644         if (TimeLeft <= 10 && game.time_limit)
11645           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11646
11647         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11648            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11649
11650         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11651
11652         if (!TimeLeft && game.time_limit)
11653         {
11654           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11655             game_em.lev->killed_out_of_time = TRUE;
11656           else
11657             for (i = 0; i < MAX_PLAYERS; i++)
11658               KillPlayer(&stored_player[i]);
11659         }
11660       }
11661       else if (game.no_level_time_limit && !game.all_players_gone)
11662       {
11663         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11664       }
11665
11666       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11667     }
11668
11669     if (tape.recording || tape.playing)
11670       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11671   }
11672
11673   if (tape.recording || tape.playing)
11674     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11675
11676   UpdateAndDisplayGameControlValues();
11677 }
11678
11679 void AdvanceFrameAndPlayerCounters(int player_nr)
11680 {
11681   int i;
11682
11683   // advance frame counters (global frame counter and time frame counter)
11684   FrameCounter++;
11685   TimeFrames++;
11686
11687   // advance player counters (counters for move delay, move animation etc.)
11688   for (i = 0; i < MAX_PLAYERS; i++)
11689   {
11690     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11691     int move_delay_value = stored_player[i].move_delay_value;
11692     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11693
11694     if (!advance_player_counters)       // not all players may be affected
11695       continue;
11696
11697     if (move_frames == 0)       // less than one move per game frame
11698     {
11699       int stepsize = TILEX / move_delay_value;
11700       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11701       int count = (stored_player[i].is_moving ?
11702                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11703
11704       if (count % delay == 0)
11705         move_frames = 1;
11706     }
11707
11708     stored_player[i].Frame += move_frames;
11709
11710     if (stored_player[i].MovPos != 0)
11711       stored_player[i].StepFrame += move_frames;
11712
11713     if (stored_player[i].move_delay > 0)
11714       stored_player[i].move_delay--;
11715
11716     // due to bugs in previous versions, counter must count up, not down
11717     if (stored_player[i].push_delay != -1)
11718       stored_player[i].push_delay++;
11719
11720     if (stored_player[i].drop_delay > 0)
11721       stored_player[i].drop_delay--;
11722
11723     if (stored_player[i].is_dropping_pressed)
11724       stored_player[i].drop_pressed_delay++;
11725   }
11726 }
11727
11728 void AdvanceFrameCounter(void)
11729 {
11730   FrameCounter++;
11731 }
11732
11733 void AdvanceGfxFrame(void)
11734 {
11735   int x, y;
11736
11737   SCAN_PLAYFIELD(x, y)
11738   {
11739     GfxFrame[x][y]++;
11740   }
11741 }
11742
11743 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11744                               struct MouseActionInfo *mouse_action_last)
11745 {
11746   if (mouse_action->button)
11747   {
11748     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11749     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11750     int x = mouse_action->lx;
11751     int y = mouse_action->ly;
11752     int element = Tile[x][y];
11753
11754     if (new_button)
11755     {
11756       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11757       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11758                                          ch_button);
11759     }
11760
11761     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11762     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11763                                        ch_button);
11764
11765     if (level.use_step_counter)
11766     {
11767       boolean counted_click = FALSE;
11768
11769       // element clicked that can change when clicked/pressed
11770       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11771           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11772            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11773         counted_click = TRUE;
11774
11775       // element clicked that can trigger change when clicked/pressed
11776       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11777           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11778         counted_click = TRUE;
11779
11780       if (new_button && counted_click)
11781         CheckLevelTime_StepCounter();
11782     }
11783   }
11784 }
11785
11786 void StartGameActions(boolean init_network_game, boolean record_tape,
11787                       int random_seed)
11788 {
11789   unsigned int new_random_seed = InitRND(random_seed);
11790
11791   if (record_tape)
11792     TapeStartRecording(new_random_seed);
11793
11794   if (setup.auto_pause_on_start && !tape.pausing)
11795     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11796
11797   if (init_network_game)
11798   {
11799     SendToServer_LevelFile();
11800     SendToServer_StartPlaying();
11801
11802     return;
11803   }
11804
11805   InitGame();
11806 }
11807
11808 static void GameActionsExt(void)
11809 {
11810 #if 0
11811   static unsigned int game_frame_delay = 0;
11812 #endif
11813   unsigned int game_frame_delay_value;
11814   byte *recorded_player_action;
11815   byte summarized_player_action = 0;
11816   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11817   int i;
11818
11819   // detect endless loops, caused by custom element programming
11820   if (recursion_loop_detected && recursion_loop_depth == 0)
11821   {
11822     char *message = getStringCat3("Internal Error! Element ",
11823                                   EL_NAME(recursion_loop_element),
11824                                   " caused endless loop! Quit the game?");
11825
11826     Warn("element '%s' caused endless loop in game engine",
11827          EL_NAME(recursion_loop_element));
11828
11829     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11830
11831     recursion_loop_detected = FALSE;    // if game should be continued
11832
11833     free(message);
11834
11835     return;
11836   }
11837
11838   if (game.restart_level)
11839     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11840
11841   CheckLevelSolved();
11842
11843   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11844     GameWon();
11845
11846   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11847     TapeStop();
11848
11849   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11850     return;
11851
11852   game_frame_delay_value =
11853     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11854
11855   if (tape.playing && tape.warp_forward && !tape.pausing)
11856     game_frame_delay_value = 0;
11857
11858   SetVideoFrameDelay(game_frame_delay_value);
11859
11860   // (de)activate virtual buttons depending on current game status
11861   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11862   {
11863     if (game.all_players_gone)  // if no players there to be controlled anymore
11864       SetOverlayActive(FALSE);
11865     else if (!tape.playing)     // if game continues after tape stopped playing
11866       SetOverlayActive(TRUE);
11867   }
11868
11869 #if 0
11870 #if 0
11871   // ---------- main game synchronization point ----------
11872
11873   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11874
11875   Debug("game:playing:skip", "skip == %d", skip);
11876
11877 #else
11878   // ---------- main game synchronization point ----------
11879
11880   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11881 #endif
11882 #endif
11883
11884   if (network_playing && !network_player_action_received)
11885   {
11886     // try to get network player actions in time
11887
11888     // last chance to get network player actions without main loop delay
11889     HandleNetworking();
11890
11891     // game was quit by network peer
11892     if (game_status != GAME_MODE_PLAYING)
11893       return;
11894
11895     // check if network player actions still missing and game still running
11896     if (!network_player_action_received && !checkGameEnded())
11897       return;           // failed to get network player actions in time
11898
11899     // do not yet reset "network_player_action_received" (for tape.pausing)
11900   }
11901
11902   if (tape.pausing)
11903     return;
11904
11905   // at this point we know that we really continue executing the game
11906
11907   network_player_action_received = FALSE;
11908
11909   // when playing tape, read previously recorded player input from tape data
11910   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11911
11912   local_player->effective_mouse_action = local_player->mouse_action;
11913
11914   if (recorded_player_action != NULL)
11915     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11916                                  recorded_player_action);
11917
11918   // TapePlayAction() may return NULL when toggling to "pause before death"
11919   if (tape.pausing)
11920     return;
11921
11922   if (tape.set_centered_player)
11923   {
11924     game.centered_player_nr_next = tape.centered_player_nr_next;
11925     game.set_centered_player = TRUE;
11926   }
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929   {
11930     summarized_player_action |= stored_player[i].action;
11931
11932     if (!network_playing && (game.team_mode || tape.playing))
11933       stored_player[i].effective_action = stored_player[i].action;
11934   }
11935
11936   if (network_playing && !checkGameEnded())
11937     SendToServer_MovePlayer(summarized_player_action);
11938
11939   // summarize all actions at local players mapped input device position
11940   // (this allows using different input devices in single player mode)
11941   if (!network.enabled && !game.team_mode)
11942     stored_player[map_player_action[local_player->index_nr]].effective_action =
11943       summarized_player_action;
11944
11945   // summarize all actions at centered player in local team mode
11946   if (tape.recording &&
11947       setup.team_mode && !network.enabled &&
11948       setup.input_on_focus &&
11949       game.centered_player_nr != -1)
11950   {
11951     for (i = 0; i < MAX_PLAYERS; i++)
11952       stored_player[map_player_action[i]].effective_action =
11953         (i == game.centered_player_nr ? summarized_player_action : 0);
11954   }
11955
11956   if (recorded_player_action != NULL)
11957     for (i = 0; i < MAX_PLAYERS; i++)
11958       stored_player[i].effective_action = recorded_player_action[i];
11959
11960   for (i = 0; i < MAX_PLAYERS; i++)
11961   {
11962     tape_action[i] = stored_player[i].effective_action;
11963
11964     /* (this may happen in the RND game engine if a player was not present on
11965        the playfield on level start, but appeared later from a custom element */
11966     if (setup.team_mode &&
11967         tape.recording &&
11968         tape_action[i] &&
11969         !tape.player_participates[i])
11970       tape.player_participates[i] = TRUE;
11971   }
11972
11973   SetTapeActionFromMouseAction(tape_action,
11974                                &local_player->effective_mouse_action);
11975
11976   // only record actions from input devices, but not programmed actions
11977   if (tape.recording)
11978     TapeRecordAction(tape_action);
11979
11980   // remember if game was played (especially after tape stopped playing)
11981   if (!tape.playing && summarized_player_action && !checkGameFailed())
11982     game.GamePlayed = TRUE;
11983
11984 #if USE_NEW_PLAYER_ASSIGNMENTS
11985   // !!! also map player actions in single player mode !!!
11986   // if (game.team_mode)
11987   if (1)
11988   {
11989     byte mapped_action[MAX_PLAYERS];
11990
11991 #if DEBUG_PLAYER_ACTIONS
11992     for (i = 0; i < MAX_PLAYERS; i++)
11993       DebugContinued("", "%d, ", stored_player[i].effective_action);
11994 #endif
11995
11996     for (i = 0; i < MAX_PLAYERS; i++)
11997       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11998
11999     for (i = 0; i < MAX_PLAYERS; i++)
12000       stored_player[i].effective_action = mapped_action[i];
12001
12002 #if DEBUG_PLAYER_ACTIONS
12003     DebugContinued("", "=> ");
12004     for (i = 0; i < MAX_PLAYERS; i++)
12005       DebugContinued("", "%d, ", stored_player[i].effective_action);
12006     DebugContinued("game:playing:player", "\n");
12007 #endif
12008   }
12009 #if DEBUG_PLAYER_ACTIONS
12010   else
12011   {
12012     for (i = 0; i < MAX_PLAYERS; i++)
12013       DebugContinued("", "%d, ", stored_player[i].effective_action);
12014     DebugContinued("game:playing:player", "\n");
12015   }
12016 #endif
12017 #endif
12018
12019   for (i = 0; i < MAX_PLAYERS; i++)
12020   {
12021     // allow engine snapshot in case of changed movement attempt
12022     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12023         (stored_player[i].effective_action & KEY_MOTION))
12024       game.snapshot.changed_action = TRUE;
12025
12026     // allow engine snapshot in case of snapping/dropping attempt
12027     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12028         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12029       game.snapshot.changed_action = TRUE;
12030
12031     game.snapshot.last_action[i] = stored_player[i].effective_action;
12032   }
12033
12034   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12035   {
12036     GameActions_EM_Main();
12037   }
12038   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12039   {
12040     GameActions_SP_Main();
12041   }
12042   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12043   {
12044     GameActions_MM_Main();
12045   }
12046   else
12047   {
12048     GameActions_RND_Main();
12049   }
12050
12051   BlitScreenToBitmap(backbuffer);
12052
12053   CheckLevelSolved();
12054   CheckLevelTime();
12055
12056   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12057
12058   if (global.show_frames_per_second)
12059   {
12060     static unsigned int fps_counter = 0;
12061     static int fps_frames = 0;
12062     unsigned int fps_delay_ms = Counter() - fps_counter;
12063
12064     fps_frames++;
12065
12066     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12067     {
12068       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12069
12070       fps_frames = 0;
12071       fps_counter = Counter();
12072
12073       // always draw FPS to screen after FPS value was updated
12074       redraw_mask |= REDRAW_FPS;
12075     }
12076
12077     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12078     if (GetDrawDeactivationMask() == REDRAW_NONE)
12079       redraw_mask |= REDRAW_FPS;
12080   }
12081 }
12082
12083 static void GameActions_CheckSaveEngineSnapshot(void)
12084 {
12085   if (!game.snapshot.save_snapshot)
12086     return;
12087
12088   // clear flag for saving snapshot _before_ saving snapshot
12089   game.snapshot.save_snapshot = FALSE;
12090
12091   SaveEngineSnapshotToList();
12092 }
12093
12094 void GameActions(void)
12095 {
12096   GameActionsExt();
12097
12098   GameActions_CheckSaveEngineSnapshot();
12099 }
12100
12101 void GameActions_EM_Main(void)
12102 {
12103   byte effective_action[MAX_PLAYERS];
12104   int i;
12105
12106   for (i = 0; i < MAX_PLAYERS; i++)
12107     effective_action[i] = stored_player[i].effective_action;
12108
12109   GameActions_EM(effective_action);
12110 }
12111
12112 void GameActions_SP_Main(void)
12113 {
12114   byte effective_action[MAX_PLAYERS];
12115   int i;
12116
12117   for (i = 0; i < MAX_PLAYERS; i++)
12118     effective_action[i] = stored_player[i].effective_action;
12119
12120   GameActions_SP(effective_action);
12121
12122   for (i = 0; i < MAX_PLAYERS; i++)
12123   {
12124     if (stored_player[i].force_dropping)
12125       stored_player[i].action |= KEY_BUTTON_DROP;
12126
12127     stored_player[i].force_dropping = FALSE;
12128   }
12129 }
12130
12131 void GameActions_MM_Main(void)
12132 {
12133   AdvanceGfxFrame();
12134
12135   GameActions_MM(local_player->effective_mouse_action);
12136 }
12137
12138 void GameActions_RND_Main(void)
12139 {
12140   GameActions_RND();
12141 }
12142
12143 void GameActions_RND(void)
12144 {
12145   static struct MouseActionInfo mouse_action_last = { 0 };
12146   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12147   int magic_wall_x = 0, magic_wall_y = 0;
12148   int i, x, y, element, graphic, last_gfx_frame;
12149
12150   InitPlayfieldScanModeVars();
12151
12152   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12153   {
12154     SCAN_PLAYFIELD(x, y)
12155     {
12156       ChangeCount[x][y] = 0;
12157       ChangeEvent[x][y] = -1;
12158     }
12159   }
12160
12161   if (game.set_centered_player)
12162   {
12163     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12164
12165     // switching to "all players" only possible if all players fit to screen
12166     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12167     {
12168       game.centered_player_nr_next = game.centered_player_nr;
12169       game.set_centered_player = FALSE;
12170     }
12171
12172     // do not switch focus to non-existing (or non-active) player
12173     if (game.centered_player_nr_next >= 0 &&
12174         !stored_player[game.centered_player_nr_next].active)
12175     {
12176       game.centered_player_nr_next = game.centered_player_nr;
12177       game.set_centered_player = FALSE;
12178     }
12179   }
12180
12181   if (game.set_centered_player &&
12182       ScreenMovPos == 0)        // screen currently aligned at tile position
12183   {
12184     int sx, sy;
12185
12186     if (game.centered_player_nr_next == -1)
12187     {
12188       setScreenCenteredToAllPlayers(&sx, &sy);
12189     }
12190     else
12191     {
12192       sx = stored_player[game.centered_player_nr_next].jx;
12193       sy = stored_player[game.centered_player_nr_next].jy;
12194     }
12195
12196     game.centered_player_nr = game.centered_player_nr_next;
12197     game.set_centered_player = FALSE;
12198
12199     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12200     DrawGameDoorValues();
12201   }
12202
12203   // check single step mode (set flag and clear again if any player is active)
12204   game.enter_single_step_mode =
12205     (tape.single_step && tape.recording && !tape.pausing);
12206
12207   for (i = 0; i < MAX_PLAYERS; i++)
12208   {
12209     int actual_player_action = stored_player[i].effective_action;
12210
12211 #if 1
12212     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12213        - rnd_equinox_tetrachloride 048
12214        - rnd_equinox_tetrachloride_ii 096
12215        - rnd_emanuel_schmieg 002
12216        - doctor_sloan_ww 001, 020
12217     */
12218     if (stored_player[i].MovPos == 0)
12219       CheckGravityMovement(&stored_player[i]);
12220 #endif
12221
12222     // overwrite programmed action with tape action
12223     if (stored_player[i].programmed_action)
12224       actual_player_action = stored_player[i].programmed_action;
12225
12226     PlayerActions(&stored_player[i], actual_player_action);
12227
12228     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12229   }
12230
12231   // single step pause mode may already have been toggled by "ScrollPlayer()"
12232   if (game.enter_single_step_mode && !tape.pausing)
12233     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12234
12235   ScrollScreen(NULL, SCROLL_GO_ON);
12236
12237   /* for backwards compatibility, the following code emulates a fixed bug that
12238      occured when pushing elements (causing elements that just made their last
12239      pushing step to already (if possible) make their first falling step in the
12240      same game frame, which is bad); this code is also needed to use the famous
12241      "spring push bug" which is used in older levels and might be wanted to be
12242      used also in newer levels, but in this case the buggy pushing code is only
12243      affecting the "spring" element and no other elements */
12244
12245   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12246   {
12247     for (i = 0; i < MAX_PLAYERS; i++)
12248     {
12249       struct PlayerInfo *player = &stored_player[i];
12250       int x = player->jx;
12251       int y = player->jy;
12252
12253       if (player->active && player->is_pushing && player->is_moving &&
12254           IS_MOVING(x, y) &&
12255           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12256            Tile[x][y] == EL_SPRING))
12257       {
12258         ContinueMoving(x, y);
12259
12260         // continue moving after pushing (this is actually a bug)
12261         if (!IS_MOVING(x, y))
12262           Stop[x][y] = FALSE;
12263       }
12264     }
12265   }
12266
12267   SCAN_PLAYFIELD(x, y)
12268   {
12269     Last[x][y] = Tile[x][y];
12270
12271     ChangeCount[x][y] = 0;
12272     ChangeEvent[x][y] = -1;
12273
12274     // this must be handled before main playfield loop
12275     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12276     {
12277       MovDelay[x][y]--;
12278       if (MovDelay[x][y] <= 0)
12279         RemoveField(x, y);
12280     }
12281
12282     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12283     {
12284       MovDelay[x][y]--;
12285       if (MovDelay[x][y] <= 0)
12286       {
12287         int element = Store[x][y];
12288         int move_direction = MovDir[x][y];
12289         int player_index_bit = Store2[x][y];
12290
12291         Store[x][y] = 0;
12292         Store2[x][y] = 0;
12293
12294         RemoveField(x, y);
12295         TEST_DrawLevelField(x, y);
12296
12297         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12298
12299         if (IS_ENVELOPE(element))
12300           local_player->show_envelope = element;
12301       }
12302     }
12303
12304 #if DEBUG
12305     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12306     {
12307       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12308             x, y);
12309       Debug("game:playing:GameActions_RND", "This should never happen!");
12310
12311       ChangePage[x][y] = -1;
12312     }
12313 #endif
12314
12315     Stop[x][y] = FALSE;
12316     if (WasJustMoving[x][y] > 0)
12317       WasJustMoving[x][y]--;
12318     if (WasJustFalling[x][y] > 0)
12319       WasJustFalling[x][y]--;
12320     if (CheckCollision[x][y] > 0)
12321       CheckCollision[x][y]--;
12322     if (CheckImpact[x][y] > 0)
12323       CheckImpact[x][y]--;
12324
12325     GfxFrame[x][y]++;
12326
12327     /* reset finished pushing action (not done in ContinueMoving() to allow
12328        continuous pushing animation for elements with zero push delay) */
12329     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12330     {
12331       ResetGfxAnimation(x, y);
12332       TEST_DrawLevelField(x, y);
12333     }
12334
12335 #if DEBUG
12336     if (IS_BLOCKED(x, y))
12337     {
12338       int oldx, oldy;
12339
12340       Blocked2Moving(x, y, &oldx, &oldy);
12341       if (!IS_MOVING(oldx, oldy))
12342       {
12343         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12344         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12345         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12346         Debug("game:playing:GameActions_RND", "This should never happen!");
12347       }
12348     }
12349 #endif
12350   }
12351
12352   HandleMouseAction(&mouse_action, &mouse_action_last);
12353
12354   SCAN_PLAYFIELD(x, y)
12355   {
12356     element = Tile[x][y];
12357     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12358     last_gfx_frame = GfxFrame[x][y];
12359
12360     if (element == EL_EMPTY)
12361       graphic = el2img(GfxElementEmpty[x][y]);
12362
12363     ResetGfxFrame(x, y);
12364
12365     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12366       DrawLevelGraphicAnimation(x, y, graphic);
12367
12368     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12369         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12370       ResetRandomAnimationValue(x, y);
12371
12372     SetRandomAnimationValue(x, y);
12373
12374     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12375
12376     if (IS_INACTIVE(element))
12377     {
12378       if (IS_ANIMATED(graphic))
12379         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12380
12381       continue;
12382     }
12383
12384     // this may take place after moving, so 'element' may have changed
12385     if (IS_CHANGING(x, y) &&
12386         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12387     {
12388       int page = element_info[element].event_page_nr[CE_DELAY];
12389
12390       HandleElementChange(x, y, page);
12391
12392       element = Tile[x][y];
12393       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12394     }
12395
12396     CheckNextToConditions(x, y);
12397
12398     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12399     {
12400       StartMoving(x, y);
12401
12402       element = Tile[x][y];
12403       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12404
12405       if (IS_ANIMATED(graphic) &&
12406           !IS_MOVING(x, y) &&
12407           !Stop[x][y])
12408         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409
12410       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12411         TEST_DrawTwinkleOnField(x, y);
12412     }
12413     else if (element == EL_ACID)
12414     {
12415       if (!Stop[x][y])
12416         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12417     }
12418     else if ((element == EL_EXIT_OPEN ||
12419               element == EL_EM_EXIT_OPEN ||
12420               element == EL_SP_EXIT_OPEN ||
12421               element == EL_STEEL_EXIT_OPEN ||
12422               element == EL_EM_STEEL_EXIT_OPEN ||
12423               element == EL_SP_TERMINAL ||
12424               element == EL_SP_TERMINAL_ACTIVE ||
12425               element == EL_EXTRA_TIME ||
12426               element == EL_SHIELD_NORMAL ||
12427               element == EL_SHIELD_DEADLY) &&
12428              IS_ANIMATED(graphic))
12429       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12430     else if (IS_MOVING(x, y))
12431       ContinueMoving(x, y);
12432     else if (IS_ACTIVE_BOMB(element))
12433       CheckDynamite(x, y);
12434     else if (element == EL_AMOEBA_GROWING)
12435       AmoebaGrowing(x, y);
12436     else if (element == EL_AMOEBA_SHRINKING)
12437       AmoebaShrinking(x, y);
12438
12439 #if !USE_NEW_AMOEBA_CODE
12440     else if (IS_AMOEBALIVE(element))
12441       AmoebaReproduce(x, y);
12442 #endif
12443
12444     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12445       Life(x, y);
12446     else if (element == EL_EXIT_CLOSED)
12447       CheckExit(x, y);
12448     else if (element == EL_EM_EXIT_CLOSED)
12449       CheckExitEM(x, y);
12450     else if (element == EL_STEEL_EXIT_CLOSED)
12451       CheckExitSteel(x, y);
12452     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12453       CheckExitSteelEM(x, y);
12454     else if (element == EL_SP_EXIT_CLOSED)
12455       CheckExitSP(x, y);
12456     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12457              element == EL_EXPANDABLE_STEELWALL_GROWING)
12458       WallGrowing(x, y);
12459     else if (element == EL_EXPANDABLE_WALL ||
12460              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12461              element == EL_EXPANDABLE_WALL_VERTICAL ||
12462              element == EL_EXPANDABLE_WALL_ANY ||
12463              element == EL_BD_EXPANDABLE_WALL ||
12464              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12465              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12466              element == EL_EXPANDABLE_STEELWALL_ANY)
12467       CheckWallGrowing(x, y);
12468     else if (element == EL_FLAMES)
12469       CheckForDragon(x, y);
12470     else if (element == EL_EXPLOSION)
12471       ; // drawing of correct explosion animation is handled separately
12472     else if (element == EL_ELEMENT_SNAPPING ||
12473              element == EL_DIAGONAL_SHRINKING ||
12474              element == EL_DIAGONAL_GROWING)
12475     {
12476       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12477
12478       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12479     }
12480     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12481       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12482
12483     if (IS_BELT_ACTIVE(element))
12484       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12485
12486     if (game.magic_wall_active)
12487     {
12488       int jx = local_player->jx, jy = local_player->jy;
12489
12490       // play the element sound at the position nearest to the player
12491       if ((element == EL_MAGIC_WALL_FULL ||
12492            element == EL_MAGIC_WALL_ACTIVE ||
12493            element == EL_MAGIC_WALL_EMPTYING ||
12494            element == EL_BD_MAGIC_WALL_FULL ||
12495            element == EL_BD_MAGIC_WALL_ACTIVE ||
12496            element == EL_BD_MAGIC_WALL_EMPTYING ||
12497            element == EL_DC_MAGIC_WALL_FULL ||
12498            element == EL_DC_MAGIC_WALL_ACTIVE ||
12499            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12500           ABS(x - jx) + ABS(y - jy) <
12501           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12502       {
12503         magic_wall_x = x;
12504         magic_wall_y = y;
12505       }
12506     }
12507   }
12508
12509 #if USE_NEW_AMOEBA_CODE
12510   // new experimental amoeba growth stuff
12511   if (!(FrameCounter % 8))
12512   {
12513     static unsigned int random = 1684108901;
12514
12515     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12516     {
12517       x = RND(lev_fieldx);
12518       y = RND(lev_fieldy);
12519       element = Tile[x][y];
12520
12521       if (!IS_PLAYER(x, y) &&
12522           (element == EL_EMPTY ||
12523            CAN_GROW_INTO(element) ||
12524            element == EL_QUICKSAND_EMPTY ||
12525            element == EL_QUICKSAND_FAST_EMPTY ||
12526            element == EL_ACID_SPLASH_LEFT ||
12527            element == EL_ACID_SPLASH_RIGHT))
12528       {
12529         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12530             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12531             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12532             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12533           Tile[x][y] = EL_AMOEBA_DROP;
12534       }
12535
12536       random = random * 129 + 1;
12537     }
12538   }
12539 #endif
12540
12541   game.explosions_delayed = FALSE;
12542
12543   SCAN_PLAYFIELD(x, y)
12544   {
12545     element = Tile[x][y];
12546
12547     if (ExplodeField[x][y])
12548       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12549     else if (element == EL_EXPLOSION)
12550       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12551
12552     ExplodeField[x][y] = EX_TYPE_NONE;
12553   }
12554
12555   game.explosions_delayed = TRUE;
12556
12557   if (game.magic_wall_active)
12558   {
12559     if (!(game.magic_wall_time_left % 4))
12560     {
12561       int element = Tile[magic_wall_x][magic_wall_y];
12562
12563       if (element == EL_BD_MAGIC_WALL_FULL ||
12564           element == EL_BD_MAGIC_WALL_ACTIVE ||
12565           element == EL_BD_MAGIC_WALL_EMPTYING)
12566         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12567       else if (element == EL_DC_MAGIC_WALL_FULL ||
12568                element == EL_DC_MAGIC_WALL_ACTIVE ||
12569                element == EL_DC_MAGIC_WALL_EMPTYING)
12570         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12571       else
12572         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12573     }
12574
12575     if (game.magic_wall_time_left > 0)
12576     {
12577       game.magic_wall_time_left--;
12578
12579       if (!game.magic_wall_time_left)
12580       {
12581         SCAN_PLAYFIELD(x, y)
12582         {
12583           element = Tile[x][y];
12584
12585           if (element == EL_MAGIC_WALL_ACTIVE ||
12586               element == EL_MAGIC_WALL_FULL)
12587           {
12588             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12589             TEST_DrawLevelField(x, y);
12590           }
12591           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12592                    element == EL_BD_MAGIC_WALL_FULL)
12593           {
12594             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12595             TEST_DrawLevelField(x, y);
12596           }
12597           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12598                    element == EL_DC_MAGIC_WALL_FULL)
12599           {
12600             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12601             TEST_DrawLevelField(x, y);
12602           }
12603         }
12604
12605         game.magic_wall_active = FALSE;
12606       }
12607     }
12608   }
12609
12610   if (game.light_time_left > 0)
12611   {
12612     game.light_time_left--;
12613
12614     if (game.light_time_left == 0)
12615       RedrawAllLightSwitchesAndInvisibleElements();
12616   }
12617
12618   if (game.timegate_time_left > 0)
12619   {
12620     game.timegate_time_left--;
12621
12622     if (game.timegate_time_left == 0)
12623       CloseAllOpenTimegates();
12624   }
12625
12626   if (game.lenses_time_left > 0)
12627   {
12628     game.lenses_time_left--;
12629
12630     if (game.lenses_time_left == 0)
12631       RedrawAllInvisibleElementsForLenses();
12632   }
12633
12634   if (game.magnify_time_left > 0)
12635   {
12636     game.magnify_time_left--;
12637
12638     if (game.magnify_time_left == 0)
12639       RedrawAllInvisibleElementsForMagnifier();
12640   }
12641
12642   for (i = 0; i < MAX_PLAYERS; i++)
12643   {
12644     struct PlayerInfo *player = &stored_player[i];
12645
12646     if (SHIELD_ON(player))
12647     {
12648       if (player->shield_deadly_time_left)
12649         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12650       else if (player->shield_normal_time_left)
12651         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12652     }
12653   }
12654
12655 #if USE_DELAYED_GFX_REDRAW
12656   SCAN_PLAYFIELD(x, y)
12657   {
12658     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12659     {
12660       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12661          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12662
12663       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12664         DrawLevelField(x, y);
12665
12666       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12667         DrawLevelFieldCrumbled(x, y);
12668
12669       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12670         DrawLevelFieldCrumbledNeighbours(x, y);
12671
12672       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12673         DrawTwinkleOnField(x, y);
12674     }
12675
12676     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12677   }
12678 #endif
12679
12680   DrawAllPlayers();
12681   PlayAllPlayersSound();
12682
12683   for (i = 0; i < MAX_PLAYERS; i++)
12684   {
12685     struct PlayerInfo *player = &stored_player[i];
12686
12687     if (player->show_envelope != 0 && (!player->active ||
12688                                        player->MovPos == 0))
12689     {
12690       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12691
12692       player->show_envelope = 0;
12693     }
12694   }
12695
12696   // use random number generator in every frame to make it less predictable
12697   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12698     RND(1);
12699
12700   mouse_action_last = mouse_action;
12701 }
12702
12703 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12704 {
12705   int min_x = x, min_y = y, max_x = x, max_y = y;
12706   int scr_fieldx = getScreenFieldSizeX();
12707   int scr_fieldy = getScreenFieldSizeY();
12708   int i;
12709
12710   for (i = 0; i < MAX_PLAYERS; i++)
12711   {
12712     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12713
12714     if (!stored_player[i].active || &stored_player[i] == player)
12715       continue;
12716
12717     min_x = MIN(min_x, jx);
12718     min_y = MIN(min_y, jy);
12719     max_x = MAX(max_x, jx);
12720     max_y = MAX(max_y, jy);
12721   }
12722
12723   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12724 }
12725
12726 static boolean AllPlayersInVisibleScreen(void)
12727 {
12728   int i;
12729
12730   for (i = 0; i < MAX_PLAYERS; i++)
12731   {
12732     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12733
12734     if (!stored_player[i].active)
12735       continue;
12736
12737     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12738       return FALSE;
12739   }
12740
12741   return TRUE;
12742 }
12743
12744 void ScrollLevel(int dx, int dy)
12745 {
12746   int scroll_offset = 2 * TILEX_VAR;
12747   int x, y;
12748
12749   BlitBitmap(drawto_field, drawto_field,
12750              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12751              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12752              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12753              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12754              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12755              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12756
12757   if (dx != 0)
12758   {
12759     x = (dx == 1 ? BX1 : BX2);
12760     for (y = BY1; y <= BY2; y++)
12761       DrawScreenField(x, y);
12762   }
12763
12764   if (dy != 0)
12765   {
12766     y = (dy == 1 ? BY1 : BY2);
12767     for (x = BX1; x <= BX2; x++)
12768       DrawScreenField(x, y);
12769   }
12770
12771   redraw_mask |= REDRAW_FIELD;
12772 }
12773
12774 static boolean canFallDown(struct PlayerInfo *player)
12775 {
12776   int jx = player->jx, jy = player->jy;
12777
12778   return (IN_LEV_FIELD(jx, jy + 1) &&
12779           (IS_FREE(jx, jy + 1) ||
12780            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12781           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12782           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12783 }
12784
12785 static boolean canPassField(int x, int y, int move_dir)
12786 {
12787   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12788   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12789   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12790   int nextx = x + dx;
12791   int nexty = y + dy;
12792   int element = Tile[x][y];
12793
12794   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12795           !CAN_MOVE(element) &&
12796           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12797           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12798           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12799 }
12800
12801 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12802 {
12803   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12804   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12805   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12806   int newx = x + dx;
12807   int newy = y + dy;
12808
12809   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12810           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12811           (IS_DIGGABLE(Tile[newx][newy]) ||
12812            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12813            canPassField(newx, newy, move_dir)));
12814 }
12815
12816 static void CheckGravityMovement(struct PlayerInfo *player)
12817 {
12818   if (player->gravity && !player->programmed_action)
12819   {
12820     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12821     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12822     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12823     int jx = player->jx, jy = player->jy;
12824     boolean player_is_moving_to_valid_field =
12825       (!player_is_snapping &&
12826        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12827         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12828     boolean player_can_fall_down = canFallDown(player);
12829
12830     if (player_can_fall_down &&
12831         !player_is_moving_to_valid_field)
12832       player->programmed_action = MV_DOWN;
12833   }
12834 }
12835
12836 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12837 {
12838   return CheckGravityMovement(player);
12839
12840   if (player->gravity && !player->programmed_action)
12841   {
12842     int jx = player->jx, jy = player->jy;
12843     boolean field_under_player_is_free =
12844       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12845     boolean player_is_standing_on_valid_field =
12846       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12847        (IS_WALKABLE(Tile[jx][jy]) &&
12848         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12849
12850     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12851       player->programmed_action = MV_DOWN;
12852   }
12853 }
12854
12855 /*
12856   MovePlayerOneStep()
12857   -----------------------------------------------------------------------------
12858   dx, dy:               direction (non-diagonal) to try to move the player to
12859   real_dx, real_dy:     direction as read from input device (can be diagonal)
12860 */
12861
12862 boolean MovePlayerOneStep(struct PlayerInfo *player,
12863                           int dx, int dy, int real_dx, int real_dy)
12864 {
12865   int jx = player->jx, jy = player->jy;
12866   int new_jx = jx + dx, new_jy = jy + dy;
12867   int can_move;
12868   boolean player_can_move = !player->cannot_move;
12869
12870   if (!player->active || (!dx && !dy))
12871     return MP_NO_ACTION;
12872
12873   player->MovDir = (dx < 0 ? MV_LEFT :
12874                     dx > 0 ? MV_RIGHT :
12875                     dy < 0 ? MV_UP :
12876                     dy > 0 ? MV_DOWN :  MV_NONE);
12877
12878   if (!IN_LEV_FIELD(new_jx, new_jy))
12879     return MP_NO_ACTION;
12880
12881   if (!player_can_move)
12882   {
12883     if (player->MovPos == 0)
12884     {
12885       player->is_moving = FALSE;
12886       player->is_digging = FALSE;
12887       player->is_collecting = FALSE;
12888       player->is_snapping = FALSE;
12889       player->is_pushing = FALSE;
12890     }
12891   }
12892
12893   if (!network.enabled && game.centered_player_nr == -1 &&
12894       !AllPlayersInSight(player, new_jx, new_jy))
12895     return MP_NO_ACTION;
12896
12897   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12898   if (can_move != MP_MOVING)
12899     return can_move;
12900
12901   // check if DigField() has caused relocation of the player
12902   if (player->jx != jx || player->jy != jy)
12903     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12904
12905   StorePlayer[jx][jy] = 0;
12906   player->last_jx = jx;
12907   player->last_jy = jy;
12908   player->jx = new_jx;
12909   player->jy = new_jy;
12910   StorePlayer[new_jx][new_jy] = player->element_nr;
12911
12912   if (player->move_delay_value_next != -1)
12913   {
12914     player->move_delay_value = player->move_delay_value_next;
12915     player->move_delay_value_next = -1;
12916   }
12917
12918   player->MovPos =
12919     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12920
12921   player->step_counter++;
12922
12923   PlayerVisit[jx][jy] = FrameCounter;
12924
12925   player->is_moving = TRUE;
12926
12927 #if 1
12928   // should better be called in MovePlayer(), but this breaks some tapes
12929   ScrollPlayer(player, SCROLL_INIT);
12930 #endif
12931
12932   return MP_MOVING;
12933 }
12934
12935 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12936 {
12937   int jx = player->jx, jy = player->jy;
12938   int old_jx = jx, old_jy = jy;
12939   int moved = MP_NO_ACTION;
12940
12941   if (!player->active)
12942     return FALSE;
12943
12944   if (!dx && !dy)
12945   {
12946     if (player->MovPos == 0)
12947     {
12948       player->is_moving = FALSE;
12949       player->is_digging = FALSE;
12950       player->is_collecting = FALSE;
12951       player->is_snapping = FALSE;
12952       player->is_pushing = FALSE;
12953     }
12954
12955     return FALSE;
12956   }
12957
12958   if (player->move_delay > 0)
12959     return FALSE;
12960
12961   player->move_delay = -1;              // set to "uninitialized" value
12962
12963   // store if player is automatically moved to next field
12964   player->is_auto_moving = (player->programmed_action != MV_NONE);
12965
12966   // remove the last programmed player action
12967   player->programmed_action = 0;
12968
12969   if (player->MovPos)
12970   {
12971     // should only happen if pre-1.2 tape recordings are played
12972     // this is only for backward compatibility
12973
12974     int original_move_delay_value = player->move_delay_value;
12975
12976 #if DEBUG
12977     Debug("game:playing:MovePlayer",
12978           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12979           tape.counter);
12980 #endif
12981
12982     // scroll remaining steps with finest movement resolution
12983     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12984
12985     while (player->MovPos)
12986     {
12987       ScrollPlayer(player, SCROLL_GO_ON);
12988       ScrollScreen(NULL, SCROLL_GO_ON);
12989
12990       AdvanceFrameAndPlayerCounters(player->index_nr);
12991
12992       DrawAllPlayers();
12993       BackToFront_WithFrameDelay(0);
12994     }
12995
12996     player->move_delay_value = original_move_delay_value;
12997   }
12998
12999   player->is_active = FALSE;
13000
13001   if (player->last_move_dir & MV_HORIZONTAL)
13002   {
13003     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13004       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13005   }
13006   else
13007   {
13008     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13009       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13010   }
13011
13012   if (!moved && !player->is_active)
13013   {
13014     player->is_moving = FALSE;
13015     player->is_digging = FALSE;
13016     player->is_collecting = FALSE;
13017     player->is_snapping = FALSE;
13018     player->is_pushing = FALSE;
13019   }
13020
13021   jx = player->jx;
13022   jy = player->jy;
13023
13024   if (moved & MP_MOVING && !ScreenMovPos &&
13025       (player->index_nr == game.centered_player_nr ||
13026        game.centered_player_nr == -1))
13027   {
13028     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13029
13030     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13031     {
13032       // actual player has left the screen -- scroll in that direction
13033       if (jx != old_jx)         // player has moved horizontally
13034         scroll_x += (jx - old_jx);
13035       else                      // player has moved vertically
13036         scroll_y += (jy - old_jy);
13037     }
13038     else
13039     {
13040       int offset_raw = game.scroll_delay_value;
13041
13042       if (jx != old_jx)         // player has moved horizontally
13043       {
13044         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13045         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13046         int new_scroll_x = jx - MIDPOSX + offset_x;
13047
13048         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13049             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13050           scroll_x = new_scroll_x;
13051
13052         // don't scroll over playfield boundaries
13053         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13054
13055         // don't scroll more than one field at a time
13056         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13057
13058         // don't scroll against the player's moving direction
13059         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13060             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13061           scroll_x = old_scroll_x;
13062       }
13063       else                      // player has moved vertically
13064       {
13065         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13066         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13067         int new_scroll_y = jy - MIDPOSY + offset_y;
13068
13069         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13070             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13071           scroll_y = new_scroll_y;
13072
13073         // don't scroll over playfield boundaries
13074         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13075
13076         // don't scroll more than one field at a time
13077         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13078
13079         // don't scroll against the player's moving direction
13080         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13081             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13082           scroll_y = old_scroll_y;
13083       }
13084     }
13085
13086     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13087     {
13088       if (!network.enabled && game.centered_player_nr == -1 &&
13089           !AllPlayersInVisibleScreen())
13090       {
13091         scroll_x = old_scroll_x;
13092         scroll_y = old_scroll_y;
13093       }
13094       else
13095       {
13096         ScrollScreen(player, SCROLL_INIT);
13097         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13098       }
13099     }
13100   }
13101
13102   player->StepFrame = 0;
13103
13104   if (moved & MP_MOVING)
13105   {
13106     if (old_jx != jx && old_jy == jy)
13107       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13108     else if (old_jx == jx && old_jy != jy)
13109       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13110
13111     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13112
13113     player->last_move_dir = player->MovDir;
13114     player->is_moving = TRUE;
13115     player->is_snapping = FALSE;
13116     player->is_switching = FALSE;
13117     player->is_dropping = FALSE;
13118     player->is_dropping_pressed = FALSE;
13119     player->drop_pressed_delay = 0;
13120
13121 #if 0
13122     // should better be called here than above, but this breaks some tapes
13123     ScrollPlayer(player, SCROLL_INIT);
13124 #endif
13125   }
13126   else
13127   {
13128     CheckGravityMovementWhenNotMoving(player);
13129
13130     player->is_moving = FALSE;
13131
13132     /* at this point, the player is allowed to move, but cannot move right now
13133        (e.g. because of something blocking the way) -- ensure that the player
13134        is also allowed to move in the next frame (in old versions before 3.1.1,
13135        the player was forced to wait again for eight frames before next try) */
13136
13137     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13138       player->move_delay = 0;   // allow direct movement in the next frame
13139   }
13140
13141   if (player->move_delay == -1)         // not yet initialized by DigField()
13142     player->move_delay = player->move_delay_value;
13143
13144   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13145   {
13146     TestIfPlayerTouchesBadThing(jx, jy);
13147     TestIfPlayerTouchesCustomElement(jx, jy);
13148   }
13149
13150   if (!player->active)
13151     RemovePlayer(player);
13152
13153   return moved;
13154 }
13155
13156 void ScrollPlayer(struct PlayerInfo *player, int mode)
13157 {
13158   int jx = player->jx, jy = player->jy;
13159   int last_jx = player->last_jx, last_jy = player->last_jy;
13160   int move_stepsize = TILEX / player->move_delay_value;
13161
13162   if (!player->active)
13163     return;
13164
13165   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13166     return;
13167
13168   if (mode == SCROLL_INIT)
13169   {
13170     player->actual_frame_counter.count = FrameCounter;
13171     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13172
13173     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13174         Tile[last_jx][last_jy] == EL_EMPTY)
13175     {
13176       int last_field_block_delay = 0;   // start with no blocking at all
13177       int block_delay_adjustment = player->block_delay_adjustment;
13178
13179       // if player blocks last field, add delay for exactly one move
13180       if (player->block_last_field)
13181       {
13182         last_field_block_delay += player->move_delay_value;
13183
13184         // when blocking enabled, prevent moving up despite gravity
13185         if (player->gravity && player->MovDir == MV_UP)
13186           block_delay_adjustment = -1;
13187       }
13188
13189       // add block delay adjustment (also possible when not blocking)
13190       last_field_block_delay += block_delay_adjustment;
13191
13192       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13193       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13194     }
13195
13196     if (player->MovPos != 0)    // player has not yet reached destination
13197       return;
13198   }
13199   else if (!FrameReached(&player->actual_frame_counter))
13200     return;
13201
13202   if (player->MovPos != 0)
13203   {
13204     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13205     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13206
13207     // before DrawPlayer() to draw correct player graphic for this case
13208     if (player->MovPos == 0)
13209       CheckGravityMovement(player);
13210   }
13211
13212   if (player->MovPos == 0)      // player reached destination field
13213   {
13214     if (player->move_delay_reset_counter > 0)
13215     {
13216       player->move_delay_reset_counter--;
13217
13218       if (player->move_delay_reset_counter == 0)
13219       {
13220         // continue with normal speed after quickly moving through gate
13221         HALVE_PLAYER_SPEED(player);
13222
13223         // be able to make the next move without delay
13224         player->move_delay = 0;
13225       }
13226     }
13227
13228     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13229         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13230         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13231         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13232         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13233         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13234         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13235         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13236     {
13237       ExitPlayer(player);
13238
13239       if (game.players_still_needed == 0 &&
13240           (game.friends_still_needed == 0 ||
13241            IS_SP_ELEMENT(Tile[jx][jy])))
13242         LevelSolved();
13243     }
13244
13245     player->last_jx = jx;
13246     player->last_jy = jy;
13247
13248     // this breaks one level: "machine", level 000
13249     {
13250       int move_direction = player->MovDir;
13251       int enter_side = MV_DIR_OPPOSITE(move_direction);
13252       int leave_side = move_direction;
13253       int old_jx = last_jx;
13254       int old_jy = last_jy;
13255       int old_element = Tile[old_jx][old_jy];
13256       int new_element = Tile[jx][jy];
13257
13258       if (IS_CUSTOM_ELEMENT(old_element))
13259         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13260                                    CE_LEFT_BY_PLAYER,
13261                                    player->index_bit, leave_side);
13262
13263       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13264                                           CE_PLAYER_LEAVES_X,
13265                                           player->index_bit, leave_side);
13266
13267       // needed because pushed element has not yet reached its destination,
13268       // so it would trigger a change event at its previous field location
13269       if (!player->is_pushing)
13270       {
13271         if (IS_CUSTOM_ELEMENT(new_element))
13272           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13273                                      player->index_bit, enter_side);
13274
13275         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13276                                             CE_PLAYER_ENTERS_X,
13277                                             player->index_bit, enter_side);
13278       }
13279
13280       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13281                                         CE_MOVE_OF_X, move_direction);
13282     }
13283
13284     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13285     {
13286       TestIfPlayerTouchesBadThing(jx, jy);
13287       TestIfPlayerTouchesCustomElement(jx, jy);
13288
13289       // needed because pushed element has not yet reached its destination,
13290       // so it would trigger a change event at its previous field location
13291       if (!player->is_pushing)
13292         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13293
13294       if (level.finish_dig_collect &&
13295           (player->is_digging || player->is_collecting))
13296       {
13297         int last_element = player->last_removed_element;
13298         int move_direction = player->MovDir;
13299         int enter_side = MV_DIR_OPPOSITE(move_direction);
13300         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13301                             CE_PLAYER_COLLECTS_X);
13302
13303         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13304                                             player->index_bit, enter_side);
13305
13306         player->last_removed_element = EL_UNDEFINED;
13307       }
13308
13309       if (!player->active)
13310         RemovePlayer(player);
13311     }
13312
13313     if (level.use_step_counter)
13314       CheckLevelTime_StepCounter();
13315
13316     if (tape.single_step && tape.recording && !tape.pausing &&
13317         !player->programmed_action)
13318       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13319
13320     if (!player->programmed_action)
13321       CheckSaveEngineSnapshot(player);
13322   }
13323 }
13324
13325 void ScrollScreen(struct PlayerInfo *player, int mode)
13326 {
13327   static DelayCounter screen_frame_counter = { 0 };
13328
13329   if (mode == SCROLL_INIT)
13330   {
13331     // set scrolling step size according to actual player's moving speed
13332     ScrollStepSize = TILEX / player->move_delay_value;
13333
13334     screen_frame_counter.count = FrameCounter;
13335     screen_frame_counter.value = 1;
13336
13337     ScreenMovDir = player->MovDir;
13338     ScreenMovPos = player->MovPos;
13339     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13340     return;
13341   }
13342   else if (!FrameReached(&screen_frame_counter))
13343     return;
13344
13345   if (ScreenMovPos)
13346   {
13347     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13348     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13349     redraw_mask |= REDRAW_FIELD;
13350   }
13351   else
13352     ScreenMovDir = MV_NONE;
13353 }
13354
13355 void CheckNextToConditions(int x, int y)
13356 {
13357   int element = Tile[x][y];
13358
13359   if (IS_PLAYER(x, y))
13360     TestIfPlayerNextToCustomElement(x, y);
13361
13362   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13363       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13364     TestIfElementNextToCustomElement(x, y);
13365 }
13366
13367 void TestIfPlayerNextToCustomElement(int x, int y)
13368 {
13369   struct XY *xy = xy_topdown;
13370   static int trigger_sides[4][2] =
13371   {
13372     // center side       border side
13373     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13374     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13375     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13376     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13377   };
13378   int i;
13379
13380   if (!IS_PLAYER(x, y))
13381     return;
13382
13383   struct PlayerInfo *player = PLAYERINFO(x, y);
13384
13385   if (player->is_moving)
13386     return;
13387
13388   for (i = 0; i < NUM_DIRECTIONS; i++)
13389   {
13390     int xx = x + xy[i].x;
13391     int yy = y + xy[i].y;
13392     int border_side = trigger_sides[i][1];
13393     int border_element;
13394
13395     if (!IN_LEV_FIELD(xx, yy))
13396       continue;
13397
13398     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13399       continue;         // center and border element not connected
13400
13401     border_element = Tile[xx][yy];
13402
13403     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13404                                player->index_bit, border_side);
13405     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13406                                         CE_PLAYER_NEXT_TO_X,
13407                                         player->index_bit, border_side);
13408
13409     /* use player element that is initially defined in the level playfield,
13410        not the player element that corresponds to the runtime player number
13411        (example: a level that contains EL_PLAYER_3 as the only player would
13412        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13413
13414     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13415                              CE_NEXT_TO_X, border_side);
13416   }
13417 }
13418
13419 void TestIfPlayerTouchesCustomElement(int x, int y)
13420 {
13421   struct XY *xy = xy_topdown;
13422   static int trigger_sides[4][2] =
13423   {
13424     // center side       border side
13425     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13426     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13427     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13428     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13429   };
13430   static int touch_dir[4] =
13431   {
13432     MV_LEFT | MV_RIGHT,
13433     MV_UP   | MV_DOWN,
13434     MV_UP   | MV_DOWN,
13435     MV_LEFT | MV_RIGHT
13436   };
13437   int center_element = Tile[x][y];      // should always be non-moving!
13438   int i;
13439
13440   for (i = 0; i < NUM_DIRECTIONS; i++)
13441   {
13442     int xx = x + xy[i].x;
13443     int yy = y + xy[i].y;
13444     int center_side = trigger_sides[i][0];
13445     int border_side = trigger_sides[i][1];
13446     int border_element;
13447
13448     if (!IN_LEV_FIELD(xx, yy))
13449       continue;
13450
13451     if (IS_PLAYER(x, y))                // player found at center element
13452     {
13453       struct PlayerInfo *player = PLAYERINFO(x, y);
13454
13455       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13456         border_element = Tile[xx][yy];          // may be moving!
13457       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13458         border_element = Tile[xx][yy];
13459       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13460         border_element = MovingOrBlocked2Element(xx, yy);
13461       else
13462         continue;               // center and border element do not touch
13463
13464       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13465                                  player->index_bit, border_side);
13466       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13467                                           CE_PLAYER_TOUCHES_X,
13468                                           player->index_bit, border_side);
13469
13470       {
13471         /* use player element that is initially defined in the level playfield,
13472            not the player element that corresponds to the runtime player number
13473            (example: a level that contains EL_PLAYER_3 as the only player would
13474            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13475         int player_element = PLAYERINFO(x, y)->initial_element;
13476
13477         // as element "X" is the player here, check opposite (center) side
13478         CheckElementChangeBySide(xx, yy, border_element, player_element,
13479                                  CE_TOUCHING_X, center_side);
13480       }
13481     }
13482     else if (IS_PLAYER(xx, yy))         // player found at border element
13483     {
13484       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13485
13486       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13487       {
13488         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13489           continue;             // center and border element do not touch
13490       }
13491
13492       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13493                                  player->index_bit, center_side);
13494       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13495                                           CE_PLAYER_TOUCHES_X,
13496                                           player->index_bit, center_side);
13497
13498       {
13499         /* use player element that is initially defined in the level playfield,
13500            not the player element that corresponds to the runtime player number
13501            (example: a level that contains EL_PLAYER_3 as the only player would
13502            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13503         int player_element = PLAYERINFO(xx, yy)->initial_element;
13504
13505         // as element "X" is the player here, check opposite (border) side
13506         CheckElementChangeBySide(x, y, center_element, player_element,
13507                                  CE_TOUCHING_X, border_side);
13508       }
13509
13510       break;
13511     }
13512   }
13513 }
13514
13515 void TestIfElementNextToCustomElement(int x, int y)
13516 {
13517   struct XY *xy = xy_topdown;
13518   static int trigger_sides[4][2] =
13519   {
13520     // center side      border side
13521     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13522     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13523     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13524     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13525   };
13526   int center_element = Tile[x][y];      // should always be non-moving!
13527   int i;
13528
13529   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13530     return;
13531
13532   for (i = 0; i < NUM_DIRECTIONS; i++)
13533   {
13534     int xx = x + xy[i].x;
13535     int yy = y + xy[i].y;
13536     int border_side = trigger_sides[i][1];
13537     int border_element;
13538
13539     if (!IN_LEV_FIELD(xx, yy))
13540       continue;
13541
13542     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13543       continue;                 // center and border element not connected
13544
13545     border_element = Tile[xx][yy];
13546
13547     // check for change of center element (but change it only once)
13548     if (CheckElementChangeBySide(x, y, center_element, border_element,
13549                                  CE_NEXT_TO_X, border_side))
13550       break;
13551   }
13552 }
13553
13554 void TestIfElementTouchesCustomElement(int x, int y)
13555 {
13556   struct XY *xy = xy_topdown;
13557   static int trigger_sides[4][2] =
13558   {
13559     // center side      border side
13560     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13561     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13562     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13563     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13564   };
13565   static int touch_dir[4] =
13566   {
13567     MV_LEFT | MV_RIGHT,
13568     MV_UP   | MV_DOWN,
13569     MV_UP   | MV_DOWN,
13570     MV_LEFT | MV_RIGHT
13571   };
13572   boolean change_center_element = FALSE;
13573   int center_element = Tile[x][y];      // should always be non-moving!
13574   int border_element_old[NUM_DIRECTIONS];
13575   int i;
13576
13577   for (i = 0; i < NUM_DIRECTIONS; i++)
13578   {
13579     int xx = x + xy[i].x;
13580     int yy = y + xy[i].y;
13581     int border_element;
13582
13583     border_element_old[i] = -1;
13584
13585     if (!IN_LEV_FIELD(xx, yy))
13586       continue;
13587
13588     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13589       border_element = Tile[xx][yy];    // may be moving!
13590     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13591       border_element = Tile[xx][yy];
13592     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13593       border_element = MovingOrBlocked2Element(xx, yy);
13594     else
13595       continue;                 // center and border element do not touch
13596
13597     border_element_old[i] = border_element;
13598   }
13599
13600   for (i = 0; i < NUM_DIRECTIONS; i++)
13601   {
13602     int xx = x + xy[i].x;
13603     int yy = y + xy[i].y;
13604     int center_side = trigger_sides[i][0];
13605     int border_element = border_element_old[i];
13606
13607     if (border_element == -1)
13608       continue;
13609
13610     // check for change of border element
13611     CheckElementChangeBySide(xx, yy, border_element, center_element,
13612                              CE_TOUCHING_X, center_side);
13613
13614     // (center element cannot be player, so we don't have to check this here)
13615   }
13616
13617   for (i = 0; i < NUM_DIRECTIONS; i++)
13618   {
13619     int xx = x + xy[i].x;
13620     int yy = y + xy[i].y;
13621     int border_side = trigger_sides[i][1];
13622     int border_element = border_element_old[i];
13623
13624     if (border_element == -1)
13625       continue;
13626
13627     // check for change of center element (but change it only once)
13628     if (!change_center_element)
13629       change_center_element =
13630         CheckElementChangeBySide(x, y, center_element, border_element,
13631                                  CE_TOUCHING_X, border_side);
13632
13633     if (IS_PLAYER(xx, yy))
13634     {
13635       /* use player element that is initially defined in the level playfield,
13636          not the player element that corresponds to the runtime player number
13637          (example: a level that contains EL_PLAYER_3 as the only player would
13638          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13639       int player_element = PLAYERINFO(xx, yy)->initial_element;
13640
13641       // as element "X" is the player here, check opposite (border) side
13642       CheckElementChangeBySide(x, y, center_element, player_element,
13643                                CE_TOUCHING_X, border_side);
13644     }
13645   }
13646 }
13647
13648 void TestIfElementHitsCustomElement(int x, int y, int direction)
13649 {
13650   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13651   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13652   int hitx = x + dx, hity = y + dy;
13653   int hitting_element = Tile[x][y];
13654   int touched_element;
13655
13656   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13657     return;
13658
13659   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13660                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13661
13662   if (IN_LEV_FIELD(hitx, hity))
13663   {
13664     int opposite_direction = MV_DIR_OPPOSITE(direction);
13665     int hitting_side = direction;
13666     int touched_side = opposite_direction;
13667     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13668                           MovDir[hitx][hity] != direction ||
13669                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13670
13671     object_hit = TRUE;
13672
13673     if (object_hit)
13674     {
13675       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13676                                CE_HITTING_X, touched_side);
13677
13678       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13679                                CE_HIT_BY_X, hitting_side);
13680
13681       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13682                                CE_HIT_BY_SOMETHING, opposite_direction);
13683
13684       if (IS_PLAYER(hitx, hity))
13685       {
13686         /* use player element that is initially defined in the level playfield,
13687            not the player element that corresponds to the runtime player number
13688            (example: a level that contains EL_PLAYER_3 as the only player would
13689            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13690         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13691
13692         CheckElementChangeBySide(x, y, hitting_element, player_element,
13693                                  CE_HITTING_X, touched_side);
13694       }
13695     }
13696   }
13697
13698   // "hitting something" is also true when hitting the playfield border
13699   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13700                            CE_HITTING_SOMETHING, direction);
13701 }
13702
13703 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13704 {
13705   int i, kill_x = -1, kill_y = -1;
13706
13707   int bad_element = -1;
13708   struct XY *test_xy = xy_topdown;
13709   static int test_dir[4] =
13710   {
13711     MV_UP,
13712     MV_LEFT,
13713     MV_RIGHT,
13714     MV_DOWN
13715   };
13716
13717   for (i = 0; i < NUM_DIRECTIONS; i++)
13718   {
13719     int test_x, test_y, test_move_dir, test_element;
13720
13721     test_x = good_x + test_xy[i].x;
13722     test_y = good_y + test_xy[i].y;
13723
13724     if (!IN_LEV_FIELD(test_x, test_y))
13725       continue;
13726
13727     test_move_dir =
13728       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13729
13730     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13731
13732     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13733        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13734     */
13735     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13736         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13737     {
13738       kill_x = test_x;
13739       kill_y = test_y;
13740       bad_element = test_element;
13741
13742       break;
13743     }
13744   }
13745
13746   if (kill_x != -1 || kill_y != -1)
13747   {
13748     if (IS_PLAYER(good_x, good_y))
13749     {
13750       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13751
13752       if (player->shield_deadly_time_left > 0 &&
13753           !IS_INDESTRUCTIBLE(bad_element))
13754         Bang(kill_x, kill_y);
13755       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13756         KillPlayer(player);
13757     }
13758     else
13759       Bang(good_x, good_y);
13760   }
13761 }
13762
13763 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13764 {
13765   int i, kill_x = -1, kill_y = -1;
13766   int bad_element = Tile[bad_x][bad_y];
13767   struct XY *test_xy = xy_topdown;
13768   static int touch_dir[4] =
13769   {
13770     MV_LEFT | MV_RIGHT,
13771     MV_UP   | MV_DOWN,
13772     MV_UP   | MV_DOWN,
13773     MV_LEFT | MV_RIGHT
13774   };
13775   static int test_dir[4] =
13776   {
13777     MV_UP,
13778     MV_LEFT,
13779     MV_RIGHT,
13780     MV_DOWN
13781   };
13782
13783   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13784     return;
13785
13786   for (i = 0; i < NUM_DIRECTIONS; i++)
13787   {
13788     int test_x, test_y, test_move_dir, test_element;
13789
13790     test_x = bad_x + test_xy[i].x;
13791     test_y = bad_y + test_xy[i].y;
13792
13793     if (!IN_LEV_FIELD(test_x, test_y))
13794       continue;
13795
13796     test_move_dir =
13797       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13798
13799     test_element = Tile[test_x][test_y];
13800
13801     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13802        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13803     */
13804     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13805         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13806     {
13807       // good thing is player or penguin that does not move away
13808       if (IS_PLAYER(test_x, test_y))
13809       {
13810         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13811
13812         if (bad_element == EL_ROBOT && player->is_moving)
13813           continue;     // robot does not kill player if he is moving
13814
13815         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13816         {
13817           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13818             continue;           // center and border element do not touch
13819         }
13820
13821         kill_x = test_x;
13822         kill_y = test_y;
13823
13824         break;
13825       }
13826       else if (test_element == EL_PENGUIN)
13827       {
13828         kill_x = test_x;
13829         kill_y = test_y;
13830
13831         break;
13832       }
13833     }
13834   }
13835
13836   if (kill_x != -1 || kill_y != -1)
13837   {
13838     if (IS_PLAYER(kill_x, kill_y))
13839     {
13840       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13841
13842       if (player->shield_deadly_time_left > 0 &&
13843           !IS_INDESTRUCTIBLE(bad_element))
13844         Bang(bad_x, bad_y);
13845       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13846         KillPlayer(player);
13847     }
13848     else
13849       Bang(kill_x, kill_y);
13850   }
13851 }
13852
13853 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13854 {
13855   int bad_element = Tile[bad_x][bad_y];
13856   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13857   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13858   int test_x = bad_x + dx, test_y = bad_y + dy;
13859   int test_move_dir, test_element;
13860   int kill_x = -1, kill_y = -1;
13861
13862   if (!IN_LEV_FIELD(test_x, test_y))
13863     return;
13864
13865   test_move_dir =
13866     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13867
13868   test_element = Tile[test_x][test_y];
13869
13870   if (test_move_dir != bad_move_dir)
13871   {
13872     // good thing can be player or penguin that does not move away
13873     if (IS_PLAYER(test_x, test_y))
13874     {
13875       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13876
13877       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13878          player as being hit when he is moving towards the bad thing, because
13879          the "get hit by" condition would be lost after the player stops) */
13880       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13881         return;         // player moves away from bad thing
13882
13883       kill_x = test_x;
13884       kill_y = test_y;
13885     }
13886     else if (test_element == EL_PENGUIN)
13887     {
13888       kill_x = test_x;
13889       kill_y = test_y;
13890     }
13891   }
13892
13893   if (kill_x != -1 || kill_y != -1)
13894   {
13895     if (IS_PLAYER(kill_x, kill_y))
13896     {
13897       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13898
13899       if (player->shield_deadly_time_left > 0 &&
13900           !IS_INDESTRUCTIBLE(bad_element))
13901         Bang(bad_x, bad_y);
13902       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13903         KillPlayer(player);
13904     }
13905     else
13906       Bang(kill_x, kill_y);
13907   }
13908 }
13909
13910 void TestIfPlayerTouchesBadThing(int x, int y)
13911 {
13912   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13913 }
13914
13915 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13916 {
13917   TestIfGoodThingHitsBadThing(x, y, move_dir);
13918 }
13919
13920 void TestIfBadThingTouchesPlayer(int x, int y)
13921 {
13922   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13923 }
13924
13925 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13926 {
13927   TestIfBadThingHitsGoodThing(x, y, move_dir);
13928 }
13929
13930 void TestIfFriendTouchesBadThing(int x, int y)
13931 {
13932   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13933 }
13934
13935 void TestIfBadThingTouchesFriend(int x, int y)
13936 {
13937   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13938 }
13939
13940 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13941 {
13942   int i, kill_x = bad_x, kill_y = bad_y;
13943   struct XY *xy = xy_topdown;
13944
13945   for (i = 0; i < NUM_DIRECTIONS; i++)
13946   {
13947     int x, y, element;
13948
13949     x = bad_x + xy[i].x;
13950     y = bad_y + xy[i].y;
13951     if (!IN_LEV_FIELD(x, y))
13952       continue;
13953
13954     element = Tile[x][y];
13955     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13956         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13957     {
13958       kill_x = x;
13959       kill_y = y;
13960       break;
13961     }
13962   }
13963
13964   if (kill_x != bad_x || kill_y != bad_y)
13965     Bang(bad_x, bad_y);
13966 }
13967
13968 void KillPlayer(struct PlayerInfo *player)
13969 {
13970   int jx = player->jx, jy = player->jy;
13971
13972   if (!player->active)
13973     return;
13974
13975 #if 0
13976   Debug("game:playing:KillPlayer",
13977         "0: killed == %d, active == %d, reanimated == %d",
13978         player->killed, player->active, player->reanimated);
13979 #endif
13980
13981   /* the following code was introduced to prevent an infinite loop when calling
13982      -> Bang()
13983      -> CheckTriggeredElementChangeExt()
13984      -> ExecuteCustomElementAction()
13985      -> KillPlayer()
13986      -> (infinitely repeating the above sequence of function calls)
13987      which occurs when killing the player while having a CE with the setting
13988      "kill player X when explosion of <player X>"; the solution using a new
13989      field "player->killed" was chosen for backwards compatibility, although
13990      clever use of the fields "player->active" etc. would probably also work */
13991 #if 1
13992   if (player->killed)
13993     return;
13994 #endif
13995
13996   player->killed = TRUE;
13997
13998   // remove accessible field at the player's position
13999   RemoveField(jx, jy);
14000
14001   // deactivate shield (else Bang()/Explode() would not work right)
14002   player->shield_normal_time_left = 0;
14003   player->shield_deadly_time_left = 0;
14004
14005 #if 0
14006   Debug("game:playing:KillPlayer",
14007         "1: killed == %d, active == %d, reanimated == %d",
14008         player->killed, player->active, player->reanimated);
14009 #endif
14010
14011   Bang(jx, jy);
14012
14013 #if 0
14014   Debug("game:playing:KillPlayer",
14015         "2: killed == %d, active == %d, reanimated == %d",
14016         player->killed, player->active, player->reanimated);
14017 #endif
14018
14019   if (player->reanimated)       // killed player may have been reanimated
14020     player->killed = player->reanimated = FALSE;
14021   else
14022     BuryPlayer(player);
14023 }
14024
14025 static void KillPlayerUnlessEnemyProtected(int x, int y)
14026 {
14027   if (!PLAYER_ENEMY_PROTECTED(x, y))
14028     KillPlayer(PLAYERINFO(x, y));
14029 }
14030
14031 static void KillPlayerUnlessExplosionProtected(int x, int y)
14032 {
14033   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14034     KillPlayer(PLAYERINFO(x, y));
14035 }
14036
14037 void BuryPlayer(struct PlayerInfo *player)
14038 {
14039   int jx = player->jx, jy = player->jy;
14040
14041   if (!player->active)
14042     return;
14043
14044   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14045
14046   RemovePlayer(player);
14047
14048   player->buried = TRUE;
14049
14050   if (game.all_players_gone)
14051     game.GameOver = TRUE;
14052 }
14053
14054 void RemovePlayer(struct PlayerInfo *player)
14055 {
14056   int jx = player->jx, jy = player->jy;
14057   int i, found = FALSE;
14058
14059   player->present = FALSE;
14060   player->active = FALSE;
14061
14062   // required for some CE actions (even if the player is not active anymore)
14063   player->MovPos = 0;
14064
14065   if (!ExplodeField[jx][jy])
14066     StorePlayer[jx][jy] = 0;
14067
14068   if (player->is_moving)
14069     TEST_DrawLevelField(player->last_jx, player->last_jy);
14070
14071   for (i = 0; i < MAX_PLAYERS; i++)
14072     if (stored_player[i].active)
14073       found = TRUE;
14074
14075   if (!found)
14076   {
14077     game.all_players_gone = TRUE;
14078     game.GameOver = TRUE;
14079   }
14080
14081   game.exit_x = game.robot_wheel_x = jx;
14082   game.exit_y = game.robot_wheel_y = jy;
14083 }
14084
14085 void ExitPlayer(struct PlayerInfo *player)
14086 {
14087   DrawPlayer(player);   // needed here only to cleanup last field
14088   RemovePlayer(player);
14089
14090   if (game.players_still_needed > 0)
14091     game.players_still_needed--;
14092 }
14093
14094 static void SetFieldForSnapping(int x, int y, int element, int direction,
14095                                 int player_index_bit)
14096 {
14097   struct ElementInfo *ei = &element_info[element];
14098   int direction_bit = MV_DIR_TO_BIT(direction);
14099   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14100   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14101                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14102
14103   Tile[x][y] = EL_ELEMENT_SNAPPING;
14104   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14105   MovDir[x][y] = direction;
14106   Store[x][y] = element;
14107   Store2[x][y] = player_index_bit;
14108
14109   ResetGfxAnimation(x, y);
14110
14111   GfxElement[x][y] = element;
14112   GfxAction[x][y] = action;
14113   GfxDir[x][y] = direction;
14114   GfxFrame[x][y] = -1;
14115 }
14116
14117 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14118                                    int player_index_bit)
14119 {
14120   TestIfElementTouchesCustomElement(x, y);      // for empty space
14121
14122   if (level.finish_dig_collect)
14123   {
14124     int dig_side = MV_DIR_OPPOSITE(direction);
14125     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14126                         CE_PLAYER_COLLECTS_X);
14127
14128     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14129                                         player_index_bit, dig_side);
14130     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14131                                         player_index_bit, dig_side);
14132   }
14133 }
14134
14135 /*
14136   =============================================================================
14137   checkDiagonalPushing()
14138   -----------------------------------------------------------------------------
14139   check if diagonal input device direction results in pushing of object
14140   (by checking if the alternative direction is walkable, diggable, ...)
14141   =============================================================================
14142 */
14143
14144 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14145                                     int x, int y, int real_dx, int real_dy)
14146 {
14147   int jx, jy, dx, dy, xx, yy;
14148
14149   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14150     return TRUE;
14151
14152   // diagonal direction: check alternative direction
14153   jx = player->jx;
14154   jy = player->jy;
14155   dx = x - jx;
14156   dy = y - jy;
14157   xx = jx + (dx == 0 ? real_dx : 0);
14158   yy = jy + (dy == 0 ? real_dy : 0);
14159
14160   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14161 }
14162
14163 /*
14164   =============================================================================
14165   DigField()
14166   -----------------------------------------------------------------------------
14167   x, y:                 field next to player (non-diagonal) to try to dig to
14168   real_dx, real_dy:     direction as read from input device (can be diagonal)
14169   =============================================================================
14170 */
14171
14172 static int DigField(struct PlayerInfo *player,
14173                     int oldx, int oldy, int x, int y,
14174                     int real_dx, int real_dy, int mode)
14175 {
14176   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14177   boolean player_was_pushing = player->is_pushing;
14178   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14179   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14180   int jx = oldx, jy = oldy;
14181   int dx = x - jx, dy = y - jy;
14182   int nextx = x + dx, nexty = y + dy;
14183   int move_direction = (dx == -1 ? MV_LEFT  :
14184                         dx == +1 ? MV_RIGHT :
14185                         dy == -1 ? MV_UP    :
14186                         dy == +1 ? MV_DOWN  : MV_NONE);
14187   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14188   int dig_side = MV_DIR_OPPOSITE(move_direction);
14189   int old_element = Tile[jx][jy];
14190   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14191   int collect_count;
14192
14193   if (is_player)                // function can also be called by EL_PENGUIN
14194   {
14195     if (player->MovPos == 0)
14196     {
14197       player->is_digging = FALSE;
14198       player->is_collecting = FALSE;
14199     }
14200
14201     if (player->MovPos == 0)    // last pushing move finished
14202       player->is_pushing = FALSE;
14203
14204     if (mode == DF_NO_PUSH)     // player just stopped pushing
14205     {
14206       player->is_switching = FALSE;
14207       player->push_delay = -1;
14208
14209       return MP_NO_ACTION;
14210     }
14211   }
14212   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14213     old_element = Back[jx][jy];
14214
14215   // in case of element dropped at player position, check background
14216   else if (Back[jx][jy] != EL_EMPTY &&
14217            game.engine_version >= VERSION_IDENT(2,2,0,0))
14218     old_element = Back[jx][jy];
14219
14220   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14221     return MP_NO_ACTION;        // field has no opening in this direction
14222
14223   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14224     return MP_NO_ACTION;        // field has no opening in this direction
14225
14226   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14227   {
14228     SplashAcid(x, y);
14229
14230     Tile[jx][jy] = player->artwork_element;
14231     InitMovingField(jx, jy, MV_DOWN);
14232     Store[jx][jy] = EL_ACID;
14233     ContinueMoving(jx, jy);
14234     BuryPlayer(player);
14235
14236     return MP_DONT_RUN_INTO;
14237   }
14238
14239   if (player_can_move && DONT_RUN_INTO(element))
14240   {
14241     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14242
14243     return MP_DONT_RUN_INTO;
14244   }
14245
14246   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14247     return MP_NO_ACTION;
14248
14249   collect_count = element_info[element].collect_count_initial;
14250
14251   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14252     return MP_NO_ACTION;
14253
14254   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14255     player_can_move = player_can_move_or_snap;
14256
14257   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14258       game.engine_version >= VERSION_IDENT(2,2,0,0))
14259   {
14260     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14261                                player->index_bit, dig_side);
14262     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14263                                         player->index_bit, dig_side);
14264
14265     if (element == EL_DC_LANDMINE)
14266       Bang(x, y);
14267
14268     if (Tile[x][y] != element)          // field changed by snapping
14269       return MP_ACTION;
14270
14271     return MP_NO_ACTION;
14272   }
14273
14274   if (player->gravity && is_player && !player->is_auto_moving &&
14275       canFallDown(player) && move_direction != MV_DOWN &&
14276       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14277     return MP_NO_ACTION;        // player cannot walk here due to gravity
14278
14279   if (player_can_move &&
14280       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14281   {
14282     int sound_element = SND_ELEMENT(element);
14283     int sound_action = ACTION_WALKING;
14284
14285     if (IS_RND_GATE(element))
14286     {
14287       if (!player->key[RND_GATE_NR(element)])
14288         return MP_NO_ACTION;
14289     }
14290     else if (IS_RND_GATE_GRAY(element))
14291     {
14292       if (!player->key[RND_GATE_GRAY_NR(element)])
14293         return MP_NO_ACTION;
14294     }
14295     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14296     {
14297       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14298         return MP_NO_ACTION;
14299     }
14300     else if (element == EL_EXIT_OPEN ||
14301              element == EL_EM_EXIT_OPEN ||
14302              element == EL_EM_EXIT_OPENING ||
14303              element == EL_STEEL_EXIT_OPEN ||
14304              element == EL_EM_STEEL_EXIT_OPEN ||
14305              element == EL_EM_STEEL_EXIT_OPENING ||
14306              element == EL_SP_EXIT_OPEN ||
14307              element == EL_SP_EXIT_OPENING)
14308     {
14309       sound_action = ACTION_PASSING;    // player is passing exit
14310     }
14311     else if (element == EL_EMPTY)
14312     {
14313       sound_action = ACTION_MOVING;             // nothing to walk on
14314     }
14315
14316     // play sound from background or player, whatever is available
14317     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14318       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14319     else
14320       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14321   }
14322   else if (player_can_move &&
14323            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14324   {
14325     if (!ACCESS_FROM(element, opposite_direction))
14326       return MP_NO_ACTION;      // field not accessible from this direction
14327
14328     if (CAN_MOVE(element))      // only fixed elements can be passed!
14329       return MP_NO_ACTION;
14330
14331     if (IS_EM_GATE(element))
14332     {
14333       if (!player->key[EM_GATE_NR(element)])
14334         return MP_NO_ACTION;
14335     }
14336     else if (IS_EM_GATE_GRAY(element))
14337     {
14338       if (!player->key[EM_GATE_GRAY_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14342     {
14343       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_EMC_GATE(element))
14347     {
14348       if (!player->key[EMC_GATE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_EMC_GATE_GRAY(element))
14352     {
14353       if (!player->key[EMC_GATE_GRAY_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14357     {
14358       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (element == EL_DC_GATE_WHITE ||
14362              element == EL_DC_GATE_WHITE_GRAY ||
14363              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14364     {
14365       if (player->num_white_keys == 0)
14366         return MP_NO_ACTION;
14367
14368       player->num_white_keys--;
14369     }
14370     else if (IS_SP_PORT(element))
14371     {
14372       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14373           element == EL_SP_GRAVITY_PORT_RIGHT ||
14374           element == EL_SP_GRAVITY_PORT_UP ||
14375           element == EL_SP_GRAVITY_PORT_DOWN)
14376         player->gravity = !player->gravity;
14377       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14378                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14379                element == EL_SP_GRAVITY_ON_PORT_UP ||
14380                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14381         player->gravity = TRUE;
14382       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14383                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14384                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14385                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14386         player->gravity = FALSE;
14387     }
14388
14389     // automatically move to the next field with double speed
14390     player->programmed_action = move_direction;
14391
14392     if (player->move_delay_reset_counter == 0)
14393     {
14394       player->move_delay_reset_counter = 2;     // two double speed steps
14395
14396       DOUBLE_PLAYER_SPEED(player);
14397     }
14398
14399     PlayLevelSoundAction(x, y, ACTION_PASSING);
14400   }
14401   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14402   {
14403     RemoveField(x, y);
14404
14405     if (mode != DF_SNAP)
14406     {
14407       GfxElement[x][y] = GFX_ELEMENT(element);
14408       player->is_digging = TRUE;
14409     }
14410
14411     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14412
14413     // use old behaviour for old levels (digging)
14414     if (!level.finish_dig_collect)
14415     {
14416       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14417                                           player->index_bit, dig_side);
14418
14419       // if digging triggered player relocation, finish digging tile
14420       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14421         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14422     }
14423
14424     if (mode == DF_SNAP)
14425     {
14426       if (level.block_snap_field)
14427         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14428       else
14429         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14430
14431       // use old behaviour for old levels (snapping)
14432       if (!level.finish_dig_collect)
14433         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14434                                             player->index_bit, dig_side);
14435     }
14436   }
14437   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14438   {
14439     RemoveField(x, y);
14440
14441     if (is_player && mode != DF_SNAP)
14442     {
14443       GfxElement[x][y] = element;
14444       player->is_collecting = TRUE;
14445     }
14446
14447     if (element == EL_SPEED_PILL)
14448     {
14449       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14450     }
14451     else if (element == EL_EXTRA_TIME && level.time > 0)
14452     {
14453       TimeLeft += level.extra_time;
14454
14455       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14456
14457       DisplayGameControlValues();
14458     }
14459     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14460     {
14461       int shield_time = (element == EL_SHIELD_DEADLY ?
14462                          level.shield_deadly_time :
14463                          level.shield_normal_time);
14464
14465       player->shield_normal_time_left += shield_time;
14466       if (element == EL_SHIELD_DEADLY)
14467         player->shield_deadly_time_left += shield_time;
14468     }
14469     else if (element == EL_DYNAMITE ||
14470              element == EL_EM_DYNAMITE ||
14471              element == EL_SP_DISK_RED)
14472     {
14473       if (player->inventory_size < MAX_INVENTORY_SIZE)
14474         player->inventory_element[player->inventory_size++] = element;
14475
14476       DrawGameDoorValues();
14477     }
14478     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14479     {
14480       player->dynabomb_count++;
14481       player->dynabombs_left++;
14482     }
14483     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14484     {
14485       player->dynabomb_size++;
14486     }
14487     else if (element == EL_DYNABOMB_INCREASE_POWER)
14488     {
14489       player->dynabomb_xl = TRUE;
14490     }
14491     else if (IS_KEY(element))
14492     {
14493       player->key[KEY_NR(element)] = TRUE;
14494
14495       DrawGameDoorValues();
14496     }
14497     else if (element == EL_DC_KEY_WHITE)
14498     {
14499       player->num_white_keys++;
14500
14501       // display white keys?
14502       // DrawGameDoorValues();
14503     }
14504     else if (IS_ENVELOPE(element))
14505     {
14506       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14507
14508       if (!wait_for_snapping)
14509         player->show_envelope = element;
14510     }
14511     else if (element == EL_EMC_LENSES)
14512     {
14513       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14514
14515       RedrawAllInvisibleElementsForLenses();
14516     }
14517     else if (element == EL_EMC_MAGNIFIER)
14518     {
14519       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14520
14521       RedrawAllInvisibleElementsForMagnifier();
14522     }
14523     else if (IS_DROPPABLE(element) ||
14524              IS_THROWABLE(element))     // can be collected and dropped
14525     {
14526       int i;
14527
14528       if (collect_count == 0)
14529         player->inventory_infinite_element = element;
14530       else
14531         for (i = 0; i < collect_count; i++)
14532           if (player->inventory_size < MAX_INVENTORY_SIZE)
14533             player->inventory_element[player->inventory_size++] = element;
14534
14535       DrawGameDoorValues();
14536     }
14537     else if (collect_count > 0)
14538     {
14539       game.gems_still_needed -= collect_count;
14540       if (game.gems_still_needed < 0)
14541         game.gems_still_needed = 0;
14542
14543       game.snapshot.collected_item = TRUE;
14544
14545       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14546
14547       DisplayGameControlValues();
14548     }
14549
14550     RaiseScoreElement(element);
14551     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14552
14553     // use old behaviour for old levels (collecting)
14554     if (!level.finish_dig_collect && is_player)
14555     {
14556       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14557                                           player->index_bit, dig_side);
14558
14559       // if collecting triggered player relocation, finish collecting tile
14560       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14561         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14562     }
14563
14564     if (mode == DF_SNAP)
14565     {
14566       if (level.block_snap_field)
14567         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14568       else
14569         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14570
14571       // use old behaviour for old levels (snapping)
14572       if (!level.finish_dig_collect)
14573         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14574                                             player->index_bit, dig_side);
14575     }
14576   }
14577   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14578   {
14579     if (mode == DF_SNAP && element != EL_BD_ROCK)
14580       return MP_NO_ACTION;
14581
14582     if (CAN_FALL(element) && dy)
14583       return MP_NO_ACTION;
14584
14585     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14586         !(element == EL_SPRING && level.use_spring_bug))
14587       return MP_NO_ACTION;
14588
14589     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14590         ((move_direction & MV_VERTICAL &&
14591           ((element_info[element].move_pattern & MV_LEFT &&
14592             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14593            (element_info[element].move_pattern & MV_RIGHT &&
14594             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14595          (move_direction & MV_HORIZONTAL &&
14596           ((element_info[element].move_pattern & MV_UP &&
14597             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14598            (element_info[element].move_pattern & MV_DOWN &&
14599             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14600       return MP_NO_ACTION;
14601
14602     // do not push elements already moving away faster than player
14603     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14604         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14605       return MP_NO_ACTION;
14606
14607     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14608     {
14609       if (player->push_delay_value == -1 || !player_was_pushing)
14610         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14611     }
14612     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14613     {
14614       if (player->push_delay_value == -1)
14615         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14616     }
14617     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14618     {
14619       if (!player->is_pushing)
14620         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14621     }
14622
14623     player->is_pushing = TRUE;
14624     player->is_active = TRUE;
14625
14626     if (!(IN_LEV_FIELD(nextx, nexty) &&
14627           (IS_FREE(nextx, nexty) ||
14628            (IS_SB_ELEMENT(element) &&
14629             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14630            (IS_CUSTOM_ELEMENT(element) &&
14631             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14632       return MP_NO_ACTION;
14633
14634     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14635       return MP_NO_ACTION;
14636
14637     if (player->push_delay == -1)       // new pushing; restart delay
14638       player->push_delay = 0;
14639
14640     if (player->push_delay < player->push_delay_value &&
14641         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14642         element != EL_SPRING && element != EL_BALLOON)
14643     {
14644       // make sure that there is no move delay before next try to push
14645       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14646         player->move_delay = 0;
14647
14648       return MP_NO_ACTION;
14649     }
14650
14651     if (IS_CUSTOM_ELEMENT(element) &&
14652         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14653     {
14654       if (!DigFieldByCE(nextx, nexty, element))
14655         return MP_NO_ACTION;
14656     }
14657
14658     if (IS_SB_ELEMENT(element))
14659     {
14660       boolean sokoban_task_solved = FALSE;
14661
14662       if (element == EL_SOKOBAN_FIELD_FULL)
14663       {
14664         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14665
14666         IncrementSokobanFieldsNeeded();
14667         IncrementSokobanObjectsNeeded();
14668       }
14669
14670       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14671       {
14672         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14673
14674         DecrementSokobanFieldsNeeded();
14675         DecrementSokobanObjectsNeeded();
14676
14677         // sokoban object was pushed from empty field to sokoban field
14678         if (Back[x][y] == EL_EMPTY)
14679           sokoban_task_solved = TRUE;
14680       }
14681
14682       Tile[x][y] = EL_SOKOBAN_OBJECT;
14683
14684       if (Back[x][y] == Back[nextx][nexty])
14685         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14686       else if (Back[x][y] != 0)
14687         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14688                                     ACTION_EMPTYING);
14689       else
14690         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14691                                     ACTION_FILLING);
14692
14693       if (sokoban_task_solved &&
14694           game.sokoban_fields_still_needed == 0 &&
14695           game.sokoban_objects_still_needed == 0 &&
14696           level.auto_exit_sokoban)
14697       {
14698         game.players_still_needed = 0;
14699
14700         LevelSolved();
14701
14702         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14703       }
14704     }
14705     else
14706       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14707
14708     InitMovingField(x, y, move_direction);
14709     GfxAction[x][y] = ACTION_PUSHING;
14710
14711     if (mode == DF_SNAP)
14712       ContinueMoving(x, y);
14713     else
14714       MovPos[x][y] = (dx != 0 ? dx : dy);
14715
14716     Pushed[x][y] = TRUE;
14717     Pushed[nextx][nexty] = TRUE;
14718
14719     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14720       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14721     else
14722       player->push_delay_value = -1;    // get new value later
14723
14724     // check for element change _after_ element has been pushed
14725     if (game.use_change_when_pushing_bug)
14726     {
14727       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14728                                  player->index_bit, dig_side);
14729       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14730                                           player->index_bit, dig_side);
14731     }
14732   }
14733   else if (IS_SWITCHABLE(element))
14734   {
14735     if (PLAYER_SWITCHING(player, x, y))
14736     {
14737       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14738                                           player->index_bit, dig_side);
14739
14740       return MP_ACTION;
14741     }
14742
14743     player->is_switching = TRUE;
14744     player->switch_x = x;
14745     player->switch_y = y;
14746
14747     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14748
14749     if (element == EL_ROBOT_WHEEL)
14750     {
14751       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14752
14753       game.robot_wheel_x = x;
14754       game.robot_wheel_y = y;
14755       game.robot_wheel_active = TRUE;
14756
14757       TEST_DrawLevelField(x, y);
14758     }
14759     else if (element == EL_SP_TERMINAL)
14760     {
14761       int xx, yy;
14762
14763       SCAN_PLAYFIELD(xx, yy)
14764       {
14765         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14766         {
14767           Bang(xx, yy);
14768         }
14769         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14770         {
14771           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14772
14773           ResetGfxAnimation(xx, yy);
14774           TEST_DrawLevelField(xx, yy);
14775         }
14776       }
14777     }
14778     else if (IS_BELT_SWITCH(element))
14779     {
14780       ToggleBeltSwitch(x, y);
14781     }
14782     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14783              element == EL_SWITCHGATE_SWITCH_DOWN ||
14784              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14785              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14786     {
14787       ToggleSwitchgateSwitch();
14788     }
14789     else if (element == EL_LIGHT_SWITCH ||
14790              element == EL_LIGHT_SWITCH_ACTIVE)
14791     {
14792       ToggleLightSwitch(x, y);
14793     }
14794     else if (element == EL_TIMEGATE_SWITCH ||
14795              element == EL_DC_TIMEGATE_SWITCH)
14796     {
14797       ActivateTimegateSwitch(x, y);
14798     }
14799     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14800              element == EL_BALLOON_SWITCH_RIGHT ||
14801              element == EL_BALLOON_SWITCH_UP    ||
14802              element == EL_BALLOON_SWITCH_DOWN  ||
14803              element == EL_BALLOON_SWITCH_NONE  ||
14804              element == EL_BALLOON_SWITCH_ANY)
14805     {
14806       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14807                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14808                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14809                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14810                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14811                              move_direction);
14812     }
14813     else if (element == EL_LAMP)
14814     {
14815       Tile[x][y] = EL_LAMP_ACTIVE;
14816       game.lights_still_needed--;
14817
14818       ResetGfxAnimation(x, y);
14819       TEST_DrawLevelField(x, y);
14820     }
14821     else if (element == EL_TIME_ORB_FULL)
14822     {
14823       Tile[x][y] = EL_TIME_ORB_EMPTY;
14824
14825       if (level.time > 0 || level.use_time_orb_bug)
14826       {
14827         TimeLeft += level.time_orb_time;
14828         game.no_level_time_limit = FALSE;
14829
14830         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14831
14832         DisplayGameControlValues();
14833       }
14834
14835       ResetGfxAnimation(x, y);
14836       TEST_DrawLevelField(x, y);
14837     }
14838     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14839              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14840     {
14841       int xx, yy;
14842
14843       game.ball_active = !game.ball_active;
14844
14845       SCAN_PLAYFIELD(xx, yy)
14846       {
14847         int e = Tile[xx][yy];
14848
14849         if (game.ball_active)
14850         {
14851           if (e == EL_EMC_MAGIC_BALL)
14852             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14853           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14854             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14855         }
14856         else
14857         {
14858           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14859             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14860           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14861             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14862         }
14863       }
14864     }
14865
14866     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14867                                         player->index_bit, dig_side);
14868
14869     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14870                                         player->index_bit, dig_side);
14871
14872     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14873                                         player->index_bit, dig_side);
14874
14875     return MP_ACTION;
14876   }
14877   else
14878   {
14879     if (!PLAYER_SWITCHING(player, x, y))
14880     {
14881       player->is_switching = TRUE;
14882       player->switch_x = x;
14883       player->switch_y = y;
14884
14885       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14886                                  player->index_bit, dig_side);
14887       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14888                                           player->index_bit, dig_side);
14889
14890       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14891                                  player->index_bit, dig_side);
14892       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14893                                           player->index_bit, dig_side);
14894     }
14895
14896     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14897                                player->index_bit, dig_side);
14898     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14899                                         player->index_bit, dig_side);
14900
14901     return MP_NO_ACTION;
14902   }
14903
14904   player->push_delay = -1;
14905
14906   if (is_player)                // function can also be called by EL_PENGUIN
14907   {
14908     if (Tile[x][y] != element)          // really digged/collected something
14909     {
14910       player->is_collecting = !player->is_digging;
14911       player->is_active = TRUE;
14912
14913       player->last_removed_element = element;
14914     }
14915   }
14916
14917   return MP_MOVING;
14918 }
14919
14920 static boolean DigFieldByCE(int x, int y, int digging_element)
14921 {
14922   int element = Tile[x][y];
14923
14924   if (!IS_FREE(x, y))
14925   {
14926     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14927                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14928                   ACTION_BREAKING);
14929
14930     // no element can dig solid indestructible elements
14931     if (IS_INDESTRUCTIBLE(element) &&
14932         !IS_DIGGABLE(element) &&
14933         !IS_COLLECTIBLE(element))
14934       return FALSE;
14935
14936     if (AmoebaNr[x][y] &&
14937         (element == EL_AMOEBA_FULL ||
14938          element == EL_BD_AMOEBA ||
14939          element == EL_AMOEBA_GROWING))
14940     {
14941       AmoebaCnt[AmoebaNr[x][y]]--;
14942       AmoebaCnt2[AmoebaNr[x][y]]--;
14943     }
14944
14945     if (IS_MOVING(x, y))
14946       RemoveMovingField(x, y);
14947     else
14948     {
14949       RemoveField(x, y);
14950       TEST_DrawLevelField(x, y);
14951     }
14952
14953     // if digged element was about to explode, prevent the explosion
14954     ExplodeField[x][y] = EX_TYPE_NONE;
14955
14956     PlayLevelSoundAction(x, y, action);
14957   }
14958
14959   Store[x][y] = EL_EMPTY;
14960
14961   // this makes it possible to leave the removed element again
14962   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14963     Store[x][y] = element;
14964
14965   return TRUE;
14966 }
14967
14968 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14969 {
14970   int jx = player->jx, jy = player->jy;
14971   int x = jx + dx, y = jy + dy;
14972   int snap_direction = (dx == -1 ? MV_LEFT  :
14973                         dx == +1 ? MV_RIGHT :
14974                         dy == -1 ? MV_UP    :
14975                         dy == +1 ? MV_DOWN  : MV_NONE);
14976   boolean can_continue_snapping = (level.continuous_snapping &&
14977                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14978
14979   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14980     return FALSE;
14981
14982   if (!player->active || !IN_LEV_FIELD(x, y))
14983     return FALSE;
14984
14985   if (dx && dy)
14986     return FALSE;
14987
14988   if (!dx && !dy)
14989   {
14990     if (player->MovPos == 0)
14991       player->is_pushing = FALSE;
14992
14993     player->is_snapping = FALSE;
14994
14995     if (player->MovPos == 0)
14996     {
14997       player->is_moving = FALSE;
14998       player->is_digging = FALSE;
14999       player->is_collecting = FALSE;
15000     }
15001
15002     return FALSE;
15003   }
15004
15005   // prevent snapping with already pressed snap key when not allowed
15006   if (player->is_snapping && !can_continue_snapping)
15007     return FALSE;
15008
15009   player->MovDir = snap_direction;
15010
15011   if (player->MovPos == 0)
15012   {
15013     player->is_moving = FALSE;
15014     player->is_digging = FALSE;
15015     player->is_collecting = FALSE;
15016   }
15017
15018   player->is_dropping = FALSE;
15019   player->is_dropping_pressed = FALSE;
15020   player->drop_pressed_delay = 0;
15021
15022   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15023     return FALSE;
15024
15025   player->is_snapping = TRUE;
15026   player->is_active = TRUE;
15027
15028   if (player->MovPos == 0)
15029   {
15030     player->is_moving = FALSE;
15031     player->is_digging = FALSE;
15032     player->is_collecting = FALSE;
15033   }
15034
15035   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15036     TEST_DrawLevelField(player->last_jx, player->last_jy);
15037
15038   TEST_DrawLevelField(x, y);
15039
15040   return TRUE;
15041 }
15042
15043 static boolean DropElement(struct PlayerInfo *player)
15044 {
15045   int old_element, new_element;
15046   int dropx = player->jx, dropy = player->jy;
15047   int drop_direction = player->MovDir;
15048   int drop_side = drop_direction;
15049   int drop_element = get_next_dropped_element(player);
15050
15051   /* do not drop an element on top of another element; when holding drop key
15052      pressed without moving, dropped element must move away before the next
15053      element can be dropped (this is especially important if the next element
15054      is dynamite, which can be placed on background for historical reasons) */
15055   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15056     return MP_ACTION;
15057
15058   if (IS_THROWABLE(drop_element))
15059   {
15060     dropx += GET_DX_FROM_DIR(drop_direction);
15061     dropy += GET_DY_FROM_DIR(drop_direction);
15062
15063     if (!IN_LEV_FIELD(dropx, dropy))
15064       return FALSE;
15065   }
15066
15067   old_element = Tile[dropx][dropy];     // old element at dropping position
15068   new_element = drop_element;           // default: no change when dropping
15069
15070   // check if player is active, not moving and ready to drop
15071   if (!player->active || player->MovPos || player->drop_delay > 0)
15072     return FALSE;
15073
15074   // check if player has anything that can be dropped
15075   if (new_element == EL_UNDEFINED)
15076     return FALSE;
15077
15078   // only set if player has anything that can be dropped
15079   player->is_dropping_pressed = TRUE;
15080
15081   // check if drop key was pressed long enough for EM style dynamite
15082   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15083     return FALSE;
15084
15085   // check if anything can be dropped at the current position
15086   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15087     return FALSE;
15088
15089   // collected custom elements can only be dropped on empty fields
15090   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15091     return FALSE;
15092
15093   if (old_element != EL_EMPTY)
15094     Back[dropx][dropy] = old_element;   // store old element on this field
15095
15096   ResetGfxAnimation(dropx, dropy);
15097   ResetRandomAnimationValue(dropx, dropy);
15098
15099   if (player->inventory_size > 0 ||
15100       player->inventory_infinite_element != EL_UNDEFINED)
15101   {
15102     if (player->inventory_size > 0)
15103     {
15104       player->inventory_size--;
15105
15106       DrawGameDoorValues();
15107
15108       if (new_element == EL_DYNAMITE)
15109         new_element = EL_DYNAMITE_ACTIVE;
15110       else if (new_element == EL_EM_DYNAMITE)
15111         new_element = EL_EM_DYNAMITE_ACTIVE;
15112       else if (new_element == EL_SP_DISK_RED)
15113         new_element = EL_SP_DISK_RED_ACTIVE;
15114     }
15115
15116     Tile[dropx][dropy] = new_element;
15117
15118     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15119       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15120                           el2img(Tile[dropx][dropy]), 0);
15121
15122     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15123
15124     // needed if previous element just changed to "empty" in the last frame
15125     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15126
15127     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15128                                player->index_bit, drop_side);
15129     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15130                                         CE_PLAYER_DROPS_X,
15131                                         player->index_bit, drop_side);
15132
15133     TestIfElementTouchesCustomElement(dropx, dropy);
15134   }
15135   else          // player is dropping a dyna bomb
15136   {
15137     player->dynabombs_left--;
15138
15139     Tile[dropx][dropy] = new_element;
15140
15141     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15142       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15143                           el2img(Tile[dropx][dropy]), 0);
15144
15145     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15146   }
15147
15148   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15149     InitField_WithBug1(dropx, dropy, FALSE);
15150
15151   new_element = Tile[dropx][dropy];     // element might have changed
15152
15153   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15154       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15155   {
15156     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15157       MovDir[dropx][dropy] = drop_direction;
15158
15159     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15160
15161     // do not cause impact style collision by dropping elements that can fall
15162     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15163   }
15164
15165   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15166   player->is_dropping = TRUE;
15167
15168   player->drop_pressed_delay = 0;
15169   player->is_dropping_pressed = FALSE;
15170
15171   player->drop_x = dropx;
15172   player->drop_y = dropy;
15173
15174   return TRUE;
15175 }
15176
15177 // ----------------------------------------------------------------------------
15178 // game sound playing functions
15179 // ----------------------------------------------------------------------------
15180
15181 static int *loop_sound_frame = NULL;
15182 static int *loop_sound_volume = NULL;
15183
15184 void InitPlayLevelSound(void)
15185 {
15186   int num_sounds = getSoundListSize();
15187
15188   checked_free(loop_sound_frame);
15189   checked_free(loop_sound_volume);
15190
15191   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15192   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15193 }
15194
15195 static void PlayLevelSound(int x, int y, int nr)
15196 {
15197   int sx = SCREENX(x), sy = SCREENY(y);
15198   int volume, stereo_position;
15199   int max_distance = 8;
15200   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15201
15202   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15203       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15204     return;
15205
15206   if (!IN_LEV_FIELD(x, y) ||
15207       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15208       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15209     return;
15210
15211   volume = SOUND_MAX_VOLUME;
15212
15213   if (!IN_SCR_FIELD(sx, sy))
15214   {
15215     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15216     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15217
15218     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15219   }
15220
15221   stereo_position = (SOUND_MAX_LEFT +
15222                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15223                      (SCR_FIELDX + 2 * max_distance));
15224
15225   if (IS_LOOP_SOUND(nr))
15226   {
15227     /* This assures that quieter loop sounds do not overwrite louder ones,
15228        while restarting sound volume comparison with each new game frame. */
15229
15230     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15231       return;
15232
15233     loop_sound_volume[nr] = volume;
15234     loop_sound_frame[nr] = FrameCounter;
15235   }
15236
15237   PlaySoundExt(nr, volume, stereo_position, type);
15238 }
15239
15240 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15241 {
15242   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15243                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15244                  y < LEVELY(BY1) ? LEVELY(BY1) :
15245                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15246                  sound_action);
15247 }
15248
15249 static void PlayLevelSoundAction(int x, int y, int action)
15250 {
15251   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15252 }
15253
15254 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15255 {
15256   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15257
15258   if (sound_effect != SND_UNDEFINED)
15259     PlayLevelSound(x, y, sound_effect);
15260 }
15261
15262 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15263                                               int action)
15264 {
15265   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15266
15267   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15268     PlayLevelSound(x, y, sound_effect);
15269 }
15270
15271 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15272 {
15273   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15274
15275   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15276     PlayLevelSound(x, y, sound_effect);
15277 }
15278
15279 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15280 {
15281   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15282
15283   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15284     StopSound(sound_effect);
15285 }
15286
15287 static int getLevelMusicNr(void)
15288 {
15289   int level_pos = level_nr - leveldir_current->first_level;
15290
15291   if (levelset.music[level_nr] != MUS_UNDEFINED)
15292     return levelset.music[level_nr];            // from config file
15293   else
15294     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15295 }
15296
15297 static void FadeLevelSounds(void)
15298 {
15299   FadeSounds();
15300 }
15301
15302 static void FadeLevelMusic(void)
15303 {
15304   int music_nr = getLevelMusicNr();
15305   char *curr_music = getCurrentlyPlayingMusicFilename();
15306   char *next_music = getMusicInfoEntryFilename(music_nr);
15307
15308   if (!strEqual(curr_music, next_music))
15309     FadeMusic();
15310 }
15311
15312 void FadeLevelSoundsAndMusic(void)
15313 {
15314   FadeLevelSounds();
15315   FadeLevelMusic();
15316 }
15317
15318 static void PlayLevelMusic(void)
15319 {
15320   int music_nr = getLevelMusicNr();
15321   char *curr_music = getCurrentlyPlayingMusicFilename();
15322   char *next_music = getMusicInfoEntryFilename(music_nr);
15323
15324   if (!strEqual(curr_music, next_music))
15325     PlayMusicLoop(music_nr);
15326 }
15327
15328 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15329 {
15330   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15331   int offset = 0;
15332   int x = xx - offset;
15333   int y = yy - offset;
15334
15335   switch (sample)
15336   {
15337     case SOUND_blank:
15338       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15339       break;
15340
15341     case SOUND_roll:
15342       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15343       break;
15344
15345     case SOUND_stone:
15346       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15347       break;
15348
15349     case SOUND_nut:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15351       break;
15352
15353     case SOUND_crack:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15355       break;
15356
15357     case SOUND_bug:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15359       break;
15360
15361     case SOUND_tank:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15363       break;
15364
15365     case SOUND_android_clone:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15367       break;
15368
15369     case SOUND_android_move:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15371       break;
15372
15373     case SOUND_spring:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15375       break;
15376
15377     case SOUND_slurp:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15379       break;
15380
15381     case SOUND_eater:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15383       break;
15384
15385     case SOUND_eater_eat:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15387       break;
15388
15389     case SOUND_alien:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15391       break;
15392
15393     case SOUND_collect:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15395       break;
15396
15397     case SOUND_diamond:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15399       break;
15400
15401     case SOUND_squash:
15402       // !!! CHECK THIS !!!
15403 #if 1
15404       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15405 #else
15406       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15407 #endif
15408       break;
15409
15410     case SOUND_wonderfall:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15412       break;
15413
15414     case SOUND_drip:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15416       break;
15417
15418     case SOUND_push:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15420       break;
15421
15422     case SOUND_dirt:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15424       break;
15425
15426     case SOUND_acid:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15428       break;
15429
15430     case SOUND_ball:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15432       break;
15433
15434     case SOUND_slide:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15436       break;
15437
15438     case SOUND_wonder:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15440       break;
15441
15442     case SOUND_door:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15444       break;
15445
15446     case SOUND_exit_open:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15448       break;
15449
15450     case SOUND_exit_leave:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15452       break;
15453
15454     case SOUND_dynamite:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15456       break;
15457
15458     case SOUND_tick:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15460       break;
15461
15462     case SOUND_press:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15464       break;
15465
15466     case SOUND_wheel:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15468       break;
15469
15470     case SOUND_boom:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15472       break;
15473
15474     case SOUND_die:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15476       break;
15477
15478     case SOUND_time:
15479       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15480       break;
15481
15482     default:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15484       break;
15485   }
15486 }
15487
15488 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15489 {
15490   int element = map_element_SP_to_RND(element_sp);
15491   int action = map_action_SP_to_RND(action_sp);
15492   int offset = (setup.sp_show_border_elements ? 0 : 1);
15493   int x = xx - offset;
15494   int y = yy - offset;
15495
15496   PlayLevelSoundElementAction(x, y, element, action);
15497 }
15498
15499 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15500 {
15501   int element = map_element_MM_to_RND(element_mm);
15502   int action = map_action_MM_to_RND(action_mm);
15503   int offset = 0;
15504   int x = xx - offset;
15505   int y = yy - offset;
15506
15507   if (!IS_MM_ELEMENT(element))
15508     element = EL_MM_DEFAULT;
15509
15510   PlayLevelSoundElementAction(x, y, element, action);
15511 }
15512
15513 void PlaySound_MM(int sound_mm)
15514 {
15515   int sound = map_sound_MM_to_RND(sound_mm);
15516
15517   if (sound == SND_UNDEFINED)
15518     return;
15519
15520   PlaySound(sound);
15521 }
15522
15523 void PlaySoundLoop_MM(int sound_mm)
15524 {
15525   int sound = map_sound_MM_to_RND(sound_mm);
15526
15527   if (sound == SND_UNDEFINED)
15528     return;
15529
15530   PlaySoundLoop(sound);
15531 }
15532
15533 void StopSound_MM(int sound_mm)
15534 {
15535   int sound = map_sound_MM_to_RND(sound_mm);
15536
15537   if (sound == SND_UNDEFINED)
15538     return;
15539
15540   StopSound(sound);
15541 }
15542
15543 void RaiseScore(int value)
15544 {
15545   game.score += value;
15546
15547   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15548
15549   DisplayGameControlValues();
15550 }
15551
15552 void RaiseScoreElement(int element)
15553 {
15554   switch (element)
15555   {
15556     case EL_EMERALD:
15557     case EL_BD_DIAMOND:
15558     case EL_EMERALD_YELLOW:
15559     case EL_EMERALD_RED:
15560     case EL_EMERALD_PURPLE:
15561     case EL_SP_INFOTRON:
15562       RaiseScore(level.score[SC_EMERALD]);
15563       break;
15564     case EL_DIAMOND:
15565       RaiseScore(level.score[SC_DIAMOND]);
15566       break;
15567     case EL_CRYSTAL:
15568       RaiseScore(level.score[SC_CRYSTAL]);
15569       break;
15570     case EL_PEARL:
15571       RaiseScore(level.score[SC_PEARL]);
15572       break;
15573     case EL_BUG:
15574     case EL_BD_BUTTERFLY:
15575     case EL_SP_ELECTRON:
15576       RaiseScore(level.score[SC_BUG]);
15577       break;
15578     case EL_SPACESHIP:
15579     case EL_BD_FIREFLY:
15580     case EL_SP_SNIKSNAK:
15581       RaiseScore(level.score[SC_SPACESHIP]);
15582       break;
15583     case EL_YAMYAM:
15584     case EL_DARK_YAMYAM:
15585       RaiseScore(level.score[SC_YAMYAM]);
15586       break;
15587     case EL_ROBOT:
15588       RaiseScore(level.score[SC_ROBOT]);
15589       break;
15590     case EL_PACMAN:
15591       RaiseScore(level.score[SC_PACMAN]);
15592       break;
15593     case EL_NUT:
15594       RaiseScore(level.score[SC_NUT]);
15595       break;
15596     case EL_DYNAMITE:
15597     case EL_EM_DYNAMITE:
15598     case EL_SP_DISK_RED:
15599     case EL_DYNABOMB_INCREASE_NUMBER:
15600     case EL_DYNABOMB_INCREASE_SIZE:
15601     case EL_DYNABOMB_INCREASE_POWER:
15602       RaiseScore(level.score[SC_DYNAMITE]);
15603       break;
15604     case EL_SHIELD_NORMAL:
15605     case EL_SHIELD_DEADLY:
15606       RaiseScore(level.score[SC_SHIELD]);
15607       break;
15608     case EL_EXTRA_TIME:
15609       RaiseScore(level.extra_time_score);
15610       break;
15611     case EL_KEY_1:
15612     case EL_KEY_2:
15613     case EL_KEY_3:
15614     case EL_KEY_4:
15615     case EL_EM_KEY_1:
15616     case EL_EM_KEY_2:
15617     case EL_EM_KEY_3:
15618     case EL_EM_KEY_4:
15619     case EL_EMC_KEY_5:
15620     case EL_EMC_KEY_6:
15621     case EL_EMC_KEY_7:
15622     case EL_EMC_KEY_8:
15623     case EL_DC_KEY_WHITE:
15624       RaiseScore(level.score[SC_KEY]);
15625       break;
15626     default:
15627       RaiseScore(element_info[element].collect_score);
15628       break;
15629   }
15630 }
15631
15632 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15633 {
15634   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15635   {
15636     if (!quick_quit)
15637     {
15638       // prevent short reactivation of overlay buttons while closing door
15639       SetOverlayActive(FALSE);
15640       UnmapGameButtons();
15641
15642       // door may still be open due to skipped or envelope style request
15643       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15644     }
15645
15646     if (network.enabled)
15647       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15648     else
15649     {
15650       if (quick_quit)
15651         FadeSkipNextFadeIn();
15652
15653       SetGameStatus(GAME_MODE_MAIN);
15654
15655       DrawMainMenu();
15656     }
15657   }
15658   else          // continue playing the game
15659   {
15660     if (tape.playing && tape.deactivate_display)
15661       TapeDeactivateDisplayOff(TRUE);
15662
15663     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15664
15665     if (tape.playing && tape.deactivate_display)
15666       TapeDeactivateDisplayOn();
15667   }
15668 }
15669
15670 void RequestQuitGame(boolean escape_key_pressed)
15671 {
15672   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15673   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15674                         level_editor_test_game);
15675   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15676                           quick_quit || score_info_tape_play);
15677
15678   RequestQuitGameExt(skip_request, quick_quit,
15679                      "Do you really want to quit the game?");
15680 }
15681
15682 static char *getRestartGameMessage(void)
15683 {
15684   boolean play_again = hasStartedNetworkGame();
15685   static char message[MAX_OUTPUT_LINESIZE];
15686   char *game_over_text = "Game over!";
15687   char *play_again_text = " Play it again?";
15688
15689   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15690       game_mm.game_over_message != NULL)
15691     game_over_text = game_mm.game_over_message;
15692
15693   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15694            (play_again ? play_again_text : ""));
15695
15696   return message;
15697 }
15698
15699 static void RequestRestartGame(void)
15700 {
15701   char *message = getRestartGameMessage();
15702   boolean has_started_game = hasStartedNetworkGame();
15703   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15704   int door_state = DOOR_CLOSE_1;
15705
15706   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15707   {
15708     CloseDoor(door_state);
15709
15710     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15711   }
15712   else
15713   {
15714     // if game was invoked from level editor, also close tape recorder door
15715     if (level_editor_test_game)
15716       door_state = DOOR_CLOSE_ALL;
15717
15718     CloseDoor(door_state);
15719
15720     SetGameStatus(GAME_MODE_MAIN);
15721
15722     DrawMainMenu();
15723   }
15724 }
15725
15726 boolean CheckRestartGame(void)
15727 {
15728   static int game_over_delay = 0;
15729   int game_over_delay_value = 50;
15730   boolean game_over = checkGameFailed();
15731
15732   if (!game_over)
15733   {
15734     game_over_delay = game_over_delay_value;
15735
15736     return FALSE;
15737   }
15738
15739   if (game_over_delay > 0)
15740   {
15741     if (game_over_delay == game_over_delay_value / 2)
15742       PlaySound(SND_GAME_LOSING);
15743
15744     game_over_delay--;
15745
15746     return FALSE;
15747   }
15748
15749   // do not handle game over if request dialog is already active
15750   if (game.request_active)
15751     return FALSE;
15752
15753   // do not ask to play again if game was never actually played
15754   if (!game.GamePlayed)
15755     return FALSE;
15756
15757   // do not ask to play again if this was disabled in setup menu
15758   if (!setup.ask_on_game_over)
15759     return FALSE;
15760
15761   RequestRestartGame();
15762
15763   return TRUE;
15764 }
15765
15766 boolean checkGameSolved(void)
15767 {
15768   // set for all game engines if level was solved
15769   return game.LevelSolved_GameEnd;
15770 }
15771
15772 boolean checkGameFailed(void)
15773 {
15774   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15775     return (game_em.game_over && !game_em.level_solved);
15776   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15777     return (game_sp.game_over && !game_sp.level_solved);
15778   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15779     return (game_mm.game_over && !game_mm.level_solved);
15780   else                          // GAME_ENGINE_TYPE_RND
15781     return (game.GameOver && !game.LevelSolved);
15782 }
15783
15784 boolean checkGameEnded(void)
15785 {
15786   return (checkGameSolved() || checkGameFailed());
15787 }
15788
15789
15790 // ----------------------------------------------------------------------------
15791 // random generator functions
15792 // ----------------------------------------------------------------------------
15793
15794 unsigned int InitEngineRandom_RND(int seed)
15795 {
15796   game.num_random_calls = 0;
15797
15798   return InitEngineRandom(seed);
15799 }
15800
15801 unsigned int RND(int max)
15802 {
15803   if (max > 0)
15804   {
15805     game.num_random_calls++;
15806
15807     return GetEngineRandom(max);
15808   }
15809
15810   return 0;
15811 }
15812
15813
15814 // ----------------------------------------------------------------------------
15815 // game engine snapshot handling functions
15816 // ----------------------------------------------------------------------------
15817
15818 struct EngineSnapshotInfo
15819 {
15820   // runtime values for custom element collect score
15821   int collect_score[NUM_CUSTOM_ELEMENTS];
15822
15823   // runtime values for group element choice position
15824   int choice_pos[NUM_GROUP_ELEMENTS];
15825
15826   // runtime values for belt position animations
15827   int belt_graphic[4][NUM_BELT_PARTS];
15828   int belt_anim_mode[4][NUM_BELT_PARTS];
15829 };
15830
15831 static struct EngineSnapshotInfo engine_snapshot_rnd;
15832 static char *snapshot_level_identifier = NULL;
15833 static int snapshot_level_nr = -1;
15834
15835 static void SaveEngineSnapshotValues_RND(void)
15836 {
15837   static int belt_base_active_element[4] =
15838   {
15839     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15840     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15841     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15842     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15843   };
15844   int i, j;
15845
15846   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15847   {
15848     int element = EL_CUSTOM_START + i;
15849
15850     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15851   }
15852
15853   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15854   {
15855     int element = EL_GROUP_START + i;
15856
15857     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15858   }
15859
15860   for (i = 0; i < 4; i++)
15861   {
15862     for (j = 0; j < NUM_BELT_PARTS; j++)
15863     {
15864       int element = belt_base_active_element[i] + j;
15865       int graphic = el2img(element);
15866       int anim_mode = graphic_info[graphic].anim_mode;
15867
15868       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15869       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15870     }
15871   }
15872 }
15873
15874 static void LoadEngineSnapshotValues_RND(void)
15875 {
15876   unsigned int num_random_calls = game.num_random_calls;
15877   int i, j;
15878
15879   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15880   {
15881     int element = EL_CUSTOM_START + i;
15882
15883     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15884   }
15885
15886   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15887   {
15888     int element = EL_GROUP_START + i;
15889
15890     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15891   }
15892
15893   for (i = 0; i < 4; i++)
15894   {
15895     for (j = 0; j < NUM_BELT_PARTS; j++)
15896     {
15897       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15898       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15899
15900       graphic_info[graphic].anim_mode = anim_mode;
15901     }
15902   }
15903
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15905   {
15906     InitRND(tape.random_seed);
15907     for (i = 0; i < num_random_calls; i++)
15908       RND(1);
15909   }
15910
15911   if (game.num_random_calls != num_random_calls)
15912   {
15913     Error("number of random calls out of sync");
15914     Error("number of random calls should be %d", num_random_calls);
15915     Error("number of random calls is %d", game.num_random_calls);
15916
15917     Fail("this should not happen -- please debug");
15918   }
15919 }
15920
15921 void FreeEngineSnapshotSingle(void)
15922 {
15923   FreeSnapshotSingle();
15924
15925   setString(&snapshot_level_identifier, NULL);
15926   snapshot_level_nr = -1;
15927 }
15928
15929 void FreeEngineSnapshotList(void)
15930 {
15931   FreeSnapshotList();
15932 }
15933
15934 static ListNode *SaveEngineSnapshotBuffers(void)
15935 {
15936   ListNode *buffers = NULL;
15937
15938   // copy some special values to a structure better suited for the snapshot
15939
15940   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15941     SaveEngineSnapshotValues_RND();
15942   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15943     SaveEngineSnapshotValues_EM();
15944   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15945     SaveEngineSnapshotValues_SP(&buffers);
15946   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15947     SaveEngineSnapshotValues_MM();
15948
15949   // save values stored in special snapshot structure
15950
15951   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15952     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15953   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15954     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15955   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15956     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15957   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15958     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15959
15960   // save further RND engine values
15961
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15965
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15975
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15977
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15999
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16002
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16006
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16009
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16016
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16019
16020 #if 0
16021   ListNode *node = engine_snapshot_list_rnd;
16022   int num_bytes = 0;
16023
16024   while (node != NULL)
16025   {
16026     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16027
16028     node = node->next;
16029   }
16030
16031   Debug("game:playing:SaveEngineSnapshotBuffers",
16032         "size of engine snapshot: %d bytes", num_bytes);
16033 #endif
16034
16035   return buffers;
16036 }
16037
16038 void SaveEngineSnapshotSingle(void)
16039 {
16040   ListNode *buffers = SaveEngineSnapshotBuffers();
16041
16042   // finally save all snapshot buffers to single snapshot
16043   SaveSnapshotSingle(buffers);
16044
16045   // save level identification information
16046   setString(&snapshot_level_identifier, leveldir_current->identifier);
16047   snapshot_level_nr = level_nr;
16048 }
16049
16050 boolean CheckSaveEngineSnapshotToList(void)
16051 {
16052   boolean save_snapshot =
16053     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16054      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16055       game.snapshot.changed_action) ||
16056      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16057       game.snapshot.collected_item));
16058
16059   game.snapshot.changed_action = FALSE;
16060   game.snapshot.collected_item = FALSE;
16061   game.snapshot.save_snapshot = save_snapshot;
16062
16063   return save_snapshot;
16064 }
16065
16066 void SaveEngineSnapshotToList(void)
16067 {
16068   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16069       tape.quick_resume)
16070     return;
16071
16072   ListNode *buffers = SaveEngineSnapshotBuffers();
16073
16074   // finally save all snapshot buffers to snapshot list
16075   SaveSnapshotToList(buffers);
16076 }
16077
16078 void SaveEngineSnapshotToListInitial(void)
16079 {
16080   FreeEngineSnapshotList();
16081
16082   SaveEngineSnapshotToList();
16083 }
16084
16085 static void LoadEngineSnapshotValues(void)
16086 {
16087   // restore special values from snapshot structure
16088
16089   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16090     LoadEngineSnapshotValues_RND();
16091   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16092     LoadEngineSnapshotValues_EM();
16093   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16094     LoadEngineSnapshotValues_SP();
16095   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16096     LoadEngineSnapshotValues_MM();
16097 }
16098
16099 void LoadEngineSnapshotSingle(void)
16100 {
16101   LoadSnapshotSingle();
16102
16103   LoadEngineSnapshotValues();
16104 }
16105
16106 static void LoadEngineSnapshot_Undo(int steps)
16107 {
16108   LoadSnapshotFromList_Older(steps);
16109
16110   LoadEngineSnapshotValues();
16111 }
16112
16113 static void LoadEngineSnapshot_Redo(int steps)
16114 {
16115   LoadSnapshotFromList_Newer(steps);
16116
16117   LoadEngineSnapshotValues();
16118 }
16119
16120 boolean CheckEngineSnapshotSingle(void)
16121 {
16122   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16123           snapshot_level_nr == level_nr);
16124 }
16125
16126 boolean CheckEngineSnapshotList(void)
16127 {
16128   return CheckSnapshotList();
16129 }
16130
16131
16132 // ---------- new game button stuff -------------------------------------------
16133
16134 static struct
16135 {
16136   int graphic;
16137   struct XY *pos;
16138   int gadget_id;
16139   boolean *setup_value;
16140   boolean allowed_on_tape;
16141   boolean is_touch_button;
16142   char *infotext;
16143 } gamebutton_info[NUM_GAME_BUTTONS] =
16144 {
16145   {
16146     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16147     GAME_CTRL_ID_STOP,                          NULL,
16148     TRUE, FALSE,                                "stop game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16152     GAME_CTRL_ID_PAUSE,                         NULL,
16153     TRUE, FALSE,                                "pause game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16157     GAME_CTRL_ID_PLAY,                          NULL,
16158     TRUE, FALSE,                                "play game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16162     GAME_CTRL_ID_UNDO,                          NULL,
16163     TRUE, FALSE,                                "undo step"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16167     GAME_CTRL_ID_REDO,                          NULL,
16168     TRUE, FALSE,                                "redo step"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16172     GAME_CTRL_ID_SAVE,                          NULL,
16173     TRUE, FALSE,                                "save game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16177     GAME_CTRL_ID_PAUSE2,                        NULL,
16178     TRUE, FALSE,                                "pause game"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16182     GAME_CTRL_ID_LOAD,                          NULL,
16183     TRUE, FALSE,                                "load game"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16187     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16188     FALSE, FALSE,                               "stop game"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16192     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16193     FALSE, FALSE,                               "pause game"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16197     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16198     FALSE, FALSE,                               "play game"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16202     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16203     FALSE, TRUE,                                "stop game"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16207     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16208     FALSE, TRUE,                                "pause game"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16212     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16213     TRUE, FALSE,                                "background music on/off"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16217     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16218     TRUE, FALSE,                                "sound loops on/off"
16219   },
16220   {
16221     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16222     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16223     TRUE, FALSE,                                "normal sounds on/off"
16224   },
16225   {
16226     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16227     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16228     FALSE, FALSE,                               "background music on/off"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16232     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16233     FALSE, FALSE,                               "sound loops on/off"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16237     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16238     FALSE, FALSE,                               "normal sounds on/off"
16239   }
16240 };
16241
16242 void CreateGameButtons(void)
16243 {
16244   int i;
16245
16246   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16247   {
16248     int graphic = gamebutton_info[i].graphic;
16249     struct GraphicInfo *gfx = &graphic_info[graphic];
16250     struct XY *pos = gamebutton_info[i].pos;
16251     struct GadgetInfo *gi;
16252     int button_type;
16253     boolean checked;
16254     unsigned int event_mask;
16255     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16256     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16257     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16258     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16259     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16260     int gd_x   = gfx->src_x;
16261     int gd_y   = gfx->src_y;
16262     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16263     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16264     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16265     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16266     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16267     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16268     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16269     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16270     int id = i;
16271
16272     // do not use touch buttons if overlay touch buttons are disabled
16273     if (is_touch_button && !setup.touch.overlay_buttons)
16274       continue;
16275
16276     if (gfx->bitmap == NULL)
16277     {
16278       game_gadget[id] = NULL;
16279
16280       continue;
16281     }
16282
16283     if (id == GAME_CTRL_ID_STOP ||
16284         id == GAME_CTRL_ID_PANEL_STOP ||
16285         id == GAME_CTRL_ID_TOUCH_STOP ||
16286         id == GAME_CTRL_ID_PLAY ||
16287         id == GAME_CTRL_ID_PANEL_PLAY ||
16288         id == GAME_CTRL_ID_SAVE ||
16289         id == GAME_CTRL_ID_LOAD)
16290     {
16291       button_type = GD_TYPE_NORMAL_BUTTON;
16292       checked = FALSE;
16293       event_mask = GD_EVENT_RELEASED;
16294     }
16295     else if (id == GAME_CTRL_ID_UNDO ||
16296              id == GAME_CTRL_ID_REDO)
16297     {
16298       button_type = GD_TYPE_NORMAL_BUTTON;
16299       checked = FALSE;
16300       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16301     }
16302     else
16303     {
16304       button_type = GD_TYPE_CHECK_BUTTON;
16305       checked = (gamebutton_info[i].setup_value != NULL ?
16306                  *gamebutton_info[i].setup_value : FALSE);
16307       event_mask = GD_EVENT_PRESSED;
16308     }
16309
16310     gi = CreateGadget(GDI_CUSTOM_ID, id,
16311                       GDI_IMAGE_ID, graphic,
16312                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16313                       GDI_X, base_x + x,
16314                       GDI_Y, base_y + y,
16315                       GDI_WIDTH, gfx->width,
16316                       GDI_HEIGHT, gfx->height,
16317                       GDI_TYPE, button_type,
16318                       GDI_STATE, GD_BUTTON_UNPRESSED,
16319                       GDI_CHECKED, checked,
16320                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16321                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16322                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16323                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16324                       GDI_DIRECT_DRAW, FALSE,
16325                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16326                       GDI_EVENT_MASK, event_mask,
16327                       GDI_CALLBACK_ACTION, HandleGameButtons,
16328                       GDI_END);
16329
16330     if (gi == NULL)
16331       Fail("cannot create gadget");
16332
16333     game_gadget[id] = gi;
16334   }
16335 }
16336
16337 void FreeGameButtons(void)
16338 {
16339   int i;
16340
16341   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16342     FreeGadget(game_gadget[i]);
16343 }
16344
16345 static void UnmapGameButtonsAtSamePosition(int id)
16346 {
16347   int i;
16348
16349   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16350     if (i != id &&
16351         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16352         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16353       UnmapGadget(game_gadget[i]);
16354 }
16355
16356 static void UnmapGameButtonsAtSamePosition_All(void)
16357 {
16358   if (setup.show_load_save_buttons)
16359   {
16360     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16361     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16362     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16363   }
16364   else if (setup.show_undo_redo_buttons)
16365   {
16366     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16368     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16369   }
16370   else
16371   {
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16374     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16375
16376     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16378     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16379   }
16380 }
16381
16382 void MapLoadSaveButtons(void)
16383 {
16384   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16385   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16386
16387   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16388   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16389 }
16390
16391 void MapUndoRedoButtons(void)
16392 {
16393   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16394   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16395
16396   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16397   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16398 }
16399
16400 void ModifyPauseButtons(void)
16401 {
16402   static int ids[] =
16403   {
16404     GAME_CTRL_ID_PAUSE,
16405     GAME_CTRL_ID_PAUSE2,
16406     GAME_CTRL_ID_PANEL_PAUSE,
16407     GAME_CTRL_ID_TOUCH_PAUSE,
16408     -1
16409   };
16410   int i;
16411
16412   for (i = 0; ids[i] > -1; i++)
16413     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16414 }
16415
16416 static void MapGameButtonsExt(boolean on_tape)
16417 {
16418   int i;
16419
16420   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16421   {
16422     if ((i == GAME_CTRL_ID_UNDO ||
16423          i == GAME_CTRL_ID_REDO) &&
16424         game_status != GAME_MODE_PLAYING)
16425       continue;
16426
16427     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16428       MapGadget(game_gadget[i]);
16429   }
16430
16431   UnmapGameButtonsAtSamePosition_All();
16432
16433   RedrawGameButtons();
16434 }
16435
16436 static void UnmapGameButtonsExt(boolean on_tape)
16437 {
16438   int i;
16439
16440   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16441     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16442       UnmapGadget(game_gadget[i]);
16443 }
16444
16445 static void RedrawGameButtonsExt(boolean on_tape)
16446 {
16447   int i;
16448
16449   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16450     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16451       RedrawGadget(game_gadget[i]);
16452 }
16453
16454 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16455 {
16456   if (gi == NULL)
16457     return;
16458
16459   gi->checked = state;
16460 }
16461
16462 static void RedrawSoundButtonGadget(int id)
16463 {
16464   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16465              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16466              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16467              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16468              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16469              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16470              id);
16471
16472   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16473   RedrawGadget(game_gadget[id2]);
16474 }
16475
16476 void MapGameButtons(void)
16477 {
16478   MapGameButtonsExt(FALSE);
16479 }
16480
16481 void UnmapGameButtons(void)
16482 {
16483   UnmapGameButtonsExt(FALSE);
16484 }
16485
16486 void RedrawGameButtons(void)
16487 {
16488   RedrawGameButtonsExt(FALSE);
16489 }
16490
16491 void MapGameButtonsOnTape(void)
16492 {
16493   MapGameButtonsExt(TRUE);
16494 }
16495
16496 void UnmapGameButtonsOnTape(void)
16497 {
16498   UnmapGameButtonsExt(TRUE);
16499 }
16500
16501 void RedrawGameButtonsOnTape(void)
16502 {
16503   RedrawGameButtonsExt(TRUE);
16504 }
16505
16506 static void GameUndoRedoExt(void)
16507 {
16508   ClearPlayerAction();
16509
16510   tape.pausing = TRUE;
16511
16512   RedrawPlayfield();
16513   UpdateAndDisplayGameControlValues();
16514
16515   DrawCompleteVideoDisplay();
16516   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16517   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16518   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16519
16520   ModifyPauseButtons();
16521
16522   BackToFront();
16523 }
16524
16525 static void GameUndo(int steps)
16526 {
16527   if (!CheckEngineSnapshotList())
16528     return;
16529
16530   int tape_property_bits = tape.property_bits;
16531
16532   LoadEngineSnapshot_Undo(steps);
16533
16534   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16535
16536   GameUndoRedoExt();
16537 }
16538
16539 static void GameRedo(int steps)
16540 {
16541   if (!CheckEngineSnapshotList())
16542     return;
16543
16544   int tape_property_bits = tape.property_bits;
16545
16546   LoadEngineSnapshot_Redo(steps);
16547
16548   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16549
16550   GameUndoRedoExt();
16551 }
16552
16553 static void HandleGameButtonsExt(int id, int button)
16554 {
16555   static boolean game_undo_executed = FALSE;
16556   int steps = BUTTON_STEPSIZE(button);
16557   boolean handle_game_buttons =
16558     (game_status == GAME_MODE_PLAYING ||
16559      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16560
16561   if (!handle_game_buttons)
16562     return;
16563
16564   switch (id)
16565   {
16566     case GAME_CTRL_ID_STOP:
16567     case GAME_CTRL_ID_PANEL_STOP:
16568     case GAME_CTRL_ID_TOUCH_STOP:
16569       TapeStopGame();
16570
16571       break;
16572
16573     case GAME_CTRL_ID_PAUSE:
16574     case GAME_CTRL_ID_PAUSE2:
16575     case GAME_CTRL_ID_PANEL_PAUSE:
16576     case GAME_CTRL_ID_TOUCH_PAUSE:
16577       if (network.enabled && game_status == GAME_MODE_PLAYING)
16578       {
16579         if (tape.pausing)
16580           SendToServer_ContinuePlaying();
16581         else
16582           SendToServer_PausePlaying();
16583       }
16584       else
16585         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16586
16587       game_undo_executed = FALSE;
16588
16589       break;
16590
16591     case GAME_CTRL_ID_PLAY:
16592     case GAME_CTRL_ID_PANEL_PLAY:
16593       if (game_status == GAME_MODE_MAIN)
16594       {
16595         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16596       }
16597       else if (tape.pausing)
16598       {
16599         if (network.enabled)
16600           SendToServer_ContinuePlaying();
16601         else
16602           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16603       }
16604       break;
16605
16606     case GAME_CTRL_ID_UNDO:
16607       // Important: When using "save snapshot when collecting an item" mode,
16608       // load last (current) snapshot for first "undo" after pressing "pause"
16609       // (else the last-but-one snapshot would be loaded, because the snapshot
16610       // pointer already points to the last snapshot when pressing "pause",
16611       // which is fine for "every step/move" mode, but not for "every collect")
16612       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16613           !game_undo_executed)
16614         steps--;
16615
16616       game_undo_executed = TRUE;
16617
16618       GameUndo(steps);
16619       break;
16620
16621     case GAME_CTRL_ID_REDO:
16622       GameRedo(steps);
16623       break;
16624
16625     case GAME_CTRL_ID_SAVE:
16626       TapeQuickSave();
16627       break;
16628
16629     case GAME_CTRL_ID_LOAD:
16630       TapeQuickLoad();
16631       break;
16632
16633     case SOUND_CTRL_ID_MUSIC:
16634     case SOUND_CTRL_ID_PANEL_MUSIC:
16635       if (setup.sound_music)
16636       { 
16637         setup.sound_music = FALSE;
16638
16639         FadeMusic();
16640       }
16641       else if (audio.music_available)
16642       { 
16643         setup.sound = setup.sound_music = TRUE;
16644
16645         SetAudioMode(setup.sound);
16646
16647         if (game_status == GAME_MODE_PLAYING)
16648           PlayLevelMusic();
16649       }
16650
16651       RedrawSoundButtonGadget(id);
16652
16653       break;
16654
16655     case SOUND_CTRL_ID_LOOPS:
16656     case SOUND_CTRL_ID_PANEL_LOOPS:
16657       if (setup.sound_loops)
16658         setup.sound_loops = FALSE;
16659       else if (audio.loops_available)
16660       {
16661         setup.sound = setup.sound_loops = TRUE;
16662
16663         SetAudioMode(setup.sound);
16664       }
16665
16666       RedrawSoundButtonGadget(id);
16667
16668       break;
16669
16670     case SOUND_CTRL_ID_SIMPLE:
16671     case SOUND_CTRL_ID_PANEL_SIMPLE:
16672       if (setup.sound_simple)
16673         setup.sound_simple = FALSE;
16674       else if (audio.sound_available)
16675       {
16676         setup.sound = setup.sound_simple = TRUE;
16677
16678         SetAudioMode(setup.sound);
16679       }
16680
16681       RedrawSoundButtonGadget(id);
16682
16683       break;
16684
16685     default:
16686       break;
16687   }
16688 }
16689
16690 static void HandleGameButtons(struct GadgetInfo *gi)
16691 {
16692   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16693 }
16694
16695 void HandleSoundButtonKeys(Key key)
16696 {
16697   if (key == setup.shortcut.sound_simple)
16698     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16699   else if (key == setup.shortcut.sound_loops)
16700     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16701   else if (key == setup.shortcut.sound_music)
16702     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16703 }