moved code to handle mouse actions to separate function
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       struct ElementChangeInfo *change = &ei->change_page[j];
3264
3265       change->actual_trigger_element = EL_EMPTY;
3266       change->actual_trigger_player = EL_EMPTY;
3267       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3268       change->actual_trigger_side = CH_SIDE_NONE;
3269       change->actual_trigger_ce_value = 0;
3270       change->actual_trigger_ce_score = 0;
3271     }
3272   }
3273
3274   // ---------- initialize trigger events -------------------------------------
3275
3276   // initialize trigger events information
3277   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3278     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3279       trigger_events[i][j] = FALSE;
3280
3281   // add trigger events from element change event properties
3282   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3283   {
3284     struct ElementInfo *ei = &element_info[i];
3285
3286     for (j = 0; j < ei->num_change_pages; j++)
3287     {
3288       struct ElementChangeInfo *change = &ei->change_page[j];
3289
3290       if (!change->can_change_or_has_action)
3291         continue;
3292
3293       if (change->has_event[CE_BY_OTHER_ACTION])
3294       {
3295         int trigger_element = change->trigger_element;
3296
3297         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3298         {
3299           if (change->has_event[k])
3300           {
3301             if (IS_GROUP_ELEMENT(trigger_element))
3302             {
3303               struct ElementGroupInfo *group =
3304                 element_info[trigger_element].group;
3305
3306               for (l = 0; l < group->num_elements_resolved; l++)
3307                 trigger_events[group->element_resolved[l]][k] = TRUE;
3308             }
3309             else if (trigger_element == EL_ANY_ELEMENT)
3310               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3311                 trigger_events[l][k] = TRUE;
3312             else
3313               trigger_events[trigger_element][k] = TRUE;
3314           }
3315         }
3316       }
3317     }
3318   }
3319
3320   // ---------- initialize push delay -----------------------------------------
3321
3322   // initialize push delay values to default
3323   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3324   {
3325     if (!IS_CUSTOM_ELEMENT(i))
3326     {
3327       // set default push delay values (corrected since version 3.0.7-1)
3328       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3329       {
3330         element_info[i].push_delay_fixed = 2;
3331         element_info[i].push_delay_random = 8;
3332       }
3333       else
3334       {
3335         element_info[i].push_delay_fixed = 8;
3336         element_info[i].push_delay_random = 8;
3337       }
3338     }
3339   }
3340
3341   // set push delay value for certain elements from pre-defined list
3342   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3343   {
3344     int e = push_delay_list[i].element;
3345
3346     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3347     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3348   }
3349
3350   // set push delay value for Supaplex elements for newer engine versions
3351   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3352   {
3353     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     {
3355       if (IS_SP_ELEMENT(i))
3356       {
3357         // set SP push delay to just enough to push under a falling zonk
3358         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3359
3360         element_info[i].push_delay_fixed  = delay;
3361         element_info[i].push_delay_random = 0;
3362       }
3363     }
3364   }
3365
3366   // ---------- initialize move stepsize --------------------------------------
3367
3368   // initialize move stepsize values to default
3369   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3370     if (!IS_CUSTOM_ELEMENT(i))
3371       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3372
3373   // set move stepsize value for certain elements from pre-defined list
3374   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3375   {
3376     int e = move_stepsize_list[i].element;
3377
3378     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3379
3380     // set move stepsize value for certain elements for older engine versions
3381     if (use_old_move_stepsize_for_magic_wall)
3382     {
3383       if (e == EL_MAGIC_WALL_FILLING ||
3384           e == EL_MAGIC_WALL_EMPTYING ||
3385           e == EL_BD_MAGIC_WALL_FILLING ||
3386           e == EL_BD_MAGIC_WALL_EMPTYING)
3387         element_info[e].move_stepsize *= 2;
3388     }
3389   }
3390
3391   // ---------- initialize collect score --------------------------------------
3392
3393   // initialize collect score values for custom elements from initial value
3394   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3395     if (IS_CUSTOM_ELEMENT(i))
3396       element_info[i].collect_score = element_info[i].collect_score_initial;
3397
3398   // ---------- initialize collect count --------------------------------------
3399
3400   // initialize collect count values for non-custom elements
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402     if (!IS_CUSTOM_ELEMENT(i))
3403       element_info[i].collect_count_initial = 0;
3404
3405   // add collect count values for all elements from pre-defined list
3406   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3407     element_info[collect_count_list[i].element].collect_count_initial =
3408       collect_count_list[i].count;
3409
3410   // ---------- initialize access direction -----------------------------------
3411
3412   // initialize access direction values to default (access from every side)
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414     if (!IS_CUSTOM_ELEMENT(i))
3415       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3416
3417   // set access direction value for certain elements from pre-defined list
3418   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3419     element_info[access_direction_list[i].element].access_direction =
3420       access_direction_list[i].direction;
3421
3422   // ---------- initialize explosion content ----------------------------------
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (IS_CUSTOM_ELEMENT(i))
3426       continue;
3427
3428     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3429     {
3430       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3431
3432       element_info[i].content.e[x][y] =
3433         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3434          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3435          i == EL_PLAYER_3 ? EL_EMERALD :
3436          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3437          i == EL_MOLE ? EL_EMERALD_RED :
3438          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3439          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3440          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3441          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3442          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3443          i == EL_WALL_EMERALD ? EL_EMERALD :
3444          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3445          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3446          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3447          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3448          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3449          i == EL_WALL_PEARL ? EL_PEARL :
3450          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3451          EL_EMPTY);
3452     }
3453   }
3454
3455   // ---------- initialize recursion detection --------------------------------
3456   recursion_loop_depth = 0;
3457   recursion_loop_detected = FALSE;
3458   recursion_loop_element = EL_UNDEFINED;
3459
3460   // ---------- initialize graphics engine ------------------------------------
3461   game.scroll_delay_value =
3462     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3463      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3464      !setup.forced_scroll_delay           ? 0 :
3465      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3466   game.scroll_delay_value =
3467     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3468
3469   // ---------- initialize game engine snapshots ------------------------------
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     game.snapshot.last_action[i] = 0;
3472   game.snapshot.changed_action = FALSE;
3473   game.snapshot.collected_item = FALSE;
3474   game.snapshot.mode =
3475     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3476      SNAPSHOT_MODE_EVERY_STEP :
3477      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3478      SNAPSHOT_MODE_EVERY_MOVE :
3479      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3480      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3481   game.snapshot.save_snapshot = FALSE;
3482
3483   // ---------- initialize level time for Supaplex engine ---------------------
3484   // Supaplex levels with time limit currently unsupported -- should be added
3485   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3486     level.time = 0;
3487
3488   // ---------- initialize flags for handling game actions --------------------
3489
3490   // set flags for game actions to default values
3491   game.use_key_actions = TRUE;
3492   game.use_mouse_actions = FALSE;
3493
3494   // when using Mirror Magic game engine, handle mouse events only
3495   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3496   {
3497     game.use_key_actions = FALSE;
3498     game.use_mouse_actions = TRUE;
3499   }
3500
3501   // check for custom elements with mouse click events
3502   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3503   {
3504     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3505     {
3506       int element = EL_CUSTOM_START + i;
3507
3508       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3509           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3510           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3511           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3512         game.use_mouse_actions = TRUE;
3513     }
3514   }
3515 }
3516
3517 static int get_num_special_action(int element, int action_first,
3518                                   int action_last)
3519 {
3520   int num_special_action = 0;
3521   int i, j;
3522
3523   for (i = action_first; i <= action_last; i++)
3524   {
3525     boolean found = FALSE;
3526
3527     for (j = 0; j < NUM_DIRECTIONS; j++)
3528       if (el_act_dir2img(element, i, j) !=
3529           el_act_dir2img(element, ACTION_DEFAULT, j))
3530         found = TRUE;
3531
3532     if (found)
3533       num_special_action++;
3534     else
3535       break;
3536   }
3537
3538   return num_special_action;
3539 }
3540
3541
3542 // ============================================================================
3543 // InitGame()
3544 // ----------------------------------------------------------------------------
3545 // initialize and start new game
3546 // ============================================================================
3547
3548 #if DEBUG_INIT_PLAYER
3549 static void DebugPrintPlayerStatus(char *message)
3550 {
3551   int i;
3552
3553   if (!options.debug)
3554     return;
3555
3556   Debug("game:init:player", "%s:", message);
3557
3558   for (i = 0; i < MAX_PLAYERS; i++)
3559   {
3560     struct PlayerInfo *player = &stored_player[i];
3561
3562     Debug("game:init:player",
3563           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3564           i + 1,
3565           player->present,
3566           player->connected,
3567           player->connected_locally,
3568           player->connected_network,
3569           player->active,
3570           (local_player == player ? " (local player)" : ""));
3571   }
3572 }
3573 #endif
3574
3575 void InitGame(void)
3576 {
3577   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3578   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3579   int fade_mask = REDRAW_FIELD;
3580   boolean restarting = (game_status == GAME_MODE_PLAYING);
3581   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3582   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3583   int initial_move_dir = MV_DOWN;
3584   int i, j, x, y;
3585
3586   // required here to update video display before fading (FIX THIS)
3587   DrawMaskedBorder(REDRAW_DOOR_2);
3588
3589   if (!game.restart_level)
3590     CloseDoor(DOOR_CLOSE_1);
3591
3592   if (restarting)
3593   {
3594     // force fading out global animations displayed during game play
3595     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3596   }
3597   else
3598   {
3599     SetGameStatus(GAME_MODE_PLAYING);
3600   }
3601
3602   if (level_editor_test_game)
3603     FadeSkipNextFadeOut();
3604   else
3605     FadeSetEnterScreen();
3606
3607   if (CheckFadeAll())
3608     fade_mask = REDRAW_ALL;
3609
3610   FadeLevelSoundsAndMusic();
3611
3612   ExpireSoundLoops(TRUE);
3613
3614   FadeOut(fade_mask);
3615
3616   if (restarting)
3617   {
3618     // force restarting global animations displayed during game play
3619     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3620
3621     SetGameStatus(GAME_MODE_PLAYING);
3622   }
3623
3624   if (level_editor_test_game)
3625     FadeSkipNextFadeIn();
3626
3627   // needed if different viewport properties defined for playing
3628   ChangeViewportPropertiesIfNeeded();
3629
3630   ClearField();
3631
3632   DrawCompleteVideoDisplay();
3633
3634   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3635
3636   InitGameEngine();
3637   InitGameControlValues();
3638
3639   if (tape.recording)
3640   {
3641     // initialize tape actions from game when recording tape
3642     tape.use_key_actions   = game.use_key_actions;
3643     tape.use_mouse_actions = game.use_mouse_actions;
3644
3645     // initialize visible playfield size when recording tape (for team mode)
3646     tape.scr_fieldx = SCR_FIELDX;
3647     tape.scr_fieldy = SCR_FIELDY;
3648   }
3649
3650   // don't play tapes over network
3651   network_playing = (network.enabled && !tape.playing);
3652
3653   for (i = 0; i < MAX_PLAYERS; i++)
3654   {
3655     struct PlayerInfo *player = &stored_player[i];
3656
3657     player->index_nr = i;
3658     player->index_bit = (1 << i);
3659     player->element_nr = EL_PLAYER_1 + i;
3660
3661     player->present = FALSE;
3662     player->active = FALSE;
3663     player->mapped = FALSE;
3664
3665     player->killed = FALSE;
3666     player->reanimated = FALSE;
3667     player->buried = FALSE;
3668
3669     player->action = 0;
3670     player->effective_action = 0;
3671     player->programmed_action = 0;
3672     player->snap_action = 0;
3673
3674     player->mouse_action.lx = 0;
3675     player->mouse_action.ly = 0;
3676     player->mouse_action.button = 0;
3677     player->mouse_action.button_hint = 0;
3678
3679     player->effective_mouse_action.lx = 0;
3680     player->effective_mouse_action.ly = 0;
3681     player->effective_mouse_action.button = 0;
3682     player->effective_mouse_action.button_hint = 0;
3683
3684     for (j = 0; j < MAX_NUM_KEYS; j++)
3685       player->key[j] = FALSE;
3686
3687     player->num_white_keys = 0;
3688
3689     player->dynabomb_count = 0;
3690     player->dynabomb_size = 1;
3691     player->dynabombs_left = 0;
3692     player->dynabomb_xl = FALSE;
3693
3694     player->MovDir = initial_move_dir;
3695     player->MovPos = 0;
3696     player->GfxPos = 0;
3697     player->GfxDir = initial_move_dir;
3698     player->GfxAction = ACTION_DEFAULT;
3699     player->Frame = 0;
3700     player->StepFrame = 0;
3701
3702     player->initial_element = player->element_nr;
3703     player->artwork_element =
3704       (level.use_artwork_element[i] ? level.artwork_element[i] :
3705        player->element_nr);
3706     player->use_murphy = FALSE;
3707
3708     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3709     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3710
3711     player->gravity = level.initial_player_gravity[i];
3712
3713     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3714
3715     player->actual_frame_counter.count = 0;
3716     player->actual_frame_counter.value = 1;
3717
3718     player->step_counter = 0;
3719
3720     player->last_move_dir = initial_move_dir;
3721
3722     player->is_active = FALSE;
3723
3724     player->is_waiting = FALSE;
3725     player->is_moving = FALSE;
3726     player->is_auto_moving = FALSE;
3727     player->is_digging = FALSE;
3728     player->is_snapping = FALSE;
3729     player->is_collecting = FALSE;
3730     player->is_pushing = FALSE;
3731     player->is_switching = FALSE;
3732     player->is_dropping = FALSE;
3733     player->is_dropping_pressed = FALSE;
3734
3735     player->is_bored = FALSE;
3736     player->is_sleeping = FALSE;
3737
3738     player->was_waiting = TRUE;
3739     player->was_moving = FALSE;
3740     player->was_snapping = FALSE;
3741     player->was_dropping = FALSE;
3742
3743     player->force_dropping = FALSE;
3744
3745     player->frame_counter_bored = -1;
3746     player->frame_counter_sleeping = -1;
3747
3748     player->anim_delay_counter = 0;
3749     player->post_delay_counter = 0;
3750
3751     player->dir_waiting = initial_move_dir;
3752     player->action_waiting = ACTION_DEFAULT;
3753     player->last_action_waiting = ACTION_DEFAULT;
3754     player->special_action_bored = ACTION_DEFAULT;
3755     player->special_action_sleeping = ACTION_DEFAULT;
3756
3757     player->switch_x = -1;
3758     player->switch_y = -1;
3759
3760     player->drop_x = -1;
3761     player->drop_y = -1;
3762
3763     player->show_envelope = 0;
3764
3765     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3766
3767     player->push_delay       = -1;      // initialized when pushing starts
3768     player->push_delay_value = game.initial_push_delay_value;
3769
3770     player->drop_delay = 0;
3771     player->drop_pressed_delay = 0;
3772
3773     player->last_jx = -1;
3774     player->last_jy = -1;
3775     player->jx = -1;
3776     player->jy = -1;
3777
3778     player->shield_normal_time_left = 0;
3779     player->shield_deadly_time_left = 0;
3780
3781     player->last_removed_element = EL_UNDEFINED;
3782
3783     player->inventory_infinite_element = EL_UNDEFINED;
3784     player->inventory_size = 0;
3785
3786     if (level.use_initial_inventory[i])
3787     {
3788       for (j = 0; j < level.initial_inventory_size[i]; j++)
3789       {
3790         int element = level.initial_inventory_content[i][j];
3791         int collect_count = element_info[element].collect_count_initial;
3792         int k;
3793
3794         if (!IS_CUSTOM_ELEMENT(element))
3795           collect_count = 1;
3796
3797         if (collect_count == 0)
3798           player->inventory_infinite_element = element;
3799         else
3800           for (k = 0; k < collect_count; k++)
3801             if (player->inventory_size < MAX_INVENTORY_SIZE)
3802               player->inventory_element[player->inventory_size++] = element;
3803       }
3804     }
3805
3806     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3807     SnapField(player, 0, 0);
3808
3809     map_player_action[i] = i;
3810   }
3811
3812   network_player_action_received = FALSE;
3813
3814   // initial null action
3815   if (network_playing)
3816     SendToServer_MovePlayer(MV_NONE);
3817
3818   FrameCounter = 0;
3819   TimeFrames = 0;
3820   TimePlayed = 0;
3821   TimeLeft = level.time;
3822   TapeTime = 0;
3823
3824   ScreenMovDir = MV_NONE;
3825   ScreenMovPos = 0;
3826   ScreenGfxPos = 0;
3827
3828   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3829
3830   game.robot_wheel_x = -1;
3831   game.robot_wheel_y = -1;
3832
3833   game.exit_x = -1;
3834   game.exit_y = -1;
3835
3836   game.all_players_gone = FALSE;
3837
3838   game.LevelSolved = FALSE;
3839   game.GameOver = FALSE;
3840
3841   game.GamePlayed = !tape.playing;
3842
3843   game.LevelSolved_GameWon = FALSE;
3844   game.LevelSolved_GameEnd = FALSE;
3845   game.LevelSolved_SaveTape = FALSE;
3846   game.LevelSolved_SaveScore = FALSE;
3847
3848   game.LevelSolved_CountingTime = 0;
3849   game.LevelSolved_CountingScore = 0;
3850   game.LevelSolved_CountingHealth = 0;
3851
3852   game.panel.active = TRUE;
3853
3854   game.no_level_time_limit = (level.time == 0);
3855   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3856
3857   game.yamyam_content_nr = 0;
3858   game.robot_wheel_active = FALSE;
3859   game.magic_wall_active = FALSE;
3860   game.magic_wall_time_left = 0;
3861   game.light_time_left = 0;
3862   game.timegate_time_left = 0;
3863   game.switchgate_pos = 0;
3864   game.wind_direction = level.wind_direction_initial;
3865
3866   game.time_final = 0;
3867   game.score_time_final = 0;
3868
3869   game.score = 0;
3870   game.score_final = 0;
3871
3872   game.health = MAX_HEALTH;
3873   game.health_final = MAX_HEALTH;
3874
3875   game.gems_still_needed = level.gems_needed;
3876   game.sokoban_fields_still_needed = 0;
3877   game.sokoban_objects_still_needed = 0;
3878   game.lights_still_needed = 0;
3879   game.players_still_needed = 0;
3880   game.friends_still_needed = 0;
3881
3882   game.lenses_time_left = 0;
3883   game.magnify_time_left = 0;
3884
3885   game.ball_active = level.ball_active_initial;
3886   game.ball_content_nr = 0;
3887
3888   game.explosions_delayed = TRUE;
3889
3890   game.envelope_active = FALSE;
3891
3892   // special case: set custom artwork setting to initial value
3893   game.use_masked_elements = game.use_masked_elements_initial;
3894
3895   for (i = 0; i < NUM_BELTS; i++)
3896   {
3897     game.belt_dir[i] = MV_NONE;
3898     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3899   }
3900
3901   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3902     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3903
3904 #if DEBUG_INIT_PLAYER
3905   DebugPrintPlayerStatus("Player status at level initialization");
3906 #endif
3907
3908   SCAN_PLAYFIELD(x, y)
3909   {
3910     Tile[x][y] = Last[x][y] = level.field[x][y];
3911     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3912     ChangeDelay[x][y] = 0;
3913     ChangePage[x][y] = -1;
3914     CustomValue[x][y] = 0;              // initialized in InitField()
3915     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3916     AmoebaNr[x][y] = 0;
3917     WasJustMoving[x][y] = 0;
3918     WasJustFalling[x][y] = 0;
3919     CheckCollision[x][y] = 0;
3920     CheckImpact[x][y] = 0;
3921     Stop[x][y] = FALSE;
3922     Pushed[x][y] = FALSE;
3923
3924     ChangeCount[x][y] = 0;
3925     ChangeEvent[x][y] = -1;
3926
3927     ExplodePhase[x][y] = 0;
3928     ExplodeDelay[x][y] = 0;
3929     ExplodeField[x][y] = EX_TYPE_NONE;
3930
3931     RunnerVisit[x][y] = 0;
3932     PlayerVisit[x][y] = 0;
3933
3934     GfxFrame[x][y] = 0;
3935     GfxRandom[x][y] = INIT_GFX_RANDOM();
3936     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3937     GfxElement[x][y] = EL_UNDEFINED;
3938     GfxElementEmpty[x][y] = EL_EMPTY;
3939     GfxAction[x][y] = ACTION_DEFAULT;
3940     GfxDir[x][y] = MV_NONE;
3941     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3942   }
3943
3944   SCAN_PLAYFIELD(x, y)
3945   {
3946     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3947       emulate_bd = FALSE;
3948     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3949       emulate_sp = FALSE;
3950
3951     InitField(x, y, TRUE);
3952
3953     ResetGfxAnimation(x, y);
3954   }
3955
3956   InitBeltMovement();
3957
3958   for (i = 0; i < MAX_PLAYERS; i++)
3959   {
3960     struct PlayerInfo *player = &stored_player[i];
3961
3962     // set number of special actions for bored and sleeping animation
3963     player->num_special_action_bored =
3964       get_num_special_action(player->artwork_element,
3965                              ACTION_BORING_1, ACTION_BORING_LAST);
3966     player->num_special_action_sleeping =
3967       get_num_special_action(player->artwork_element,
3968                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3969   }
3970
3971   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3972                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3973
3974   // initialize type of slippery elements
3975   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3976   {
3977     if (!IS_CUSTOM_ELEMENT(i))
3978     {
3979       // default: elements slip down either to the left or right randomly
3980       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3981
3982       // SP style elements prefer to slip down on the left side
3983       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985
3986       // BD style elements prefer to slip down on the left side
3987       if (game.emulation == EMU_BOULDERDASH)
3988         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3989     }
3990   }
3991
3992   // initialize explosion and ignition delay
3993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3994   {
3995     if (!IS_CUSTOM_ELEMENT(i))
3996     {
3997       int num_phase = 8;
3998       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3999                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4000                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4001       int last_phase = (num_phase + 1) * delay;
4002       int half_phase = (num_phase / 2) * delay;
4003
4004       element_info[i].explosion_delay = last_phase - 1;
4005       element_info[i].ignition_delay = half_phase;
4006
4007       if (i == EL_BLACK_ORB)
4008         element_info[i].ignition_delay = 1;
4009     }
4010   }
4011
4012   // correct non-moving belts to start moving left
4013   for (i = 0; i < NUM_BELTS; i++)
4014     if (game.belt_dir[i] == MV_NONE)
4015       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4016
4017 #if USE_NEW_PLAYER_ASSIGNMENTS
4018   // use preferred player also in local single-player mode
4019   if (!network.enabled && !game.team_mode)
4020   {
4021     int new_index_nr = setup.network_player_nr;
4022
4023     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4024     {
4025       for (i = 0; i < MAX_PLAYERS; i++)
4026         stored_player[i].connected_locally = FALSE;
4027
4028       stored_player[new_index_nr].connected_locally = TRUE;
4029     }
4030   }
4031
4032   for (i = 0; i < MAX_PLAYERS; i++)
4033   {
4034     stored_player[i].connected = FALSE;
4035
4036     // in network game mode, the local player might not be the first player
4037     if (stored_player[i].connected_locally)
4038       local_player = &stored_player[i];
4039   }
4040
4041   if (!network.enabled)
4042     local_player->connected = TRUE;
4043
4044   if (tape.playing)
4045   {
4046     for (i = 0; i < MAX_PLAYERS; i++)
4047       stored_player[i].connected = tape.player_participates[i];
4048   }
4049   else if (network.enabled)
4050   {
4051     // add team mode players connected over the network (needed for correct
4052     // assignment of player figures from level to locally playing players)
4053
4054     for (i = 0; i < MAX_PLAYERS; i++)
4055       if (stored_player[i].connected_network)
4056         stored_player[i].connected = TRUE;
4057   }
4058   else if (game.team_mode)
4059   {
4060     // try to guess locally connected team mode players (needed for correct
4061     // assignment of player figures from level to locally playing players)
4062
4063     for (i = 0; i < MAX_PLAYERS; i++)
4064       if (setup.input[i].use_joystick ||
4065           setup.input[i].key.left != KSYM_UNDEFINED)
4066         stored_player[i].connected = TRUE;
4067   }
4068
4069 #if DEBUG_INIT_PLAYER
4070   DebugPrintPlayerStatus("Player status after level initialization");
4071 #endif
4072
4073 #if DEBUG_INIT_PLAYER
4074   Debug("game:init:player", "Reassigning players ...");
4075 #endif
4076
4077   // check if any connected player was not found in playfield
4078   for (i = 0; i < MAX_PLAYERS; i++)
4079   {
4080     struct PlayerInfo *player = &stored_player[i];
4081
4082     if (player->connected && !player->present)
4083     {
4084       struct PlayerInfo *field_player = NULL;
4085
4086 #if DEBUG_INIT_PLAYER
4087       Debug("game:init:player",
4088             "- looking for field player for player %d ...", i + 1);
4089 #endif
4090
4091       // assign first free player found that is present in the playfield
4092
4093       // first try: look for unmapped playfield player that is not connected
4094       for (j = 0; j < MAX_PLAYERS; j++)
4095         if (field_player == NULL &&
4096             stored_player[j].present &&
4097             !stored_player[j].mapped &&
4098             !stored_player[j].connected)
4099           field_player = &stored_player[j];
4100
4101       // second try: look for *any* unmapped playfield player
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103         if (field_player == NULL &&
4104             stored_player[j].present &&
4105             !stored_player[j].mapped)
4106           field_player = &stored_player[j];
4107
4108       if (field_player != NULL)
4109       {
4110         int jx = field_player->jx, jy = field_player->jy;
4111
4112 #if DEBUG_INIT_PLAYER
4113         Debug("game:init:player", "- found player %d",
4114               field_player->index_nr + 1);
4115 #endif
4116
4117         player->present = FALSE;
4118         player->active = FALSE;
4119
4120         field_player->present = TRUE;
4121         field_player->active = TRUE;
4122
4123         /*
4124         player->initial_element = field_player->initial_element;
4125         player->artwork_element = field_player->artwork_element;
4126
4127         player->block_last_field       = field_player->block_last_field;
4128         player->block_delay_adjustment = field_player->block_delay_adjustment;
4129         */
4130
4131         StorePlayer[jx][jy] = field_player->element_nr;
4132
4133         field_player->jx = field_player->last_jx = jx;
4134         field_player->jy = field_player->last_jy = jy;
4135
4136         if (local_player == player)
4137           local_player = field_player;
4138
4139         map_player_action[field_player->index_nr] = i;
4140
4141         field_player->mapped = TRUE;
4142
4143 #if DEBUG_INIT_PLAYER
4144         Debug("game:init:player", "- map_player_action[%d] == %d",
4145               field_player->index_nr + 1, i + 1);
4146 #endif
4147       }
4148     }
4149
4150     if (player->connected && player->present)
4151       player->mapped = TRUE;
4152   }
4153
4154 #if DEBUG_INIT_PLAYER
4155   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4156 #endif
4157
4158 #else
4159
4160   // check if any connected player was not found in playfield
4161   for (i = 0; i < MAX_PLAYERS; i++)
4162   {
4163     struct PlayerInfo *player = &stored_player[i];
4164
4165     if (player->connected && !player->present)
4166     {
4167       for (j = 0; j < MAX_PLAYERS; j++)
4168       {
4169         struct PlayerInfo *field_player = &stored_player[j];
4170         int jx = field_player->jx, jy = field_player->jy;
4171
4172         // assign first free player found that is present in the playfield
4173         if (field_player->present && !field_player->connected)
4174         {
4175           player->present = TRUE;
4176           player->active = TRUE;
4177
4178           field_player->present = FALSE;
4179           field_player->active = FALSE;
4180
4181           player->initial_element = field_player->initial_element;
4182           player->artwork_element = field_player->artwork_element;
4183
4184           player->block_last_field       = field_player->block_last_field;
4185           player->block_delay_adjustment = field_player->block_delay_adjustment;
4186
4187           StorePlayer[jx][jy] = player->element_nr;
4188
4189           player->jx = player->last_jx = jx;
4190           player->jy = player->last_jy = jy;
4191
4192           break;
4193         }
4194       }
4195     }
4196   }
4197 #endif
4198
4199 #if 0
4200   Debug("game:init:player", "local_player->present == %d",
4201         local_player->present);
4202 #endif
4203
4204   // set focus to local player for network games, else to all players
4205   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4206   game.centered_player_nr_next = game.centered_player_nr;
4207   game.set_centered_player = FALSE;
4208   game.set_centered_player_wrap = FALSE;
4209
4210   if (network_playing && tape.recording)
4211   {
4212     // store client dependent player focus when recording network games
4213     tape.centered_player_nr_next = game.centered_player_nr_next;
4214     tape.set_centered_player = TRUE;
4215   }
4216
4217   if (tape.playing)
4218   {
4219     // when playing a tape, eliminate all players who do not participate
4220
4221 #if USE_NEW_PLAYER_ASSIGNMENTS
4222
4223     if (!game.team_mode)
4224     {
4225       for (i = 0; i < MAX_PLAYERS; i++)
4226       {
4227         if (stored_player[i].active &&
4228             !tape.player_participates[map_player_action[i]])
4229         {
4230           struct PlayerInfo *player = &stored_player[i];
4231           int jx = player->jx, jy = player->jy;
4232
4233 #if DEBUG_INIT_PLAYER
4234           Debug("game:init:player", "Removing player %d at (%d, %d)",
4235                 i + 1, jx, jy);
4236 #endif
4237
4238           player->active = FALSE;
4239           StorePlayer[jx][jy] = 0;
4240           Tile[jx][jy] = EL_EMPTY;
4241         }
4242       }
4243     }
4244
4245 #else
4246
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248     {
4249       if (stored_player[i].active &&
4250           !tape.player_participates[i])
4251       {
4252         struct PlayerInfo *player = &stored_player[i];
4253         int jx = player->jx, jy = player->jy;
4254
4255         player->active = FALSE;
4256         StorePlayer[jx][jy] = 0;
4257         Tile[jx][jy] = EL_EMPTY;
4258       }
4259     }
4260 #endif
4261   }
4262   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4263   {
4264     // when in single player mode, eliminate all but the local player
4265
4266     for (i = 0; i < MAX_PLAYERS; i++)
4267     {
4268       struct PlayerInfo *player = &stored_player[i];
4269
4270       if (player->active && player != local_player)
4271       {
4272         int jx = player->jx, jy = player->jy;
4273
4274         player->active = FALSE;
4275         player->present = FALSE;
4276
4277         StorePlayer[jx][jy] = 0;
4278         Tile[jx][jy] = EL_EMPTY;
4279       }
4280     }
4281   }
4282
4283   for (i = 0; i < MAX_PLAYERS; i++)
4284     if (stored_player[i].active)
4285       game.players_still_needed++;
4286
4287   if (level.solved_by_one_player)
4288     game.players_still_needed = 1;
4289
4290   // when recording the game, store which players take part in the game
4291   if (tape.recording)
4292   {
4293 #if USE_NEW_PLAYER_ASSIGNMENTS
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].connected)
4296         tape.player_participates[i] = TRUE;
4297 #else
4298     for (i = 0; i < MAX_PLAYERS; i++)
4299       if (stored_player[i].active)
4300         tape.player_participates[i] = TRUE;
4301 #endif
4302   }
4303
4304 #if DEBUG_INIT_PLAYER
4305   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4306 #endif
4307
4308   if (BorderElement == EL_EMPTY)
4309   {
4310     SBX_Left = 0;
4311     SBX_Right = lev_fieldx - SCR_FIELDX;
4312     SBY_Upper = 0;
4313     SBY_Lower = lev_fieldy - SCR_FIELDY;
4314   }
4315   else
4316   {
4317     SBX_Left = -1;
4318     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4319     SBY_Upper = -1;
4320     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4321   }
4322
4323   if (full_lev_fieldx <= SCR_FIELDX)
4324     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4325   if (full_lev_fieldy <= SCR_FIELDY)
4326     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4327
4328   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4329     SBX_Left--;
4330   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4331     SBY_Upper--;
4332
4333   // if local player not found, look for custom element that might create
4334   // the player (make some assumptions about the right custom element)
4335   if (!local_player->present)
4336   {
4337     int start_x = 0, start_y = 0;
4338     int found_rating = 0;
4339     int found_element = EL_UNDEFINED;
4340     int player_nr = local_player->index_nr;
4341
4342     SCAN_PLAYFIELD(x, y)
4343     {
4344       int element = Tile[x][y];
4345       int content;
4346       int xx, yy;
4347       boolean is_player;
4348
4349       if (level.use_start_element[player_nr] &&
4350           level.start_element[player_nr] == element &&
4351           found_rating < 4)
4352       {
4353         start_x = x;
4354         start_y = y;
4355
4356         found_rating = 4;
4357         found_element = element;
4358       }
4359
4360       if (!IS_CUSTOM_ELEMENT(element))
4361         continue;
4362
4363       if (CAN_CHANGE(element))
4364       {
4365         for (i = 0; i < element_info[element].num_change_pages; i++)
4366         {
4367           // check for player created from custom element as single target
4368           content = element_info[element].change_page[i].target_element;
4369           is_player = IS_PLAYER_ELEMENT(content);
4370
4371           if (is_player && (found_rating < 3 ||
4372                             (found_rating == 3 && element < found_element)))
4373           {
4374             start_x = x;
4375             start_y = y;
4376
4377             found_rating = 3;
4378             found_element = element;
4379           }
4380         }
4381       }
4382
4383       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4384       {
4385         // check for player created from custom element as explosion content
4386         content = element_info[element].content.e[xx][yy];
4387         is_player = IS_PLAYER_ELEMENT(content);
4388
4389         if (is_player && (found_rating < 2 ||
4390                           (found_rating == 2 && element < found_element)))
4391         {
4392           start_x = x + xx - 1;
4393           start_y = y + yy - 1;
4394
4395           found_rating = 2;
4396           found_element = element;
4397         }
4398
4399         if (!CAN_CHANGE(element))
4400           continue;
4401
4402         for (i = 0; i < element_info[element].num_change_pages; i++)
4403         {
4404           // check for player created from custom element as extended target
4405           content =
4406             element_info[element].change_page[i].target_content.e[xx][yy];
4407
4408           is_player = IS_PLAYER_ELEMENT(content);
4409
4410           if (is_player && (found_rating < 1 ||
4411                             (found_rating == 1 && element < found_element)))
4412           {
4413             start_x = x + xx - 1;
4414             start_y = y + yy - 1;
4415
4416             found_rating = 1;
4417             found_element = element;
4418           }
4419         }
4420       }
4421     }
4422
4423     scroll_x = SCROLL_POSITION_X(start_x);
4424     scroll_y = SCROLL_POSITION_Y(start_y);
4425   }
4426   else
4427   {
4428     scroll_x = SCROLL_POSITION_X(local_player->jx);
4429     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4430   }
4431
4432   // !!! FIX THIS (START) !!!
4433   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4434   {
4435     InitGameEngine_EM();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4438   {
4439     InitGameEngine_SP();
4440   }
4441   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4442   {
4443     InitGameEngine_MM();
4444   }
4445   else
4446   {
4447     DrawLevel(REDRAW_FIELD);
4448     DrawAllPlayers();
4449
4450     // after drawing the level, correct some elements
4451     if (game.timegate_time_left == 0)
4452       CloseAllOpenTimegates();
4453   }
4454
4455   // blit playfield from scroll buffer to normal back buffer for fading in
4456   BlitScreenToBitmap(backbuffer);
4457   // !!! FIX THIS (END) !!!
4458
4459   DrawMaskedBorder(fade_mask);
4460
4461   FadeIn(fade_mask);
4462
4463 #if 1
4464   // full screen redraw is required at this point in the following cases:
4465   // - special editor door undrawn when game was started from level editor
4466   // - drawing area (playfield) was changed and has to be removed completely
4467   redraw_mask = REDRAW_ALL;
4468   BackToFront();
4469 #endif
4470
4471   if (!game.restart_level)
4472   {
4473     // copy default game door content to main double buffer
4474
4475     // !!! CHECK AGAIN !!!
4476     SetPanelBackground();
4477     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4478     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4479   }
4480
4481   SetPanelBackground();
4482   SetDrawBackgroundMask(REDRAW_DOOR_1);
4483
4484   UpdateAndDisplayGameControlValues();
4485
4486   if (!game.restart_level)
4487   {
4488     UnmapGameButtons();
4489     UnmapTapeButtons();
4490
4491     FreeGameButtons();
4492     CreateGameButtons();
4493
4494     MapGameButtons();
4495     MapTapeButtons();
4496
4497     // copy actual game door content to door double buffer for OpenDoor()
4498     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4499
4500     OpenDoor(DOOR_OPEN_ALL);
4501
4502     KeyboardAutoRepeatOffUnlessAutoplay();
4503
4504 #if DEBUG_INIT_PLAYER
4505     DebugPrintPlayerStatus("Player status (final)");
4506 #endif
4507   }
4508
4509   UnmapAllGadgets();
4510
4511   MapGameButtons();
4512   MapTapeButtons();
4513
4514   if (!game.restart_level && !tape.playing)
4515   {
4516     LevelStats_incPlayed(level_nr);
4517
4518     SaveLevelSetup_SeriesInfo();
4519   }
4520
4521   game.restart_level = FALSE;
4522
4523   game.request_active = FALSE;
4524   game.request_active_or_moving = FALSE;
4525
4526   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4527     InitGameActions_MM();
4528
4529   SaveEngineSnapshotToListInitial();
4530
4531   if (!game.restart_level)
4532   {
4533     PlaySound(SND_GAME_STARTING);
4534
4535     if (setup.sound_music)
4536       PlayLevelMusic();
4537   }
4538
4539   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4540 }
4541
4542 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4543                         int actual_player_x, int actual_player_y)
4544 {
4545   // this is used for non-R'n'D game engines to update certain engine values
4546
4547   // needed to determine if sounds are played within the visible screen area
4548   scroll_x = actual_scroll_x;
4549   scroll_y = actual_scroll_y;
4550
4551   // needed to get player position for "follow finger" playing input method
4552   local_player->jx = actual_player_x;
4553   local_player->jy = actual_player_y;
4554 }
4555
4556 void InitMovDir(int x, int y)
4557 {
4558   int i, element = Tile[x][y];
4559   static int xy[4][2] =
4560   {
4561     {  0, +1 },
4562     { +1,  0 },
4563     {  0, -1 },
4564     { -1,  0 }
4565   };
4566   static int direction[3][4] =
4567   {
4568     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4569     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4570     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4571   };
4572
4573   switch (element)
4574   {
4575     case EL_BUG_RIGHT:
4576     case EL_BUG_UP:
4577     case EL_BUG_LEFT:
4578     case EL_BUG_DOWN:
4579       Tile[x][y] = EL_BUG;
4580       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4581       break;
4582
4583     case EL_SPACESHIP_RIGHT:
4584     case EL_SPACESHIP_UP:
4585     case EL_SPACESHIP_LEFT:
4586     case EL_SPACESHIP_DOWN:
4587       Tile[x][y] = EL_SPACESHIP;
4588       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4589       break;
4590
4591     case EL_BD_BUTTERFLY_RIGHT:
4592     case EL_BD_BUTTERFLY_UP:
4593     case EL_BD_BUTTERFLY_LEFT:
4594     case EL_BD_BUTTERFLY_DOWN:
4595       Tile[x][y] = EL_BD_BUTTERFLY;
4596       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4597       break;
4598
4599     case EL_BD_FIREFLY_RIGHT:
4600     case EL_BD_FIREFLY_UP:
4601     case EL_BD_FIREFLY_LEFT:
4602     case EL_BD_FIREFLY_DOWN:
4603       Tile[x][y] = EL_BD_FIREFLY;
4604       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4605       break;
4606
4607     case EL_PACMAN_RIGHT:
4608     case EL_PACMAN_UP:
4609     case EL_PACMAN_LEFT:
4610     case EL_PACMAN_DOWN:
4611       Tile[x][y] = EL_PACMAN;
4612       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4613       break;
4614
4615     case EL_YAMYAM_LEFT:
4616     case EL_YAMYAM_RIGHT:
4617     case EL_YAMYAM_UP:
4618     case EL_YAMYAM_DOWN:
4619       Tile[x][y] = EL_YAMYAM;
4620       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4621       break;
4622
4623     case EL_SP_SNIKSNAK:
4624       MovDir[x][y] = MV_UP;
4625       break;
4626
4627     case EL_SP_ELECTRON:
4628       MovDir[x][y] = MV_LEFT;
4629       break;
4630
4631     case EL_MOLE_LEFT:
4632     case EL_MOLE_RIGHT:
4633     case EL_MOLE_UP:
4634     case EL_MOLE_DOWN:
4635       Tile[x][y] = EL_MOLE;
4636       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4637       break;
4638
4639     case EL_SPRING_LEFT:
4640     case EL_SPRING_RIGHT:
4641       Tile[x][y] = EL_SPRING;
4642       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4643       break;
4644
4645     default:
4646       if (IS_CUSTOM_ELEMENT(element))
4647       {
4648         struct ElementInfo *ei = &element_info[element];
4649         int move_direction_initial = ei->move_direction_initial;
4650         int move_pattern = ei->move_pattern;
4651
4652         if (move_direction_initial == MV_START_PREVIOUS)
4653         {
4654           if (MovDir[x][y] != MV_NONE)
4655             return;
4656
4657           move_direction_initial = MV_START_AUTOMATIC;
4658         }
4659
4660         if (move_direction_initial == MV_START_RANDOM)
4661           MovDir[x][y] = 1 << RND(4);
4662         else if (move_direction_initial & MV_ANY_DIRECTION)
4663           MovDir[x][y] = move_direction_initial;
4664         else if (move_pattern == MV_ALL_DIRECTIONS ||
4665                  move_pattern == MV_TURNING_LEFT ||
4666                  move_pattern == MV_TURNING_RIGHT ||
4667                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4668                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4669                  move_pattern == MV_TURNING_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_pattern == MV_HORIZONTAL)
4672           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4673         else if (move_pattern == MV_VERTICAL)
4674           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4675         else if (move_pattern & MV_ANY_DIRECTION)
4676           MovDir[x][y] = element_info[element].move_pattern;
4677         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4678                  move_pattern == MV_ALONG_RIGHT_SIDE)
4679         {
4680           // use random direction as default start direction
4681           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4682             MovDir[x][y] = 1 << RND(4);
4683
4684           for (i = 0; i < NUM_DIRECTIONS; i++)
4685           {
4686             int x1 = x + xy[i][0];
4687             int y1 = y + xy[i][1];
4688
4689             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4690             {
4691               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4692                 MovDir[x][y] = direction[0][i];
4693               else
4694                 MovDir[x][y] = direction[1][i];
4695
4696               break;
4697             }
4698           }
4699         }                
4700       }
4701       else
4702       {
4703         MovDir[x][y] = 1 << RND(4);
4704
4705         if (element != EL_BUG &&
4706             element != EL_SPACESHIP &&
4707             element != EL_BD_BUTTERFLY &&
4708             element != EL_BD_FIREFLY)
4709           break;
4710
4711         for (i = 0; i < NUM_DIRECTIONS; i++)
4712         {
4713           int x1 = x + xy[i][0];
4714           int y1 = y + xy[i][1];
4715
4716           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4717           {
4718             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4719             {
4720               MovDir[x][y] = direction[0][i];
4721               break;
4722             }
4723             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4724                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725             {
4726               MovDir[x][y] = direction[1][i];
4727               break;
4728             }
4729           }
4730         }
4731       }
4732       break;
4733   }
4734
4735   GfxDir[x][y] = MovDir[x][y];
4736 }
4737
4738 void InitAmoebaNr(int x, int y)
4739 {
4740   int i;
4741   int group_nr = AmoebaNeighbourNr(x, y);
4742
4743   if (group_nr == 0)
4744   {
4745     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4746     {
4747       if (AmoebaCnt[i] == 0)
4748       {
4749         group_nr = i;
4750         break;
4751       }
4752     }
4753   }
4754
4755   AmoebaNr[x][y] = group_nr;
4756   AmoebaCnt[group_nr]++;
4757   AmoebaCnt2[group_nr]++;
4758 }
4759
4760 static void LevelSolved_SetFinalGameValues(void)
4761 {
4762   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4763   game.score_time_final = (level.use_step_counter ? TimePlayed :
4764                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4765
4766   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4767                       game_em.lev->score :
4768                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4769                       game_mm.score :
4770                       game.score);
4771
4772   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4773                        MM_HEALTH(game_mm.laser_overload_value) :
4774                        game.health);
4775
4776   game.LevelSolved_CountingTime = game.time_final;
4777   game.LevelSolved_CountingScore = game.score_final;
4778   game.LevelSolved_CountingHealth = game.health_final;
4779 }
4780
4781 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4782 {
4783   game.LevelSolved_CountingTime = time;
4784   game.LevelSolved_CountingScore = score;
4785   game.LevelSolved_CountingHealth = health;
4786
4787   game_panel_controls[GAME_PANEL_TIME].value = time;
4788   game_panel_controls[GAME_PANEL_SCORE].value = score;
4789   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4790
4791   DisplayGameControlValues();
4792 }
4793
4794 static void LevelSolved(void)
4795 {
4796   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4797       game.players_still_needed > 0)
4798     return;
4799
4800   game.LevelSolved = TRUE;
4801   game.GameOver = TRUE;
4802
4803   tape.solved = TRUE;
4804
4805   // needed here to display correct panel values while player walks into exit
4806   LevelSolved_SetFinalGameValues();
4807 }
4808
4809 void GameWon(void)
4810 {
4811   static int time_count_steps;
4812   static int time, time_final;
4813   static float score, score_final; // needed for time score < 10 for 10 seconds
4814   static int health, health_final;
4815   static int game_over_delay_1 = 0;
4816   static int game_over_delay_2 = 0;
4817   static int game_over_delay_3 = 0;
4818   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4819   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4820
4821   if (!game.LevelSolved_GameWon)
4822   {
4823     int i;
4824
4825     // do not start end game actions before the player stops moving (to exit)
4826     if (local_player->active && local_player->MovPos)
4827       return;
4828
4829     // calculate final game values after player finished walking into exit
4830     LevelSolved_SetFinalGameValues();
4831
4832     game.LevelSolved_GameWon = TRUE;
4833     game.LevelSolved_SaveTape = tape.recording;
4834     game.LevelSolved_SaveScore = !tape.playing;
4835
4836     if (!tape.playing)
4837     {
4838       LevelStats_incSolved(level_nr);
4839
4840       SaveLevelSetup_SeriesInfo();
4841     }
4842
4843     if (tape.auto_play)         // tape might already be stopped here
4844       tape.auto_play_level_solved = TRUE;
4845
4846     TapeStop();
4847
4848     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4849     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4850     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4851
4852     time = time_final = game.time_final;
4853     score = score_final = game.score_final;
4854     health = health_final = game.health_final;
4855
4856     // update game panel values before (delayed) counting of score (if any)
4857     LevelSolved_DisplayFinalGameValues(time, score, health);
4858
4859     // if level has time score defined, calculate new final game values
4860     if (time_score > 0)
4861     {
4862       int time_final_max = 999;
4863       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4864       int time_frames = 0;
4865       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4866       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4867
4868       if (TimeLeft > 0)
4869       {
4870         time_final = 0;
4871         time_frames = time_frames_left;
4872       }
4873       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4874       {
4875         time_final = time_final_max;
4876         time_frames = time_frames_final_max - time_frames_played;
4877       }
4878
4879       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4880
4881       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4882
4883       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4884       {
4885         health_final = 0;
4886         score_final += health * time_score;
4887       }
4888
4889       game.score_final = score_final;
4890       game.health_final = health_final;
4891     }
4892
4893     // if not counting score after game, immediately update game panel values
4894     if (level_editor_test_game || !setup.count_score_after_game)
4895     {
4896       time = time_final;
4897       score = score_final;
4898
4899       LevelSolved_DisplayFinalGameValues(time, score, health);
4900     }
4901
4902     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4903     {
4904       // check if last player has left the level
4905       if (game.exit_x >= 0 &&
4906           game.exit_y >= 0)
4907       {
4908         int x = game.exit_x;
4909         int y = game.exit_y;
4910         int element = Tile[x][y];
4911
4912         // close exit door after last player
4913         if ((game.all_players_gone &&
4914              (element == EL_EXIT_OPEN ||
4915               element == EL_SP_EXIT_OPEN ||
4916               element == EL_STEEL_EXIT_OPEN)) ||
4917             element == EL_EM_EXIT_OPEN ||
4918             element == EL_EM_STEEL_EXIT_OPEN)
4919         {
4920
4921           Tile[x][y] =
4922             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4923              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4924              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4925              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4926              EL_EM_STEEL_EXIT_CLOSING);
4927
4928           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4929         }
4930
4931         // player disappears
4932         DrawLevelField(x, y);
4933       }
4934
4935       for (i = 0; i < MAX_PLAYERS; i++)
4936       {
4937         struct PlayerInfo *player = &stored_player[i];
4938
4939         if (player->present)
4940         {
4941           RemovePlayer(player);
4942
4943           // player disappears
4944           DrawLevelField(player->jx, player->jy);
4945         }
4946       }
4947     }
4948
4949     PlaySound(SND_GAME_WINNING);
4950   }
4951
4952   if (setup.count_score_after_game)
4953   {
4954     if (time != time_final)
4955     {
4956       if (game_over_delay_1 > 0)
4957       {
4958         game_over_delay_1--;
4959
4960         return;
4961       }
4962
4963       int time_to_go = ABS(time_final - time);
4964       int time_count_dir = (time < time_final ? +1 : -1);
4965
4966       if (time_to_go < time_count_steps)
4967         time_count_steps = 1;
4968
4969       time  += time_count_steps * time_count_dir;
4970       score += time_count_steps * time_score;
4971
4972       // set final score to correct rounding differences after counting score
4973       if (time == time_final)
4974         score = score_final;
4975
4976       LevelSolved_DisplayFinalGameValues(time, score, health);
4977
4978       if (time == time_final)
4979         StopSound(SND_GAME_LEVELTIME_BONUS);
4980       else if (setup.sound_loops)
4981         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4982       else
4983         PlaySound(SND_GAME_LEVELTIME_BONUS);
4984
4985       return;
4986     }
4987
4988     if (health != health_final)
4989     {
4990       if (game_over_delay_2 > 0)
4991       {
4992         game_over_delay_2--;
4993
4994         return;
4995       }
4996
4997       int health_count_dir = (health < health_final ? +1 : -1);
4998
4999       health += health_count_dir;
5000       score  += time_score;
5001
5002       LevelSolved_DisplayFinalGameValues(time, score, health);
5003
5004       if (health == health_final)
5005         StopSound(SND_GAME_LEVELTIME_BONUS);
5006       else if (setup.sound_loops)
5007         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5008       else
5009         PlaySound(SND_GAME_LEVELTIME_BONUS);
5010
5011       return;
5012     }
5013   }
5014
5015   game.panel.active = FALSE;
5016
5017   if (game_over_delay_3 > 0)
5018   {
5019     game_over_delay_3--;
5020
5021     return;
5022   }
5023
5024   GameEnd();
5025 }
5026
5027 void GameEnd(void)
5028 {
5029   // used instead of "level_nr" (needed for network games)
5030   int last_level_nr = levelset.level_nr;
5031   boolean tape_saved = FALSE;
5032
5033   game.LevelSolved_GameEnd = TRUE;
5034
5035   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5036   {
5037     // make sure that request dialog to save tape does not open door again
5038     if (!global.use_envelope_request)
5039       CloseDoor(DOOR_CLOSE_1);
5040
5041     // ask to save tape
5042     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5043
5044     // set unique basename for score tape (also saved in high score table)
5045     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5046   }
5047
5048   // if no tape is to be saved, close both doors simultaneously
5049   CloseDoor(DOOR_CLOSE_ALL);
5050
5051   if (level_editor_test_game || score_info_tape_play)
5052   {
5053     SetGameStatus(GAME_MODE_MAIN);
5054
5055     DrawMainMenu();
5056
5057     return;
5058   }
5059
5060   if (!game.LevelSolved_SaveScore)
5061   {
5062     SetGameStatus(GAME_MODE_MAIN);
5063
5064     DrawMainMenu();
5065
5066     return;
5067   }
5068
5069   if (level_nr == leveldir_current->handicap_level)
5070   {
5071     leveldir_current->handicap_level++;
5072
5073     SaveLevelSetup_SeriesInfo();
5074   }
5075
5076   // save score and score tape before potentially erasing tape below
5077   NewHighScore(last_level_nr, tape_saved);
5078
5079   if (setup.increment_levels &&
5080       level_nr < leveldir_current->last_level &&
5081       !network_playing)
5082   {
5083     level_nr++;         // advance to next level
5084     TapeErase();        // start with empty tape
5085
5086     if (setup.auto_play_next_level)
5087     {
5088       scores.continue_playing = TRUE;
5089       scores.next_level_nr = level_nr;
5090
5091       LoadLevel(level_nr);
5092
5093       SaveLevelSetup_SeriesInfo();
5094     }
5095   }
5096
5097   if (scores.last_added >= 0 && setup.show_scores_after_game)
5098   {
5099     SetGameStatus(GAME_MODE_SCORES);
5100
5101     DrawHallOfFame(last_level_nr);
5102   }
5103   else if (scores.continue_playing)
5104   {
5105     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5106   }
5107   else
5108   {
5109     SetGameStatus(GAME_MODE_MAIN);
5110
5111     DrawMainMenu();
5112   }
5113 }
5114
5115 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5116                          boolean one_score_entry_per_name)
5117 {
5118   int i;
5119
5120   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5121     return -1;
5122
5123   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5124   {
5125     struct ScoreEntry *entry = &list->entry[i];
5126     boolean score_is_better = (new_entry->score >  entry->score);
5127     boolean score_is_equal  = (new_entry->score == entry->score);
5128     boolean time_is_better  = (new_entry->time  <  entry->time);
5129     boolean time_is_equal   = (new_entry->time  == entry->time);
5130     boolean better_by_score = (score_is_better ||
5131                                (score_is_equal && time_is_better));
5132     boolean better_by_time  = (time_is_better ||
5133                                (time_is_equal && score_is_better));
5134     boolean is_better = (level.rate_time_over_score ? better_by_time :
5135                          better_by_score);
5136     boolean entry_is_empty = (entry->score == 0 &&
5137                               entry->time == 0);
5138
5139     // prevent adding server score entries if also existing in local score file
5140     // (special case: historic score entries have an empty tape basename entry)
5141     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5142         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5143     {
5144       // add fields from server score entry not stored in local score entry
5145       // (currently, this means setting platform, version and country fields;
5146       // in rare cases, this may also correct an invalid score value, as
5147       // historic scores might have been truncated to 16-bit values locally)
5148       *entry = *new_entry;
5149
5150       return -1;
5151     }
5152
5153     if (is_better || entry_is_empty)
5154     {
5155       // player has made it to the hall of fame
5156
5157       if (i < MAX_SCORE_ENTRIES - 1)
5158       {
5159         int m = MAX_SCORE_ENTRIES - 1;
5160         int l;
5161
5162         if (one_score_entry_per_name)
5163         {
5164           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5165             if (strEqual(list->entry[l].name, new_entry->name))
5166               m = l;
5167
5168           if (m == i)   // player's new highscore overwrites his old one
5169             goto put_into_list;
5170         }
5171
5172         for (l = m; l > i; l--)
5173           list->entry[l] = list->entry[l - 1];
5174       }
5175
5176       put_into_list:
5177
5178       *entry = *new_entry;
5179
5180       return i;
5181     }
5182     else if (one_score_entry_per_name &&
5183              strEqual(entry->name, new_entry->name))
5184     {
5185       // player already in high score list with better score or time
5186
5187       return -1;
5188     }
5189   }
5190
5191   // special case: new score is beyond the last high score list position
5192   return MAX_SCORE_ENTRIES;
5193 }
5194
5195 void NewHighScore(int level_nr, boolean tape_saved)
5196 {
5197   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5198   boolean one_per_name = FALSE;
5199
5200   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5201   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5202
5203   new_entry.score = game.score_final;
5204   new_entry.time = game.score_time_final;
5205
5206   LoadScore(level_nr);
5207
5208   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5209
5210   if (scores.last_added >= MAX_SCORE_ENTRIES)
5211   {
5212     scores.last_added = MAX_SCORE_ENTRIES - 1;
5213     scores.force_last_added = TRUE;
5214
5215     scores.entry[scores.last_added] = new_entry;
5216
5217     // store last added local score entry (before merging server scores)
5218     scores.last_added_local = scores.last_added;
5219
5220     return;
5221   }
5222
5223   if (scores.last_added < 0)
5224     return;
5225
5226   SaveScore(level_nr);
5227
5228   // store last added local score entry (before merging server scores)
5229   scores.last_added_local = scores.last_added;
5230
5231   if (!game.LevelSolved_SaveTape)
5232     return;
5233
5234   SaveScoreTape(level_nr);
5235
5236   if (setup.ask_for_using_api_server)
5237   {
5238     setup.use_api_server =
5239       Request("Upload your score and tape to the high score server?", REQ_ASK);
5240
5241     if (!setup.use_api_server)
5242       Request("Not using high score server! Use setup menu to enable again!",
5243               REQ_CONFIRM);
5244
5245     runtime.use_api_server = setup.use_api_server;
5246
5247     // after asking for using API server once, do not ask again
5248     setup.ask_for_using_api_server = FALSE;
5249
5250     SaveSetup_ServerSetup();
5251   }
5252
5253   SaveServerScore(level_nr, tape_saved);
5254 }
5255
5256 void MergeServerScore(void)
5257 {
5258   struct ScoreEntry last_added_entry;
5259   boolean one_per_name = FALSE;
5260   int i;
5261
5262   if (scores.last_added >= 0)
5263     last_added_entry = scores.entry[scores.last_added];
5264
5265   for (i = 0; i < server_scores.num_entries; i++)
5266   {
5267     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5268
5269     if (pos >= 0 && pos <= scores.last_added)
5270       scores.last_added++;
5271   }
5272
5273   if (scores.last_added >= MAX_SCORE_ENTRIES)
5274   {
5275     scores.last_added = MAX_SCORE_ENTRIES - 1;
5276     scores.force_last_added = TRUE;
5277
5278     scores.entry[scores.last_added] = last_added_entry;
5279   }
5280 }
5281
5282 static int getElementMoveStepsizeExt(int x, int y, int direction)
5283 {
5284   int element = Tile[x][y];
5285   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5286   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5287   int horiz_move = (dx != 0);
5288   int sign = (horiz_move ? dx : dy);
5289   int step = sign * element_info[element].move_stepsize;
5290
5291   // special values for move stepsize for spring and things on conveyor belt
5292   if (horiz_move)
5293   {
5294     if (CAN_FALL(element) &&
5295         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5296       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5297     else if (element == EL_SPRING)
5298       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5299   }
5300
5301   return step;
5302 }
5303
5304 static int getElementMoveStepsize(int x, int y)
5305 {
5306   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5307 }
5308
5309 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5310 {
5311   if (player->GfxAction != action || player->GfxDir != dir)
5312   {
5313     player->GfxAction = action;
5314     player->GfxDir = dir;
5315     player->Frame = 0;
5316     player->StepFrame = 0;
5317   }
5318 }
5319
5320 static void ResetGfxFrame(int x, int y)
5321 {
5322   // profiling showed that "autotest" spends 10~20% of its time in this function
5323   if (DrawingDeactivatedField())
5324     return;
5325
5326   int element = Tile[x][y];
5327   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5328
5329   if (graphic_info[graphic].anim_global_sync)
5330     GfxFrame[x][y] = FrameCounter;
5331   else if (graphic_info[graphic].anim_global_anim_sync)
5332     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5333   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5334     GfxFrame[x][y] = CustomValue[x][y];
5335   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5336     GfxFrame[x][y] = element_info[element].collect_score;
5337   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5338     GfxFrame[x][y] = ChangeDelay[x][y];
5339 }
5340
5341 static void ResetGfxAnimation(int x, int y)
5342 {
5343   GfxAction[x][y] = ACTION_DEFAULT;
5344   GfxDir[x][y] = MovDir[x][y];
5345   GfxFrame[x][y] = 0;
5346
5347   ResetGfxFrame(x, y);
5348 }
5349
5350 static void ResetRandomAnimationValue(int x, int y)
5351 {
5352   GfxRandom[x][y] = INIT_GFX_RANDOM();
5353 }
5354
5355 static void InitMovingField(int x, int y, int direction)
5356 {
5357   int element = Tile[x][y];
5358   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5359   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5360   int newx = x + dx;
5361   int newy = y + dy;
5362   boolean is_moving_before, is_moving_after;
5363
5364   // check if element was/is moving or being moved before/after mode change
5365   is_moving_before = (WasJustMoving[x][y] != 0);
5366   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5367
5368   // reset animation only for moving elements which change direction of moving
5369   // or which just started or stopped moving
5370   // (else CEs with property "can move" / "not moving" are reset each frame)
5371   if (is_moving_before != is_moving_after ||
5372       direction != MovDir[x][y])
5373     ResetGfxAnimation(x, y);
5374
5375   MovDir[x][y] = direction;
5376   GfxDir[x][y] = direction;
5377
5378   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5379                      direction == MV_DOWN && CAN_FALL(element) ?
5380                      ACTION_FALLING : ACTION_MOVING);
5381
5382   // this is needed for CEs with property "can move" / "not moving"
5383
5384   if (is_moving_after)
5385   {
5386     if (Tile[newx][newy] == EL_EMPTY)
5387       Tile[newx][newy] = EL_BLOCKED;
5388
5389     MovDir[newx][newy] = MovDir[x][y];
5390
5391     CustomValue[newx][newy] = CustomValue[x][y];
5392
5393     GfxFrame[newx][newy] = GfxFrame[x][y];
5394     GfxRandom[newx][newy] = GfxRandom[x][y];
5395     GfxAction[newx][newy] = GfxAction[x][y];
5396     GfxDir[newx][newy] = GfxDir[x][y];
5397   }
5398 }
5399
5400 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5401 {
5402   int direction = MovDir[x][y];
5403   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5404   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5405
5406   *goes_to_x = newx;
5407   *goes_to_y = newy;
5408 }
5409
5410 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5411 {
5412   int direction = MovDir[x][y];
5413   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5414   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5415
5416   *comes_from_x = oldx;
5417   *comes_from_y = oldy;
5418 }
5419
5420 static int MovingOrBlocked2Element(int x, int y)
5421 {
5422   int element = Tile[x][y];
5423
5424   if (element == EL_BLOCKED)
5425   {
5426     int oldx, oldy;
5427
5428     Blocked2Moving(x, y, &oldx, &oldy);
5429
5430     return Tile[oldx][oldy];
5431   }
5432
5433   return element;
5434 }
5435
5436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5437 {
5438   // like MovingOrBlocked2Element(), but if element is moving
5439   // and (x, y) is the field the moving element is just leaving,
5440   // return EL_BLOCKED instead of the element value
5441   int element = Tile[x][y];
5442
5443   if (IS_MOVING(x, y))
5444   {
5445     if (element == EL_BLOCKED)
5446     {
5447       int oldx, oldy;
5448
5449       Blocked2Moving(x, y, &oldx, &oldy);
5450       return Tile[oldx][oldy];
5451     }
5452     else
5453       return EL_BLOCKED;
5454   }
5455   else
5456     return element;
5457 }
5458
5459 static void RemoveField(int x, int y)
5460 {
5461   Tile[x][y] = EL_EMPTY;
5462
5463   MovPos[x][y] = 0;
5464   MovDir[x][y] = 0;
5465   MovDelay[x][y] = 0;
5466
5467   CustomValue[x][y] = 0;
5468
5469   AmoebaNr[x][y] = 0;
5470   ChangeDelay[x][y] = 0;
5471   ChangePage[x][y] = -1;
5472   Pushed[x][y] = FALSE;
5473
5474   GfxElement[x][y] = EL_UNDEFINED;
5475   GfxAction[x][y] = ACTION_DEFAULT;
5476   GfxDir[x][y] = MV_NONE;
5477 }
5478
5479 static void RemoveMovingField(int x, int y)
5480 {
5481   int oldx = x, oldy = y, newx = x, newy = y;
5482   int element = Tile[x][y];
5483   int next_element = EL_UNDEFINED;
5484
5485   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5486     return;
5487
5488   if (IS_MOVING(x, y))
5489   {
5490     Moving2Blocked(x, y, &newx, &newy);
5491
5492     if (Tile[newx][newy] != EL_BLOCKED)
5493     {
5494       // element is moving, but target field is not free (blocked), but
5495       // already occupied by something different (example: acid pool);
5496       // in this case, only remove the moving field, but not the target
5497
5498       RemoveField(oldx, oldy);
5499
5500       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5501
5502       TEST_DrawLevelField(oldx, oldy);
5503
5504       return;
5505     }
5506   }
5507   else if (element == EL_BLOCKED)
5508   {
5509     Blocked2Moving(x, y, &oldx, &oldy);
5510     if (!IS_MOVING(oldx, oldy))
5511       return;
5512   }
5513
5514   if (element == EL_BLOCKED &&
5515       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5516        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5517        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5518        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5519        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5520        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5521     next_element = get_next_element(Tile[oldx][oldy]);
5522
5523   RemoveField(oldx, oldy);
5524   RemoveField(newx, newy);
5525
5526   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5527
5528   if (next_element != EL_UNDEFINED)
5529     Tile[oldx][oldy] = next_element;
5530
5531   TEST_DrawLevelField(oldx, oldy);
5532   TEST_DrawLevelField(newx, newy);
5533 }
5534
5535 void DrawDynamite(int x, int y)
5536 {
5537   int sx = SCREENX(x), sy = SCREENY(y);
5538   int graphic = el2img(Tile[x][y]);
5539   int frame;
5540
5541   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5542     return;
5543
5544   if (IS_WALKABLE_INSIDE(Back[x][y]))
5545     return;
5546
5547   if (Back[x][y])
5548     DrawLevelElement(x, y, Back[x][y]);
5549   else if (Store[x][y])
5550     DrawLevelElement(x, y, Store[x][y]);
5551   else if (game.use_masked_elements)
5552     DrawLevelElement(x, y, EL_EMPTY);
5553
5554   frame = getGraphicAnimationFrameXY(graphic, x, y);
5555
5556   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5557     DrawGraphicThruMask(sx, sy, graphic, frame);
5558   else
5559     DrawGraphic(sx, sy, graphic, frame);
5560 }
5561
5562 static void CheckDynamite(int x, int y)
5563 {
5564   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5565   {
5566     MovDelay[x][y]--;
5567
5568     if (MovDelay[x][y] != 0)
5569     {
5570       DrawDynamite(x, y);
5571       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5572
5573       return;
5574     }
5575   }
5576
5577   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5578
5579   Bang(x, y);
5580 }
5581
5582 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5583 {
5584   boolean num_checked_players = 0;
5585   int i;
5586
5587   for (i = 0; i < MAX_PLAYERS; i++)
5588   {
5589     if (stored_player[i].active)
5590     {
5591       int sx = stored_player[i].jx;
5592       int sy = stored_player[i].jy;
5593
5594       if (num_checked_players == 0)
5595       {
5596         *sx1 = *sx2 = sx;
5597         *sy1 = *sy2 = sy;
5598       }
5599       else
5600       {
5601         *sx1 = MIN(*sx1, sx);
5602         *sy1 = MIN(*sy1, sy);
5603         *sx2 = MAX(*sx2, sx);
5604         *sy2 = MAX(*sy2, sy);
5605       }
5606
5607       num_checked_players++;
5608     }
5609   }
5610 }
5611
5612 static boolean checkIfAllPlayersFitToScreen_RND(void)
5613 {
5614   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5615
5616   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5617
5618   return (sx2 - sx1 < SCR_FIELDX &&
5619           sy2 - sy1 < SCR_FIELDY);
5620 }
5621
5622 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5623 {
5624   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5625
5626   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5627
5628   *sx = (sx1 + sx2) / 2;
5629   *sy = (sy1 + sy2) / 2;
5630 }
5631
5632 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5633                                boolean center_screen, boolean quick_relocation)
5634 {
5635   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5636   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5637   boolean no_delay = (tape.warp_forward);
5638   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5639   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5640   int new_scroll_x, new_scroll_y;
5641
5642   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5643   {
5644     // case 1: quick relocation inside visible screen (without scrolling)
5645
5646     RedrawPlayfield();
5647
5648     return;
5649   }
5650
5651   if (!level.shifted_relocation || center_screen)
5652   {
5653     // relocation _with_ centering of screen
5654
5655     new_scroll_x = SCROLL_POSITION_X(x);
5656     new_scroll_y = SCROLL_POSITION_Y(y);
5657   }
5658   else
5659   {
5660     // relocation _without_ centering of screen
5661
5662     int center_scroll_x = SCROLL_POSITION_X(old_x);
5663     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5664     int offset_x = x + (scroll_x - center_scroll_x);
5665     int offset_y = y + (scroll_y - center_scroll_y);
5666
5667     // for new screen position, apply previous offset to center position
5668     new_scroll_x = SCROLL_POSITION_X(offset_x);
5669     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5670   }
5671
5672   if (quick_relocation)
5673   {
5674     // case 2: quick relocation (redraw without visible scrolling)
5675
5676     scroll_x = new_scroll_x;
5677     scroll_y = new_scroll_y;
5678
5679     RedrawPlayfield();
5680
5681     return;
5682   }
5683
5684   // case 3: visible relocation (with scrolling to new position)
5685
5686   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5687
5688   SetVideoFrameDelay(wait_delay_value);
5689
5690   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5691   {
5692     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5693     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5694
5695     if (dx == 0 && dy == 0)             // no scrolling needed at all
5696       break;
5697
5698     scroll_x -= dx;
5699     scroll_y -= dy;
5700
5701     // set values for horizontal/vertical screen scrolling (half tile size)
5702     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5703     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5704     int pos_x = dx * TILEX / 2;
5705     int pos_y = dy * TILEY / 2;
5706     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5707     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5708
5709     ScrollLevel(dx, dy);
5710     DrawAllPlayers();
5711
5712     // scroll in two steps of half tile size to make things smoother
5713     BlitScreenToBitmapExt_RND(window, fx, fy);
5714
5715     // scroll second step to align at full tile size
5716     BlitScreenToBitmap(window);
5717   }
5718
5719   DrawAllPlayers();
5720   BackToFront();
5721
5722   SetVideoFrameDelay(frame_delay_value_old);
5723 }
5724
5725 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5726 {
5727   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5728   int player_nr = GET_PLAYER_NR(el_player);
5729   struct PlayerInfo *player = &stored_player[player_nr];
5730   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5731   boolean no_delay = (tape.warp_forward);
5732   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5733   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5734   int old_jx = player->jx;
5735   int old_jy = player->jy;
5736   int old_element = Tile[old_jx][old_jy];
5737   int element = Tile[jx][jy];
5738   boolean player_relocated = (old_jx != jx || old_jy != jy);
5739
5740   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5741   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5742   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5743   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5744   int leave_side_horiz = move_dir_horiz;
5745   int leave_side_vert  = move_dir_vert;
5746   int enter_side = enter_side_horiz | enter_side_vert;
5747   int leave_side = leave_side_horiz | leave_side_vert;
5748
5749   if (player->buried)           // do not reanimate dead player
5750     return;
5751
5752   if (!player_relocated)        // no need to relocate the player
5753     return;
5754
5755   if (IS_PLAYER(jx, jy))        // player already placed at new position
5756   {
5757     RemoveField(jx, jy);        // temporarily remove newly placed player
5758     DrawLevelField(jx, jy);
5759   }
5760
5761   if (player->present)
5762   {
5763     while (player->MovPos)
5764     {
5765       ScrollPlayer(player, SCROLL_GO_ON);
5766       ScrollScreen(NULL, SCROLL_GO_ON);
5767
5768       AdvanceFrameAndPlayerCounters(player->index_nr);
5769
5770       DrawPlayer(player);
5771
5772       BackToFront_WithFrameDelay(wait_delay_value);
5773     }
5774
5775     DrawPlayer(player);         // needed here only to cleanup last field
5776     DrawLevelField(player->jx, player->jy);     // remove player graphic
5777
5778     player->is_moving = FALSE;
5779   }
5780
5781   if (IS_CUSTOM_ELEMENT(old_element))
5782     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5783                                CE_LEFT_BY_PLAYER,
5784                                player->index_bit, leave_side);
5785
5786   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5787                                       CE_PLAYER_LEAVES_X,
5788                                       player->index_bit, leave_side);
5789
5790   Tile[jx][jy] = el_player;
5791   InitPlayerField(jx, jy, el_player, TRUE);
5792
5793   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5794      possible that the relocation target field did not contain a player element,
5795      but a walkable element, to which the new player was relocated -- in this
5796      case, restore that (already initialized!) element on the player field */
5797   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5798   {
5799     Tile[jx][jy] = element;     // restore previously existing element
5800   }
5801
5802   // only visually relocate centered player
5803   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5804                      FALSE, level.instant_relocation);
5805
5806   TestIfPlayerTouchesBadThing(jx, jy);
5807   TestIfPlayerTouchesCustomElement(jx, jy);
5808
5809   if (IS_CUSTOM_ELEMENT(element))
5810     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5811                                player->index_bit, enter_side);
5812
5813   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5814                                       player->index_bit, enter_side);
5815
5816   if (player->is_switching)
5817   {
5818     /* ensure that relocation while still switching an element does not cause
5819        a new element to be treated as also switched directly after relocation
5820        (this is important for teleporter switches that teleport the player to
5821        a place where another teleporter switch is in the same direction, which
5822        would then incorrectly be treated as immediately switched before the
5823        direction key that caused the switch was released) */
5824
5825     player->switch_x += jx - old_jx;
5826     player->switch_y += jy - old_jy;
5827   }
5828 }
5829
5830 static void Explode(int ex, int ey, int phase, int mode)
5831 {
5832   int x, y;
5833   int last_phase;
5834   int border_element;
5835
5836   if (game.explosions_delayed)
5837   {
5838     ExplodeField[ex][ey] = mode;
5839     return;
5840   }
5841
5842   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5843   {
5844     int center_element = Tile[ex][ey];
5845     int ce_value = CustomValue[ex][ey];
5846     int ce_score = element_info[center_element].collect_score;
5847     int artwork_element, explosion_element;     // set these values later
5848
5849     // remove things displayed in background while burning dynamite
5850     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5851       Back[ex][ey] = 0;
5852
5853     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5854     {
5855       // put moving element to center field (and let it explode there)
5856       center_element = MovingOrBlocked2Element(ex, ey);
5857       RemoveMovingField(ex, ey);
5858       Tile[ex][ey] = center_element;
5859     }
5860
5861     // now "center_element" is finally determined -- set related values now
5862     artwork_element = center_element;           // for custom player artwork
5863     explosion_element = center_element;         // for custom player artwork
5864
5865     if (IS_PLAYER(ex, ey))
5866     {
5867       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5868
5869       artwork_element = stored_player[player_nr].artwork_element;
5870
5871       if (level.use_explosion_element[player_nr])
5872       {
5873         explosion_element = level.explosion_element[player_nr];
5874         artwork_element = explosion_element;
5875       }
5876     }
5877
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882
5883     last_phase = element_info[explosion_element].explosion_delay + 1;
5884
5885     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5886     {
5887       int xx = x - ex + 1;
5888       int yy = y - ey + 1;
5889       int element;
5890
5891       if (!IN_LEV_FIELD(x, y) ||
5892           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5893           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5894         continue;
5895
5896       element = Tile[x][y];
5897
5898       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5899       {
5900         element = MovingOrBlocked2Element(x, y);
5901
5902         if (!IS_EXPLOSION_PROOF(element))
5903           RemoveMovingField(x, y);
5904       }
5905
5906       // indestructible elements can only explode in center (but not flames)
5907       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5908                                            mode == EX_TYPE_BORDER)) ||
5909           element == EL_FLAMES)
5910         continue;
5911
5912       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5913          behaviour, for example when touching a yamyam that explodes to rocks
5914          with active deadly shield, a rock is created under the player !!! */
5915       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5916 #if 0
5917       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5918           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5919            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5920 #else
5921       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5922 #endif
5923       {
5924         if (IS_ACTIVE_BOMB(element))
5925         {
5926           // re-activate things under the bomb like gate or penguin
5927           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5928           Back[x][y] = 0;
5929         }
5930
5931         continue;
5932       }
5933
5934       // save walkable background elements while explosion on same tile
5935       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5936           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5937         Back[x][y] = element;
5938
5939       // ignite explodable elements reached by other explosion
5940       if (element == EL_EXPLOSION)
5941         element = Store2[x][y];
5942
5943       if (AmoebaNr[x][y] &&
5944           (element == EL_AMOEBA_FULL ||
5945            element == EL_BD_AMOEBA ||
5946            element == EL_AMOEBA_GROWING))
5947       {
5948         AmoebaCnt[AmoebaNr[x][y]]--;
5949         AmoebaCnt2[AmoebaNr[x][y]]--;
5950       }
5951
5952       RemoveField(x, y);
5953
5954       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5955       {
5956         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5957
5958         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5959
5960         if (PLAYERINFO(ex, ey)->use_murphy)
5961           Store[x][y] = EL_EMPTY;
5962       }
5963
5964       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5965       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5966       else if (IS_PLAYER_ELEMENT(center_element))
5967         Store[x][y] = EL_EMPTY;
5968       else if (center_element == EL_YAMYAM)
5969         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5970       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5971         Store[x][y] = element_info[center_element].content.e[xx][yy];
5972 #if 1
5973       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5974       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5975       // otherwise) -- FIX THIS !!!
5976       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5977         Store[x][y] = element_info[element].content.e[1][1];
5978 #else
5979       else if (!CAN_EXPLODE(element))
5980         Store[x][y] = element_info[element].content.e[1][1];
5981 #endif
5982       else
5983         Store[x][y] = EL_EMPTY;
5984
5985       if (IS_CUSTOM_ELEMENT(center_element))
5986         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
5987                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
5988                        Store[x][y] >= EL_PREV_CE_8 &&
5989                        Store[x][y] <= EL_NEXT_CE_8 ?
5990                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
5991                        Store[x][y]);
5992
5993       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5994           center_element == EL_AMOEBA_TO_DIAMOND)
5995         Store2[x][y] = element;
5996
5997       Tile[x][y] = EL_EXPLOSION;
5998       GfxElement[x][y] = artwork_element;
5999
6000       ExplodePhase[x][y] = 1;
6001       ExplodeDelay[x][y] = last_phase;
6002
6003       Stop[x][y] = TRUE;
6004     }
6005
6006     if (center_element == EL_YAMYAM)
6007       game.yamyam_content_nr =
6008         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6009
6010     return;
6011   }
6012
6013   if (Stop[ex][ey])
6014     return;
6015
6016   x = ex;
6017   y = ey;
6018
6019   if (phase == 1)
6020     GfxFrame[x][y] = 0;         // restart explosion animation
6021
6022   last_phase = ExplodeDelay[x][y];
6023
6024   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6025
6026   // this can happen if the player leaves an explosion just in time
6027   if (GfxElement[x][y] == EL_UNDEFINED)
6028     GfxElement[x][y] = EL_EMPTY;
6029
6030   border_element = Store2[x][y];
6031   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6032     border_element = StorePlayer[x][y];
6033
6034   if (phase == element_info[border_element].ignition_delay ||
6035       phase == last_phase)
6036   {
6037     boolean border_explosion = FALSE;
6038
6039     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6040         !PLAYER_EXPLOSION_PROTECTED(x, y))
6041     {
6042       KillPlayerUnlessExplosionProtected(x, y);
6043       border_explosion = TRUE;
6044     }
6045     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6046     {
6047       Tile[x][y] = Store2[x][y];
6048       Store2[x][y] = 0;
6049       Bang(x, y);
6050       border_explosion = TRUE;
6051     }
6052     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6053     {
6054       AmoebaToDiamond(x, y);
6055       Store2[x][y] = 0;
6056       border_explosion = TRUE;
6057     }
6058
6059     // if an element just explodes due to another explosion (chain-reaction),
6060     // do not immediately end the new explosion when it was the last frame of
6061     // the explosion (as it would be done in the following "if"-statement!)
6062     if (border_explosion && phase == last_phase)
6063       return;
6064   }
6065
6066   // this can happen if the player was just killed by an explosion
6067   if (GfxElement[x][y] == EL_UNDEFINED)
6068     GfxElement[x][y] = EL_EMPTY;
6069
6070   if (phase == last_phase)
6071   {
6072     int element;
6073
6074     element = Tile[x][y] = Store[x][y];
6075     Store[x][y] = Store2[x][y] = 0;
6076     GfxElement[x][y] = EL_UNDEFINED;
6077
6078     // player can escape from explosions and might therefore be still alive
6079     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6080         element <= EL_PLAYER_IS_EXPLODING_4)
6081     {
6082       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6083       int explosion_element = EL_PLAYER_1 + player_nr;
6084       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6085       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6086
6087       if (level.use_explosion_element[player_nr])
6088         explosion_element = level.explosion_element[player_nr];
6089
6090       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6091                     element_info[explosion_element].content.e[xx][yy]);
6092     }
6093
6094     // restore probably existing indestructible background element
6095     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6096       element = Tile[x][y] = Back[x][y];
6097     Back[x][y] = 0;
6098
6099     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6100     GfxDir[x][y] = MV_NONE;
6101     ChangeDelay[x][y] = 0;
6102     ChangePage[x][y] = -1;
6103
6104     CustomValue[x][y] = 0;
6105
6106     InitField_WithBug2(x, y, FALSE);
6107
6108     TEST_DrawLevelField(x, y);
6109
6110     TestIfElementTouchesCustomElement(x, y);
6111
6112     if (GFX_CRUMBLED(element))
6113       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6114
6115     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6116       StorePlayer[x][y] = 0;
6117
6118     if (IS_PLAYER_ELEMENT(element))
6119       RelocatePlayer(x, y, element);
6120   }
6121   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6122   {
6123     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6124     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6125
6126     if (phase == 1)
6127       TEST_DrawLevelFieldCrumbled(x, y);
6128
6129     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6130     {
6131       DrawLevelElement(x, y, Back[x][y]);
6132       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6133     }
6134     else if (IS_WALKABLE_UNDER(Back[x][y]))
6135     {
6136       DrawLevelGraphic(x, y, graphic, frame);
6137       DrawLevelElementThruMask(x, y, Back[x][y]);
6138     }
6139     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6140       DrawLevelGraphic(x, y, graphic, frame);
6141   }
6142 }
6143
6144 static void DynaExplode(int ex, int ey)
6145 {
6146   int i, j;
6147   int dynabomb_element = Tile[ex][ey];
6148   int dynabomb_size = 1;
6149   boolean dynabomb_xl = FALSE;
6150   struct PlayerInfo *player;
6151   struct XY *xy = xy_topdown;
6152
6153   if (IS_ACTIVE_BOMB(dynabomb_element))
6154   {
6155     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6156     dynabomb_size = player->dynabomb_size;
6157     dynabomb_xl = player->dynabomb_xl;
6158     player->dynabombs_left++;
6159   }
6160
6161   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6162
6163   for (i = 0; i < NUM_DIRECTIONS; i++)
6164   {
6165     for (j = 1; j <= dynabomb_size; j++)
6166     {
6167       int x = ex + j * xy[i].x;
6168       int y = ey + j * xy[i].y;
6169       int element;
6170
6171       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6172         break;
6173
6174       element = Tile[x][y];
6175
6176       // do not restart explosions of fields with active bombs
6177       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6178         continue;
6179
6180       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6181
6182       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6183           !IS_DIGGABLE(element) && !dynabomb_xl)
6184         break;
6185     }
6186   }
6187 }
6188
6189 void Bang(int x, int y)
6190 {
6191   int element = MovingOrBlocked2Element(x, y);
6192   int explosion_type = EX_TYPE_NORMAL;
6193
6194   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6195   {
6196     struct PlayerInfo *player = PLAYERINFO(x, y);
6197
6198     element = Tile[x][y] = player->initial_element;
6199
6200     if (level.use_explosion_element[player->index_nr])
6201     {
6202       int explosion_element = level.explosion_element[player->index_nr];
6203
6204       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6205         explosion_type = EX_TYPE_CROSS;
6206       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6207         explosion_type = EX_TYPE_CENTER;
6208     }
6209   }
6210
6211   switch (element)
6212   {
6213     case EL_BUG:
6214     case EL_SPACESHIP:
6215     case EL_BD_BUTTERFLY:
6216     case EL_BD_FIREFLY:
6217     case EL_YAMYAM:
6218     case EL_DARK_YAMYAM:
6219     case EL_ROBOT:
6220     case EL_PACMAN:
6221     case EL_MOLE:
6222       RaiseScoreElement(element);
6223       break;
6224
6225     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6226     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6227     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6228     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6229     case EL_DYNABOMB_INCREASE_NUMBER:
6230     case EL_DYNABOMB_INCREASE_SIZE:
6231     case EL_DYNABOMB_INCREASE_POWER:
6232       explosion_type = EX_TYPE_DYNA;
6233       break;
6234
6235     case EL_DC_LANDMINE:
6236       explosion_type = EX_TYPE_CENTER;
6237       break;
6238
6239     case EL_PENGUIN:
6240     case EL_LAMP:
6241     case EL_LAMP_ACTIVE:
6242     case EL_AMOEBA_TO_DIAMOND:
6243       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6244         explosion_type = EX_TYPE_CENTER;
6245       break;
6246
6247     default:
6248       if (element_info[element].explosion_type == EXPLODES_CROSS)
6249         explosion_type = EX_TYPE_CROSS;
6250       else if (element_info[element].explosion_type == EXPLODES_1X1)
6251         explosion_type = EX_TYPE_CENTER;
6252       break;
6253   }
6254
6255   if (explosion_type == EX_TYPE_DYNA)
6256     DynaExplode(x, y);
6257   else
6258     Explode(x, y, EX_PHASE_START, explosion_type);
6259
6260   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6261 }
6262
6263 static void SplashAcid(int x, int y)
6264 {
6265   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6266       (!IN_LEV_FIELD(x - 1, y - 2) ||
6267        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6268     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6269
6270   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6271       (!IN_LEV_FIELD(x + 1, y - 2) ||
6272        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6273     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6274
6275   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6276 }
6277
6278 static void InitBeltMovement(void)
6279 {
6280   static int belt_base_element[4] =
6281   {
6282     EL_CONVEYOR_BELT_1_LEFT,
6283     EL_CONVEYOR_BELT_2_LEFT,
6284     EL_CONVEYOR_BELT_3_LEFT,
6285     EL_CONVEYOR_BELT_4_LEFT
6286   };
6287   static int belt_base_active_element[4] =
6288   {
6289     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6290     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6291     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6292     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6293   };
6294
6295   int x, y, i, j;
6296
6297   // set frame order for belt animation graphic according to belt direction
6298   for (i = 0; i < NUM_BELTS; i++)
6299   {
6300     int belt_nr = i;
6301
6302     for (j = 0; j < NUM_BELT_PARTS; j++)
6303     {
6304       int element = belt_base_active_element[belt_nr] + j;
6305       int graphic_1 = el2img(element);
6306       int graphic_2 = el2panelimg(element);
6307
6308       if (game.belt_dir[i] == MV_LEFT)
6309       {
6310         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6311         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6312       }
6313       else
6314       {
6315         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6316         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6317       }
6318     }
6319   }
6320
6321   SCAN_PLAYFIELD(x, y)
6322   {
6323     int element = Tile[x][y];
6324
6325     for (i = 0; i < NUM_BELTS; i++)
6326     {
6327       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6328       {
6329         int e_belt_nr = getBeltNrFromBeltElement(element);
6330         int belt_nr = i;
6331
6332         if (e_belt_nr == belt_nr)
6333         {
6334           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6335
6336           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6337         }
6338       }
6339     }
6340   }
6341 }
6342
6343 static void ToggleBeltSwitch(int x, int y)
6344 {
6345   static int belt_base_element[4] =
6346   {
6347     EL_CONVEYOR_BELT_1_LEFT,
6348     EL_CONVEYOR_BELT_2_LEFT,
6349     EL_CONVEYOR_BELT_3_LEFT,
6350     EL_CONVEYOR_BELT_4_LEFT
6351   };
6352   static int belt_base_active_element[4] =
6353   {
6354     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6355     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6356     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6357     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6358   };
6359   static int belt_base_switch_element[4] =
6360   {
6361     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6362     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6363     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6364     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6365   };
6366   static int belt_move_dir[4] =
6367   {
6368     MV_LEFT,
6369     MV_NONE,
6370     MV_RIGHT,
6371     MV_NONE,
6372   };
6373
6374   int element = Tile[x][y];
6375   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6376   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6377   int belt_dir = belt_move_dir[belt_dir_nr];
6378   int xx, yy, i;
6379
6380   if (!IS_BELT_SWITCH(element))
6381     return;
6382
6383   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6384   game.belt_dir[belt_nr] = belt_dir;
6385
6386   if (belt_dir_nr == 3)
6387     belt_dir_nr = 1;
6388
6389   // set frame order for belt animation graphic according to belt direction
6390   for (i = 0; i < NUM_BELT_PARTS; i++)
6391   {
6392     int element = belt_base_active_element[belt_nr] + i;
6393     int graphic_1 = el2img(element);
6394     int graphic_2 = el2panelimg(element);
6395
6396     if (belt_dir == MV_LEFT)
6397     {
6398       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6399       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6400     }
6401     else
6402     {
6403       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6404       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6405     }
6406   }
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (IS_BELT_SWITCH(element))
6413     {
6414       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6415
6416       if (e_belt_nr == belt_nr)
6417       {
6418         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6419         TEST_DrawLevelField(xx, yy);
6420       }
6421     }
6422     else if (IS_BELT(element) && belt_dir != MV_NONE)
6423     {
6424       int e_belt_nr = getBeltNrFromBeltElement(element);
6425
6426       if (e_belt_nr == belt_nr)
6427       {
6428         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6429
6430         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6431         TEST_DrawLevelField(xx, yy);
6432       }
6433     }
6434     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6435     {
6436       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6437
6438       if (e_belt_nr == belt_nr)
6439       {
6440         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6441
6442         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6443         TEST_DrawLevelField(xx, yy);
6444       }
6445     }
6446   }
6447 }
6448
6449 static void ToggleSwitchgateSwitch(void)
6450 {
6451   int xx, yy;
6452
6453   game.switchgate_pos = !game.switchgate_pos;
6454
6455   SCAN_PLAYFIELD(xx, yy)
6456   {
6457     int element = Tile[xx][yy];
6458
6459     if (element == EL_SWITCHGATE_SWITCH_UP)
6460     {
6461       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6462       TEST_DrawLevelField(xx, yy);
6463     }
6464     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6465     {
6466       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6467       TEST_DrawLevelField(xx, yy);
6468     }
6469     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6470     {
6471       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6472       TEST_DrawLevelField(xx, yy);
6473     }
6474     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6475     {
6476       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6477       TEST_DrawLevelField(xx, yy);
6478     }
6479     else if (element == EL_SWITCHGATE_OPEN ||
6480              element == EL_SWITCHGATE_OPENING)
6481     {
6482       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6483
6484       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6485     }
6486     else if (element == EL_SWITCHGATE_CLOSED ||
6487              element == EL_SWITCHGATE_CLOSING)
6488     {
6489       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6490
6491       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6492     }
6493   }
6494 }
6495
6496 static int getInvisibleActiveFromInvisibleElement(int element)
6497 {
6498   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6499           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6500           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6501           element);
6502 }
6503
6504 static int getInvisibleFromInvisibleActiveElement(int element)
6505 {
6506   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6507           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6508           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6509           element);
6510 }
6511
6512 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6513 {
6514   int x, y;
6515
6516   SCAN_PLAYFIELD(x, y)
6517   {
6518     int element = Tile[x][y];
6519
6520     if (element == EL_LIGHT_SWITCH &&
6521         game.light_time_left > 0)
6522     {
6523       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6524       TEST_DrawLevelField(x, y);
6525     }
6526     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6527              game.light_time_left == 0)
6528     {
6529       Tile[x][y] = EL_LIGHT_SWITCH;
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (element == EL_EMC_DRIPPER &&
6533              game.light_time_left > 0)
6534     {
6535       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6536       TEST_DrawLevelField(x, y);
6537     }
6538     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6539              game.light_time_left == 0)
6540     {
6541       Tile[x][y] = EL_EMC_DRIPPER;
6542       TEST_DrawLevelField(x, y);
6543     }
6544     else if (element == EL_INVISIBLE_STEELWALL ||
6545              element == EL_INVISIBLE_WALL ||
6546              element == EL_INVISIBLE_SAND)
6547     {
6548       if (game.light_time_left > 0)
6549         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6550
6551       TEST_DrawLevelField(x, y);
6552
6553       // uncrumble neighbour fields, if needed
6554       if (element == EL_INVISIBLE_SAND)
6555         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6556     }
6557     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6558              element == EL_INVISIBLE_WALL_ACTIVE ||
6559              element == EL_INVISIBLE_SAND_ACTIVE)
6560     {
6561       if (game.light_time_left == 0)
6562         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6563
6564       TEST_DrawLevelField(x, y);
6565
6566       // re-crumble neighbour fields, if needed
6567       if (element == EL_INVISIBLE_SAND)
6568         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6569     }
6570   }
6571 }
6572
6573 static void RedrawAllInvisibleElementsForLenses(void)
6574 {
6575   int x, y;
6576
6577   SCAN_PLAYFIELD(x, y)
6578   {
6579     int element = Tile[x][y];
6580
6581     if (element == EL_EMC_DRIPPER &&
6582         game.lenses_time_left > 0)
6583     {
6584       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6588              game.lenses_time_left == 0)
6589     {
6590       Tile[x][y] = EL_EMC_DRIPPER;
6591       TEST_DrawLevelField(x, y);
6592     }
6593     else if (element == EL_INVISIBLE_STEELWALL ||
6594              element == EL_INVISIBLE_WALL ||
6595              element == EL_INVISIBLE_SAND)
6596     {
6597       if (game.lenses_time_left > 0)
6598         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6599
6600       TEST_DrawLevelField(x, y);
6601
6602       // uncrumble neighbour fields, if needed
6603       if (element == EL_INVISIBLE_SAND)
6604         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6605     }
6606     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6607              element == EL_INVISIBLE_WALL_ACTIVE ||
6608              element == EL_INVISIBLE_SAND_ACTIVE)
6609     {
6610       if (game.lenses_time_left == 0)
6611         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6612
6613       TEST_DrawLevelField(x, y);
6614
6615       // re-crumble neighbour fields, if needed
6616       if (element == EL_INVISIBLE_SAND)
6617         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6618     }
6619   }
6620 }
6621
6622 static void RedrawAllInvisibleElementsForMagnifier(void)
6623 {
6624   int x, y;
6625
6626   SCAN_PLAYFIELD(x, y)
6627   {
6628     int element = Tile[x][y];
6629
6630     if (element == EL_EMC_FAKE_GRASS &&
6631         game.magnify_time_left > 0)
6632     {
6633       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6637              game.magnify_time_left == 0)
6638     {
6639       Tile[x][y] = EL_EMC_FAKE_GRASS;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (IS_GATE_GRAY(element) &&
6643              game.magnify_time_left > 0)
6644     {
6645       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6646                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6647                     IS_EM_GATE_GRAY(element) ?
6648                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6649                     IS_EMC_GATE_GRAY(element) ?
6650                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6651                     IS_DC_GATE_GRAY(element) ?
6652                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6653                     element);
6654       TEST_DrawLevelField(x, y);
6655     }
6656     else if (IS_GATE_GRAY_ACTIVE(element) &&
6657              game.magnify_time_left == 0)
6658     {
6659       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6660                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6661                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6662                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6663                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6664                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6665                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6666                     EL_DC_GATE_WHITE_GRAY :
6667                     element);
6668       TEST_DrawLevelField(x, y);
6669     }
6670   }
6671 }
6672
6673 static void ToggleLightSwitch(int x, int y)
6674 {
6675   int element = Tile[x][y];
6676
6677   game.light_time_left =
6678     (element == EL_LIGHT_SWITCH ?
6679      level.time_light * FRAMES_PER_SECOND : 0);
6680
6681   RedrawAllLightSwitchesAndInvisibleElements();
6682 }
6683
6684 static void ActivateTimegateSwitch(int x, int y)
6685 {
6686   int xx, yy;
6687
6688   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6689
6690   SCAN_PLAYFIELD(xx, yy)
6691   {
6692     int element = Tile[xx][yy];
6693
6694     if (element == EL_TIMEGATE_CLOSED ||
6695         element == EL_TIMEGATE_CLOSING)
6696     {
6697       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6698       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6699     }
6700
6701     /*
6702     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6703     {
6704       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6705       TEST_DrawLevelField(xx, yy);
6706     }
6707     */
6708
6709   }
6710
6711   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6712                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6713 }
6714
6715 static void Impact(int x, int y)
6716 {
6717   boolean last_line = (y == lev_fieldy - 1);
6718   boolean object_hit = FALSE;
6719   boolean impact = (last_line || object_hit);
6720   int element = Tile[x][y];
6721   int smashed = EL_STEELWALL;
6722
6723   if (!last_line)       // check if element below was hit
6724   {
6725     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6726       return;
6727
6728     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6729                                          MovDir[x][y + 1] != MV_DOWN ||
6730                                          MovPos[x][y + 1] <= TILEY / 2));
6731
6732     // do not smash moving elements that left the smashed field in time
6733     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6734         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6735       object_hit = FALSE;
6736
6737 #if USE_QUICKSAND_IMPACT_BUGFIX
6738     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6739     {
6740       RemoveMovingField(x, y + 1);
6741       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6742       Tile[x][y + 2] = EL_ROCK;
6743       TEST_DrawLevelField(x, y + 2);
6744
6745       object_hit = TRUE;
6746     }
6747
6748     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6749     {
6750       RemoveMovingField(x, y + 1);
6751       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6752       Tile[x][y + 2] = EL_ROCK;
6753       TEST_DrawLevelField(x, y + 2);
6754
6755       object_hit = TRUE;
6756     }
6757 #endif
6758
6759     if (object_hit)
6760       smashed = MovingOrBlocked2Element(x, y + 1);
6761
6762     impact = (last_line || object_hit);
6763   }
6764
6765   if (!last_line && smashed == EL_ACID) // element falls into acid
6766   {
6767     SplashAcid(x, y + 1);
6768     return;
6769   }
6770
6771   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6772   // only reset graphic animation if graphic really changes after impact
6773   if (impact &&
6774       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6775   {
6776     ResetGfxAnimation(x, y);
6777     TEST_DrawLevelField(x, y);
6778   }
6779
6780   if (impact && CAN_EXPLODE_IMPACT(element))
6781   {
6782     Bang(x, y);
6783     return;
6784   }
6785   else if (impact && element == EL_PEARL &&
6786            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6787   {
6788     ResetGfxAnimation(x, y);
6789
6790     Tile[x][y] = EL_PEARL_BREAKING;
6791     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6792     return;
6793   }
6794   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6795   {
6796     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6797
6798     return;
6799   }
6800
6801   if (impact && element == EL_AMOEBA_DROP)
6802   {
6803     if (object_hit && IS_PLAYER(x, y + 1))
6804       KillPlayerUnlessEnemyProtected(x, y + 1);
6805     else if (object_hit && smashed == EL_PENGUIN)
6806       Bang(x, y + 1);
6807     else
6808     {
6809       Tile[x][y] = EL_AMOEBA_GROWING;
6810       Store[x][y] = EL_AMOEBA_WET;
6811
6812       ResetRandomAnimationValue(x, y);
6813     }
6814     return;
6815   }
6816
6817   if (object_hit)               // check which object was hit
6818   {
6819     if ((CAN_PASS_MAGIC_WALL(element) && 
6820          (smashed == EL_MAGIC_WALL ||
6821           smashed == EL_BD_MAGIC_WALL)) ||
6822         (CAN_PASS_DC_MAGIC_WALL(element) &&
6823          smashed == EL_DC_MAGIC_WALL))
6824     {
6825       int xx, yy;
6826       int activated_magic_wall =
6827         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6828          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6829          EL_DC_MAGIC_WALL_ACTIVE);
6830
6831       // activate magic wall / mill
6832       SCAN_PLAYFIELD(xx, yy)
6833       {
6834         if (Tile[xx][yy] == smashed)
6835           Tile[xx][yy] = activated_magic_wall;
6836       }
6837
6838       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6839       game.magic_wall_active = TRUE;
6840
6841       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6842                             SND_MAGIC_WALL_ACTIVATING :
6843                             smashed == EL_BD_MAGIC_WALL ?
6844                             SND_BD_MAGIC_WALL_ACTIVATING :
6845                             SND_DC_MAGIC_WALL_ACTIVATING));
6846     }
6847
6848     if (IS_PLAYER(x, y + 1))
6849     {
6850       if (CAN_SMASH_PLAYER(element))
6851       {
6852         KillPlayerUnlessEnemyProtected(x, y + 1);
6853         return;
6854       }
6855     }
6856     else if (smashed == EL_PENGUIN)
6857     {
6858       if (CAN_SMASH_PLAYER(element))
6859       {
6860         Bang(x, y + 1);
6861         return;
6862       }
6863     }
6864     else if (element == EL_BD_DIAMOND)
6865     {
6866       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6867       {
6868         Bang(x, y + 1);
6869         return;
6870       }
6871     }
6872     else if (((element == EL_SP_INFOTRON ||
6873                element == EL_SP_ZONK) &&
6874               (smashed == EL_SP_SNIKSNAK ||
6875                smashed == EL_SP_ELECTRON ||
6876                smashed == EL_SP_DISK_ORANGE)) ||
6877              (element == EL_SP_INFOTRON &&
6878               smashed == EL_SP_DISK_YELLOW))
6879     {
6880       Bang(x, y + 1);
6881       return;
6882     }
6883     else if (CAN_SMASH_EVERYTHING(element))
6884     {
6885       if (IS_CLASSIC_ENEMY(smashed) ||
6886           CAN_EXPLODE_SMASHED(smashed))
6887       {
6888         Bang(x, y + 1);
6889         return;
6890       }
6891       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6892       {
6893         if (smashed == EL_LAMP ||
6894             smashed == EL_LAMP_ACTIVE)
6895         {
6896           Bang(x, y + 1);
6897           return;
6898         }
6899         else if (smashed == EL_NUT)
6900         {
6901           Tile[x][y + 1] = EL_NUT_BREAKING;
6902           PlayLevelSound(x, y, SND_NUT_BREAKING);
6903           RaiseScoreElement(EL_NUT);
6904           return;
6905         }
6906         else if (smashed == EL_PEARL)
6907         {
6908           ResetGfxAnimation(x, y);
6909
6910           Tile[x][y + 1] = EL_PEARL_BREAKING;
6911           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6912           return;
6913         }
6914         else if (smashed == EL_DIAMOND)
6915         {
6916           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6917           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6918           return;
6919         }
6920         else if (IS_BELT_SWITCH(smashed))
6921         {
6922           ToggleBeltSwitch(x, y + 1);
6923         }
6924         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6925                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6926                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6927                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6928         {
6929           ToggleSwitchgateSwitch();
6930         }
6931         else if (smashed == EL_LIGHT_SWITCH ||
6932                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6933         {
6934           ToggleLightSwitch(x, y + 1);
6935         }
6936         else
6937         {
6938           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6939
6940           CheckElementChangeBySide(x, y + 1, smashed, element,
6941                                    CE_SWITCHED, CH_SIDE_TOP);
6942           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6943                                             CH_SIDE_TOP);
6944         }
6945       }
6946       else
6947       {
6948         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6949       }
6950     }
6951   }
6952
6953   // play sound of magic wall / mill
6954   if (!last_line &&
6955       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6956        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6957        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6958   {
6959     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6960       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6961     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6962       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6963     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6964       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6965
6966     return;
6967   }
6968
6969   // play sound of object that hits the ground
6970   if (last_line || object_hit)
6971     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6972 }
6973
6974 static void TurnRoundExt(int x, int y)
6975 {
6976   static struct
6977   {
6978     int dx, dy;
6979   } move_xy[] =
6980   {
6981     {  0,  0 },
6982     { -1,  0 },
6983     { +1,  0 },
6984     {  0,  0 },
6985     {  0, -1 },
6986     {  0,  0 }, { 0, 0 }, { 0, 0 },
6987     {  0, +1 }
6988   };
6989   static struct
6990   {
6991     int left, right, back;
6992   } turn[] =
6993   {
6994     { 0,        0,              0        },
6995     { MV_DOWN,  MV_UP,          MV_RIGHT },
6996     { MV_UP,    MV_DOWN,        MV_LEFT  },
6997     { 0,        0,              0        },
6998     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6999     { 0,        0,              0        },
7000     { 0,        0,              0        },
7001     { 0,        0,              0        },
7002     { MV_RIGHT, MV_LEFT,        MV_UP    }
7003   };
7004
7005   int element = Tile[x][y];
7006   int move_pattern = element_info[element].move_pattern;
7007
7008   int old_move_dir = MovDir[x][y];
7009   int left_dir  = turn[old_move_dir].left;
7010   int right_dir = turn[old_move_dir].right;
7011   int back_dir  = turn[old_move_dir].back;
7012
7013   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7014   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7015   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7016   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7017
7018   int left_x  = x + left_dx,  left_y  = y + left_dy;
7019   int right_x = x + right_dx, right_y = y + right_dy;
7020   int move_x  = x + move_dx,  move_y  = y + move_dy;
7021
7022   int xx, yy;
7023
7024   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7025   {
7026     TestIfBadThingTouchesOtherBadThing(x, y);
7027
7028     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7029       MovDir[x][y] = right_dir;
7030     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7031       MovDir[x][y] = left_dir;
7032
7033     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7034       MovDelay[x][y] = 9;
7035     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7036       MovDelay[x][y] = 1;
7037   }
7038   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7039   {
7040     TestIfBadThingTouchesOtherBadThing(x, y);
7041
7042     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7043       MovDir[x][y] = left_dir;
7044     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7045       MovDir[x][y] = right_dir;
7046
7047     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7048       MovDelay[x][y] = 9;
7049     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7050       MovDelay[x][y] = 1;
7051   }
7052   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7053   {
7054     TestIfBadThingTouchesOtherBadThing(x, y);
7055
7056     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7057       MovDir[x][y] = left_dir;
7058     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7059       MovDir[x][y] = right_dir;
7060
7061     if (MovDir[x][y] != old_move_dir)
7062       MovDelay[x][y] = 9;
7063   }
7064   else if (element == EL_YAMYAM)
7065   {
7066     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7067     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7068
7069     if (can_turn_left && can_turn_right)
7070       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7071     else if (can_turn_left)
7072       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7073     else if (can_turn_right)
7074       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7075     else
7076       MovDir[x][y] = back_dir;
7077
7078     MovDelay[x][y] = 16 + 16 * RND(3);
7079   }
7080   else if (element == EL_DARK_YAMYAM)
7081   {
7082     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7083                                                          left_x, left_y);
7084     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7085                                                          right_x, right_y);
7086
7087     if (can_turn_left && can_turn_right)
7088       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7089     else if (can_turn_left)
7090       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7091     else if (can_turn_right)
7092       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7093     else
7094       MovDir[x][y] = back_dir;
7095
7096     MovDelay[x][y] = 16 + 16 * RND(3);
7097   }
7098   else if (element == EL_PACMAN)
7099   {
7100     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7101     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7102
7103     if (can_turn_left && can_turn_right)
7104       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7105     else if (can_turn_left)
7106       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7107     else if (can_turn_right)
7108       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7109     else
7110       MovDir[x][y] = back_dir;
7111
7112     MovDelay[x][y] = 6 + RND(40);
7113   }
7114   else if (element == EL_PIG)
7115   {
7116     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7117     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7118     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7119     boolean should_turn_left, should_turn_right, should_move_on;
7120     int rnd_value = 24;
7121     int rnd = RND(rnd_value);
7122
7123     should_turn_left = (can_turn_left &&
7124                         (!can_move_on ||
7125                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7126                                                    y + back_dy + left_dy)));
7127     should_turn_right = (can_turn_right &&
7128                          (!can_move_on ||
7129                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7130                                                     y + back_dy + right_dy)));
7131     should_move_on = (can_move_on &&
7132                       (!can_turn_left ||
7133                        !can_turn_right ||
7134                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7135                                                  y + move_dy + left_dy) ||
7136                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7137                                                  y + move_dy + right_dy)));
7138
7139     if (should_turn_left || should_turn_right || should_move_on)
7140     {
7141       if (should_turn_left && should_turn_right && should_move_on)
7142         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7143                         rnd < 2 * rnd_value / 3 ? right_dir :
7144                         old_move_dir);
7145       else if (should_turn_left && should_turn_right)
7146         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7147       else if (should_turn_left && should_move_on)
7148         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7149       else if (should_turn_right && should_move_on)
7150         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7151       else if (should_turn_left)
7152         MovDir[x][y] = left_dir;
7153       else if (should_turn_right)
7154         MovDir[x][y] = right_dir;
7155       else if (should_move_on)
7156         MovDir[x][y] = old_move_dir;
7157     }
7158     else if (can_move_on && rnd > rnd_value / 8)
7159       MovDir[x][y] = old_move_dir;
7160     else if (can_turn_left && can_turn_right)
7161       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7162     else if (can_turn_left && rnd > rnd_value / 8)
7163       MovDir[x][y] = left_dir;
7164     else if (can_turn_right && rnd > rnd_value/8)
7165       MovDir[x][y] = right_dir;
7166     else
7167       MovDir[x][y] = back_dir;
7168
7169     xx = x + move_xy[MovDir[x][y]].dx;
7170     yy = y + move_xy[MovDir[x][y]].dy;
7171
7172     if (!IN_LEV_FIELD(xx, yy) ||
7173         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7174       MovDir[x][y] = old_move_dir;
7175
7176     MovDelay[x][y] = 0;
7177   }
7178   else if (element == EL_DRAGON)
7179   {
7180     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7181     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7182     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7183     int rnd_value = 24;
7184     int rnd = RND(rnd_value);
7185
7186     if (can_move_on && rnd > rnd_value / 8)
7187       MovDir[x][y] = old_move_dir;
7188     else if (can_turn_left && can_turn_right)
7189       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7190     else if (can_turn_left && rnd > rnd_value / 8)
7191       MovDir[x][y] = left_dir;
7192     else if (can_turn_right && rnd > rnd_value / 8)
7193       MovDir[x][y] = right_dir;
7194     else
7195       MovDir[x][y] = back_dir;
7196
7197     xx = x + move_xy[MovDir[x][y]].dx;
7198     yy = y + move_xy[MovDir[x][y]].dy;
7199
7200     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7201       MovDir[x][y] = old_move_dir;
7202
7203     MovDelay[x][y] = 0;
7204   }
7205   else if (element == EL_MOLE)
7206   {
7207     boolean can_move_on =
7208       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7209                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7210                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7211     if (!can_move_on)
7212     {
7213       boolean can_turn_left =
7214         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7215                               IS_AMOEBOID(Tile[left_x][left_y])));
7216
7217       boolean can_turn_right =
7218         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7219                               IS_AMOEBOID(Tile[right_x][right_y])));
7220
7221       if (can_turn_left && can_turn_right)
7222         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7223       else if (can_turn_left)
7224         MovDir[x][y] = left_dir;
7225       else
7226         MovDir[x][y] = right_dir;
7227     }
7228
7229     if (MovDir[x][y] != old_move_dir)
7230       MovDelay[x][y] = 9;
7231   }
7232   else if (element == EL_BALLOON)
7233   {
7234     MovDir[x][y] = game.wind_direction;
7235     MovDelay[x][y] = 0;
7236   }
7237   else if (element == EL_SPRING)
7238   {
7239     if (MovDir[x][y] & MV_HORIZONTAL)
7240     {
7241       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7242           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7243       {
7244         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7245         ResetGfxAnimation(move_x, move_y);
7246         TEST_DrawLevelField(move_x, move_y);
7247
7248         MovDir[x][y] = back_dir;
7249       }
7250       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7251                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7252         MovDir[x][y] = MV_NONE;
7253     }
7254
7255     MovDelay[x][y] = 0;
7256   }
7257   else if (element == EL_ROBOT ||
7258            element == EL_SATELLITE ||
7259            element == EL_PENGUIN ||
7260            element == EL_EMC_ANDROID)
7261   {
7262     int attr_x = -1, attr_y = -1;
7263
7264     if (game.all_players_gone)
7265     {
7266       attr_x = game.exit_x;
7267       attr_y = game.exit_y;
7268     }
7269     else
7270     {
7271       int i;
7272
7273       for (i = 0; i < MAX_PLAYERS; i++)
7274       {
7275         struct PlayerInfo *player = &stored_player[i];
7276         int jx = player->jx, jy = player->jy;
7277
7278         if (!player->active)
7279           continue;
7280
7281         if (attr_x == -1 ||
7282             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7283         {
7284           attr_x = jx;
7285           attr_y = jy;
7286         }
7287       }
7288     }
7289
7290     if (element == EL_ROBOT &&
7291         game.robot_wheel_x >= 0 &&
7292         game.robot_wheel_y >= 0 &&
7293         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7294          game.engine_version < VERSION_IDENT(3,1,0,0)))
7295     {
7296       attr_x = game.robot_wheel_x;
7297       attr_y = game.robot_wheel_y;
7298     }
7299
7300     if (element == EL_PENGUIN)
7301     {
7302       int i;
7303       struct XY *xy = xy_topdown;
7304
7305       for (i = 0; i < NUM_DIRECTIONS; i++)
7306       {
7307         int ex = x + xy[i].x;
7308         int ey = y + xy[i].y;
7309
7310         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7311                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7312                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7313                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7314         {
7315           attr_x = ex;
7316           attr_y = ey;
7317           break;
7318         }
7319       }
7320     }
7321
7322     MovDir[x][y] = MV_NONE;
7323     if (attr_x < x)
7324       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7325     else if (attr_x > x)
7326       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7327     if (attr_y < y)
7328       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7329     else if (attr_y > y)
7330       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7331
7332     if (element == EL_ROBOT)
7333     {
7334       int newx, newy;
7335
7336       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7337         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7338       Moving2Blocked(x, y, &newx, &newy);
7339
7340       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7341         MovDelay[x][y] = 8 + 8 * !RND(3);
7342       else
7343         MovDelay[x][y] = 16;
7344     }
7345     else if (element == EL_PENGUIN)
7346     {
7347       int newx, newy;
7348
7349       MovDelay[x][y] = 1;
7350
7351       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7352       {
7353         boolean first_horiz = RND(2);
7354         int new_move_dir = MovDir[x][y];
7355
7356         MovDir[x][y] =
7357           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358         Moving2Blocked(x, y, &newx, &newy);
7359
7360         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7361           return;
7362
7363         MovDir[x][y] =
7364           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7365         Moving2Blocked(x, y, &newx, &newy);
7366
7367         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7368           return;
7369
7370         MovDir[x][y] = old_move_dir;
7371         return;
7372       }
7373     }
7374     else if (element == EL_SATELLITE)
7375     {
7376       int newx, newy;
7377
7378       MovDelay[x][y] = 1;
7379
7380       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7381       {
7382         boolean first_horiz = RND(2);
7383         int new_move_dir = MovDir[x][y];
7384
7385         MovDir[x][y] =
7386           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7387         Moving2Blocked(x, y, &newx, &newy);
7388
7389         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7390           return;
7391
7392         MovDir[x][y] =
7393           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7394         Moving2Blocked(x, y, &newx, &newy);
7395
7396         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7397           return;
7398
7399         MovDir[x][y] = old_move_dir;
7400         return;
7401       }
7402     }
7403     else if (element == EL_EMC_ANDROID)
7404     {
7405       static int check_pos[16] =
7406       {
7407         -1,             //  0 => (invalid)
7408         7,              //  1 => MV_LEFT
7409         3,              //  2 => MV_RIGHT
7410         -1,             //  3 => (invalid)
7411         1,              //  4 =>            MV_UP
7412         0,              //  5 => MV_LEFT  | MV_UP
7413         2,              //  6 => MV_RIGHT | MV_UP
7414         -1,             //  7 => (invalid)
7415         5,              //  8 =>            MV_DOWN
7416         6,              //  9 => MV_LEFT  | MV_DOWN
7417         4,              // 10 => MV_RIGHT | MV_DOWN
7418         -1,             // 11 => (invalid)
7419         -1,             // 12 => (invalid)
7420         -1,             // 13 => (invalid)
7421         -1,             // 14 => (invalid)
7422         -1,             // 15 => (invalid)
7423       };
7424       static struct
7425       {
7426         int dx, dy;
7427         int dir;
7428       } check_xy[8] =
7429       {
7430         { -1, -1,       MV_LEFT  | MV_UP   },
7431         {  0, -1,                  MV_UP   },
7432         { +1, -1,       MV_RIGHT | MV_UP   },
7433         { +1,  0,       MV_RIGHT           },
7434         { +1, +1,       MV_RIGHT | MV_DOWN },
7435         {  0, +1,                  MV_DOWN },
7436         { -1, +1,       MV_LEFT  | MV_DOWN },
7437         { -1,  0,       MV_LEFT            },
7438       };
7439       int start_pos, check_order;
7440       boolean can_clone = FALSE;
7441       int i;
7442
7443       // check if there is any free field around current position
7444       for (i = 0; i < 8; i++)
7445       {
7446         int newx = x + check_xy[i].dx;
7447         int newy = y + check_xy[i].dy;
7448
7449         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7450         {
7451           can_clone = TRUE;
7452
7453           break;
7454         }
7455       }
7456
7457       if (can_clone)            // randomly find an element to clone
7458       {
7459         can_clone = FALSE;
7460
7461         start_pos = check_pos[RND(8)];
7462         check_order = (RND(2) ? -1 : +1);
7463
7464         for (i = 0; i < 8; i++)
7465         {
7466           int pos_raw = start_pos + i * check_order;
7467           int pos = (pos_raw + 8) % 8;
7468           int newx = x + check_xy[pos].dx;
7469           int newy = y + check_xy[pos].dy;
7470
7471           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7472           {
7473             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7474             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7475
7476             Store[x][y] = Tile[newx][newy];
7477
7478             can_clone = TRUE;
7479
7480             break;
7481           }
7482         }
7483       }
7484
7485       if (can_clone)            // randomly find a direction to move
7486       {
7487         can_clone = FALSE;
7488
7489         start_pos = check_pos[RND(8)];
7490         check_order = (RND(2) ? -1 : +1);
7491
7492         for (i = 0; i < 8; i++)
7493         {
7494           int pos_raw = start_pos + i * check_order;
7495           int pos = (pos_raw + 8) % 8;
7496           int newx = x + check_xy[pos].dx;
7497           int newy = y + check_xy[pos].dy;
7498           int new_move_dir = check_xy[pos].dir;
7499
7500           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7501           {
7502             MovDir[x][y] = new_move_dir;
7503             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7504
7505             can_clone = TRUE;
7506
7507             break;
7508           }
7509         }
7510       }
7511
7512       if (can_clone)            // cloning and moving successful
7513         return;
7514
7515       // cannot clone -- try to move towards player
7516
7517       start_pos = check_pos[MovDir[x][y] & 0x0f];
7518       check_order = (RND(2) ? -1 : +1);
7519
7520       for (i = 0; i < 3; i++)
7521       {
7522         // first check start_pos, then previous/next or (next/previous) pos
7523         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7524         int pos = (pos_raw + 8) % 8;
7525         int newx = x + check_xy[pos].dx;
7526         int newy = y + check_xy[pos].dy;
7527         int new_move_dir = check_xy[pos].dir;
7528
7529         if (IS_PLAYER(newx, newy))
7530           break;
7531
7532         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7533         {
7534           MovDir[x][y] = new_move_dir;
7535           MovDelay[x][y] = level.android_move_time * 8 + 1;
7536
7537           break;
7538         }
7539       }
7540     }
7541   }
7542   else if (move_pattern == MV_TURNING_LEFT ||
7543            move_pattern == MV_TURNING_RIGHT ||
7544            move_pattern == MV_TURNING_LEFT_RIGHT ||
7545            move_pattern == MV_TURNING_RIGHT_LEFT ||
7546            move_pattern == MV_TURNING_RANDOM ||
7547            move_pattern == MV_ALL_DIRECTIONS)
7548   {
7549     boolean can_turn_left =
7550       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7551     boolean can_turn_right =
7552       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7553
7554     if (element_info[element].move_stepsize == 0)       // "not moving"
7555       return;
7556
7557     if (move_pattern == MV_TURNING_LEFT)
7558       MovDir[x][y] = left_dir;
7559     else if (move_pattern == MV_TURNING_RIGHT)
7560       MovDir[x][y] = right_dir;
7561     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7562       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7563     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7564       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7565     else if (move_pattern == MV_TURNING_RANDOM)
7566       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7567                       can_turn_right && !can_turn_left ? right_dir :
7568                       RND(2) ? left_dir : right_dir);
7569     else if (can_turn_left && can_turn_right)
7570       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7571     else if (can_turn_left)
7572       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7573     else if (can_turn_right)
7574       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7575     else
7576       MovDir[x][y] = back_dir;
7577
7578     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7579   }
7580   else if (move_pattern == MV_HORIZONTAL ||
7581            move_pattern == MV_VERTICAL)
7582   {
7583     if (move_pattern & old_move_dir)
7584       MovDir[x][y] = back_dir;
7585     else if (move_pattern == MV_HORIZONTAL)
7586       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7587     else if (move_pattern == MV_VERTICAL)
7588       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7589
7590     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7591   }
7592   else if (move_pattern & MV_ANY_DIRECTION)
7593   {
7594     MovDir[x][y] = move_pattern;
7595     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7596   }
7597   else if (move_pattern & MV_WIND_DIRECTION)
7598   {
7599     MovDir[x][y] = game.wind_direction;
7600     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7601   }
7602   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7603   {
7604     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7605       MovDir[x][y] = left_dir;
7606     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7607       MovDir[x][y] = right_dir;
7608
7609     if (MovDir[x][y] != old_move_dir)
7610       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7611   }
7612   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7613   {
7614     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7615       MovDir[x][y] = right_dir;
7616     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7617       MovDir[x][y] = left_dir;
7618
7619     if (MovDir[x][y] != old_move_dir)
7620       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7621   }
7622   else if (move_pattern == MV_TOWARDS_PLAYER ||
7623            move_pattern == MV_AWAY_FROM_PLAYER)
7624   {
7625     int attr_x = -1, attr_y = -1;
7626     int newx, newy;
7627     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7628
7629     if (game.all_players_gone)
7630     {
7631       attr_x = game.exit_x;
7632       attr_y = game.exit_y;
7633     }
7634     else
7635     {
7636       int i;
7637
7638       for (i = 0; i < MAX_PLAYERS; i++)
7639       {
7640         struct PlayerInfo *player = &stored_player[i];
7641         int jx = player->jx, jy = player->jy;
7642
7643         if (!player->active)
7644           continue;
7645
7646         if (attr_x == -1 ||
7647             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7648         {
7649           attr_x = jx;
7650           attr_y = jy;
7651         }
7652       }
7653     }
7654
7655     MovDir[x][y] = MV_NONE;
7656     if (attr_x < x)
7657       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7658     else if (attr_x > x)
7659       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7660     if (attr_y < y)
7661       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7662     else if (attr_y > y)
7663       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7664
7665     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7666
7667     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7668     {
7669       boolean first_horiz = RND(2);
7670       int new_move_dir = MovDir[x][y];
7671
7672       if (element_info[element].move_stepsize == 0)     // "not moving"
7673       {
7674         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7675         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7676
7677         return;
7678       }
7679
7680       MovDir[x][y] =
7681         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7682       Moving2Blocked(x, y, &newx, &newy);
7683
7684       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7685         return;
7686
7687       MovDir[x][y] =
7688         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7689       Moving2Blocked(x, y, &newx, &newy);
7690
7691       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7692         return;
7693
7694       MovDir[x][y] = old_move_dir;
7695     }
7696   }
7697   else if (move_pattern == MV_WHEN_PUSHED ||
7698            move_pattern == MV_WHEN_DROPPED)
7699   {
7700     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7701       MovDir[x][y] = MV_NONE;
7702
7703     MovDelay[x][y] = 0;
7704   }
7705   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7706   {
7707     struct XY *test_xy = xy_topdown;
7708     static int test_dir[4] =
7709     {
7710       MV_UP,
7711       MV_LEFT,
7712       MV_RIGHT,
7713       MV_DOWN
7714     };
7715     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7716     int move_preference = -1000000;     // start with very low preference
7717     int new_move_dir = MV_NONE;
7718     int start_test = RND(4);
7719     int i;
7720
7721     for (i = 0; i < NUM_DIRECTIONS; i++)
7722     {
7723       int j = (start_test + i) % 4;
7724       int move_dir = test_dir[j];
7725       int move_dir_preference;
7726
7727       xx = x + test_xy[j].x;
7728       yy = y + test_xy[j].y;
7729
7730       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7731           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7732       {
7733         new_move_dir = move_dir;
7734
7735         break;
7736       }
7737
7738       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7739         continue;
7740
7741       move_dir_preference = -1 * RunnerVisit[xx][yy];
7742       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7743         move_dir_preference = PlayerVisit[xx][yy];
7744
7745       if (move_dir_preference > move_preference)
7746       {
7747         // prefer field that has not been visited for the longest time
7748         move_preference = move_dir_preference;
7749         new_move_dir = move_dir;
7750       }
7751       else if (move_dir_preference == move_preference &&
7752                move_dir == old_move_dir)
7753       {
7754         // prefer last direction when all directions are preferred equally
7755         move_preference = move_dir_preference;
7756         new_move_dir = move_dir;
7757       }
7758     }
7759
7760     MovDir[x][y] = new_move_dir;
7761     if (old_move_dir != new_move_dir)
7762       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7763   }
7764 }
7765
7766 static void TurnRound(int x, int y)
7767 {
7768   int direction = MovDir[x][y];
7769
7770   TurnRoundExt(x, y);
7771
7772   GfxDir[x][y] = MovDir[x][y];
7773
7774   if (direction != MovDir[x][y])
7775     GfxFrame[x][y] = 0;
7776
7777   if (MovDelay[x][y])
7778     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7779
7780   ResetGfxFrame(x, y);
7781 }
7782
7783 static boolean JustBeingPushed(int x, int y)
7784 {
7785   int i;
7786
7787   for (i = 0; i < MAX_PLAYERS; i++)
7788   {
7789     struct PlayerInfo *player = &stored_player[i];
7790
7791     if (player->active && player->is_pushing && player->MovPos)
7792     {
7793       int next_jx = player->jx + (player->jx - player->last_jx);
7794       int next_jy = player->jy + (player->jy - player->last_jy);
7795
7796       if (x == next_jx && y == next_jy)
7797         return TRUE;
7798     }
7799   }
7800
7801   return FALSE;
7802 }
7803
7804 static void StartMoving(int x, int y)
7805 {
7806   boolean started_moving = FALSE;       // some elements can fall _and_ move
7807   int element = Tile[x][y];
7808
7809   if (Stop[x][y])
7810     return;
7811
7812   if (MovDelay[x][y] == 0)
7813     GfxAction[x][y] = ACTION_DEFAULT;
7814
7815   if (CAN_FALL(element) && y < lev_fieldy - 1)
7816   {
7817     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7818         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7819       if (JustBeingPushed(x, y))
7820         return;
7821
7822     if (element == EL_QUICKSAND_FULL)
7823     {
7824       if (IS_FREE(x, y + 1))
7825       {
7826         InitMovingField(x, y, MV_DOWN);
7827         started_moving = TRUE;
7828
7829         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7830 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7831         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7832           Store[x][y] = EL_ROCK;
7833 #else
7834         Store[x][y] = EL_ROCK;
7835 #endif
7836
7837         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7838       }
7839       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7840       {
7841         if (!MovDelay[x][y])
7842         {
7843           MovDelay[x][y] = TILEY + 1;
7844
7845           ResetGfxAnimation(x, y);
7846           ResetGfxAnimation(x, y + 1);
7847         }
7848
7849         if (MovDelay[x][y])
7850         {
7851           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7852           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7853
7854           MovDelay[x][y]--;
7855           if (MovDelay[x][y])
7856             return;
7857         }
7858
7859         Tile[x][y] = EL_QUICKSAND_EMPTY;
7860         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7861         Store[x][y + 1] = Store[x][y];
7862         Store[x][y] = 0;
7863
7864         PlayLevelSoundAction(x, y, ACTION_FILLING);
7865       }
7866       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7867       {
7868         if (!MovDelay[x][y])
7869         {
7870           MovDelay[x][y] = TILEY + 1;
7871
7872           ResetGfxAnimation(x, y);
7873           ResetGfxAnimation(x, y + 1);
7874         }
7875
7876         if (MovDelay[x][y])
7877         {
7878           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7879           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7880
7881           MovDelay[x][y]--;
7882           if (MovDelay[x][y])
7883             return;
7884         }
7885
7886         Tile[x][y] = EL_QUICKSAND_EMPTY;
7887         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7888         Store[x][y + 1] = Store[x][y];
7889         Store[x][y] = 0;
7890
7891         PlayLevelSoundAction(x, y, ACTION_FILLING);
7892       }
7893     }
7894     else if (element == EL_QUICKSAND_FAST_FULL)
7895     {
7896       if (IS_FREE(x, y + 1))
7897       {
7898         InitMovingField(x, y, MV_DOWN);
7899         started_moving = TRUE;
7900
7901         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7902 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7903         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7904           Store[x][y] = EL_ROCK;
7905 #else
7906         Store[x][y] = EL_ROCK;
7907 #endif
7908
7909         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7910       }
7911       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7912       {
7913         if (!MovDelay[x][y])
7914         {
7915           MovDelay[x][y] = TILEY + 1;
7916
7917           ResetGfxAnimation(x, y);
7918           ResetGfxAnimation(x, y + 1);
7919         }
7920
7921         if (MovDelay[x][y])
7922         {
7923           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7924           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7925
7926           MovDelay[x][y]--;
7927           if (MovDelay[x][y])
7928             return;
7929         }
7930
7931         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7932         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7933         Store[x][y + 1] = Store[x][y];
7934         Store[x][y] = 0;
7935
7936         PlayLevelSoundAction(x, y, ACTION_FILLING);
7937       }
7938       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7939       {
7940         if (!MovDelay[x][y])
7941         {
7942           MovDelay[x][y] = TILEY + 1;
7943
7944           ResetGfxAnimation(x, y);
7945           ResetGfxAnimation(x, y + 1);
7946         }
7947
7948         if (MovDelay[x][y])
7949         {
7950           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7951           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7952
7953           MovDelay[x][y]--;
7954           if (MovDelay[x][y])
7955             return;
7956         }
7957
7958         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7959         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7960         Store[x][y + 1] = Store[x][y];
7961         Store[x][y] = 0;
7962
7963         PlayLevelSoundAction(x, y, ACTION_FILLING);
7964       }
7965     }
7966     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7967              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7968     {
7969       InitMovingField(x, y, MV_DOWN);
7970       started_moving = TRUE;
7971
7972       Tile[x][y] = EL_QUICKSAND_FILLING;
7973       Store[x][y] = element;
7974
7975       PlayLevelSoundAction(x, y, ACTION_FILLING);
7976     }
7977     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7978              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7979     {
7980       InitMovingField(x, y, MV_DOWN);
7981       started_moving = TRUE;
7982
7983       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7984       Store[x][y] = element;
7985
7986       PlayLevelSoundAction(x, y, ACTION_FILLING);
7987     }
7988     else if (element == EL_MAGIC_WALL_FULL)
7989     {
7990       if (IS_FREE(x, y + 1))
7991       {
7992         InitMovingField(x, y, MV_DOWN);
7993         started_moving = TRUE;
7994
7995         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7996         Store[x][y] = EL_CHANGED(Store[x][y]);
7997       }
7998       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7999       {
8000         if (!MovDelay[x][y])
8001           MovDelay[x][y] = TILEY / 4 + 1;
8002
8003         if (MovDelay[x][y])
8004         {
8005           MovDelay[x][y]--;
8006           if (MovDelay[x][y])
8007             return;
8008         }
8009
8010         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8011         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8012         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8013         Store[x][y] = 0;
8014       }
8015     }
8016     else if (element == EL_BD_MAGIC_WALL_FULL)
8017     {
8018       if (IS_FREE(x, y + 1))
8019       {
8020         InitMovingField(x, y, MV_DOWN);
8021         started_moving = TRUE;
8022
8023         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8024         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8025       }
8026       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8027       {
8028         if (!MovDelay[x][y])
8029           MovDelay[x][y] = TILEY / 4 + 1;
8030
8031         if (MovDelay[x][y])
8032         {
8033           MovDelay[x][y]--;
8034           if (MovDelay[x][y])
8035             return;
8036         }
8037
8038         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8039         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8040         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8041         Store[x][y] = 0;
8042       }
8043     }
8044     else if (element == EL_DC_MAGIC_WALL_FULL)
8045     {
8046       if (IS_FREE(x, y + 1))
8047       {
8048         InitMovingField(x, y, MV_DOWN);
8049         started_moving = TRUE;
8050
8051         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8052         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8053       }
8054       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8055       {
8056         if (!MovDelay[x][y])
8057           MovDelay[x][y] = TILEY / 4 + 1;
8058
8059         if (MovDelay[x][y])
8060         {
8061           MovDelay[x][y]--;
8062           if (MovDelay[x][y])
8063             return;
8064         }
8065
8066         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8067         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8068         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8069         Store[x][y] = 0;
8070       }
8071     }
8072     else if ((CAN_PASS_MAGIC_WALL(element) &&
8073               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8074                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8075              (CAN_PASS_DC_MAGIC_WALL(element) &&
8076               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8077
8078     {
8079       InitMovingField(x, y, MV_DOWN);
8080       started_moving = TRUE;
8081
8082       Tile[x][y] =
8083         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8084          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8085          EL_DC_MAGIC_WALL_FILLING);
8086       Store[x][y] = element;
8087     }
8088     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8089     {
8090       SplashAcid(x, y + 1);
8091
8092       InitMovingField(x, y, MV_DOWN);
8093       started_moving = TRUE;
8094
8095       Store[x][y] = EL_ACID;
8096     }
8097     else if (
8098              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8099               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8100              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8101               CAN_FALL(element) && WasJustFalling[x][y] &&
8102               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8103
8104              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8105               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8106               (Tile[x][y + 1] == EL_BLOCKED)))
8107     {
8108       /* this is needed for a special case not covered by calling "Impact()"
8109          from "ContinueMoving()": if an element moves to a tile directly below
8110          another element which was just falling on that tile (which was empty
8111          in the previous frame), the falling element above would just stop
8112          instead of smashing the element below (in previous version, the above
8113          element was just checked for "moving" instead of "falling", resulting
8114          in incorrect smashes caused by horizontal movement of the above
8115          element; also, the case of the player being the element to smash was
8116          simply not covered here... :-/ ) */
8117
8118       CheckCollision[x][y] = 0;
8119       CheckImpact[x][y] = 0;
8120
8121       Impact(x, y);
8122     }
8123     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8124     {
8125       if (MovDir[x][y] == MV_NONE)
8126       {
8127         InitMovingField(x, y, MV_DOWN);
8128         started_moving = TRUE;
8129       }
8130     }
8131     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8132     {
8133       if (WasJustFalling[x][y]) // prevent animation from being restarted
8134         MovDir[x][y] = MV_DOWN;
8135
8136       InitMovingField(x, y, MV_DOWN);
8137       started_moving = TRUE;
8138     }
8139     else if (element == EL_AMOEBA_DROP)
8140     {
8141       Tile[x][y] = EL_AMOEBA_GROWING;
8142       Store[x][y] = EL_AMOEBA_WET;
8143     }
8144     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8145               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8146              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8147              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8148     {
8149       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8150                                 (IS_FREE(x - 1, y + 1) ||
8151                                  Tile[x - 1][y + 1] == EL_ACID));
8152       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8153                                 (IS_FREE(x + 1, y + 1) ||
8154                                  Tile[x + 1][y + 1] == EL_ACID));
8155       boolean can_fall_any  = (can_fall_left || can_fall_right);
8156       boolean can_fall_both = (can_fall_left && can_fall_right);
8157       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8158
8159       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8160       {
8161         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162           can_fall_right = FALSE;
8163         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164           can_fall_left = FALSE;
8165         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8166           can_fall_right = FALSE;
8167         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8168           can_fall_left = FALSE;
8169
8170         can_fall_any  = (can_fall_left || can_fall_right);
8171         can_fall_both = FALSE;
8172       }
8173
8174       if (can_fall_both)
8175       {
8176         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8177           can_fall_right = FALSE;       // slip down on left side
8178         else
8179           can_fall_left = !(can_fall_right = RND(2));
8180
8181         can_fall_both = FALSE;
8182       }
8183
8184       if (can_fall_any)
8185       {
8186         // if not determined otherwise, prefer left side for slipping down
8187         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8188         started_moving = TRUE;
8189       }
8190     }
8191     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8192     {
8193       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8194       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8195       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8196       int belt_dir = game.belt_dir[belt_nr];
8197
8198       if ((belt_dir == MV_LEFT  && left_is_free) ||
8199           (belt_dir == MV_RIGHT && right_is_free))
8200       {
8201         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8202
8203         InitMovingField(x, y, belt_dir);
8204         started_moving = TRUE;
8205
8206         Pushed[x][y] = TRUE;
8207         Pushed[nextx][y] = TRUE;
8208
8209         GfxAction[x][y] = ACTION_DEFAULT;
8210       }
8211       else
8212       {
8213         MovDir[x][y] = 0;       // if element was moving, stop it
8214       }
8215     }
8216   }
8217
8218   // not "else if" because of elements that can fall and move (EL_SPRING)
8219   if (CAN_MOVE(element) && !started_moving)
8220   {
8221     int move_pattern = element_info[element].move_pattern;
8222     int newx, newy;
8223
8224     Moving2Blocked(x, y, &newx, &newy);
8225
8226     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8227       return;
8228
8229     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8230         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8231     {
8232       WasJustMoving[x][y] = 0;
8233       CheckCollision[x][y] = 0;
8234
8235       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8236
8237       if (Tile[x][y] != element)        // element has changed
8238         return;
8239     }
8240
8241     if (!MovDelay[x][y])        // start new movement phase
8242     {
8243       // all objects that can change their move direction after each step
8244       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8245
8246       if (element != EL_YAMYAM &&
8247           element != EL_DARK_YAMYAM &&
8248           element != EL_PACMAN &&
8249           !(move_pattern & MV_ANY_DIRECTION) &&
8250           move_pattern != MV_TURNING_LEFT &&
8251           move_pattern != MV_TURNING_RIGHT &&
8252           move_pattern != MV_TURNING_LEFT_RIGHT &&
8253           move_pattern != MV_TURNING_RIGHT_LEFT &&
8254           move_pattern != MV_TURNING_RANDOM)
8255       {
8256         TurnRound(x, y);
8257
8258         if (MovDelay[x][y] && (element == EL_BUG ||
8259                                element == EL_SPACESHIP ||
8260                                element == EL_SP_SNIKSNAK ||
8261                                element == EL_SP_ELECTRON ||
8262                                element == EL_MOLE))
8263           TEST_DrawLevelField(x, y);
8264       }
8265     }
8266
8267     if (MovDelay[x][y])         // wait some time before next movement
8268     {
8269       MovDelay[x][y]--;
8270
8271       if (element == EL_ROBOT ||
8272           element == EL_YAMYAM ||
8273           element == EL_DARK_YAMYAM)
8274       {
8275         DrawLevelElementAnimationIfNeeded(x, y, element);
8276         PlayLevelSoundAction(x, y, ACTION_WAITING);
8277       }
8278       else if (element == EL_SP_ELECTRON)
8279         DrawLevelElementAnimationIfNeeded(x, y, element);
8280       else if (element == EL_DRAGON)
8281       {
8282         int i;
8283         int dir = MovDir[x][y];
8284         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8285         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8286         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8287                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8288                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8289                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8290         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8291
8292         GfxAction[x][y] = ACTION_ATTACKING;
8293
8294         if (IS_PLAYER(x, y))
8295           DrawPlayerField(x, y);
8296         else
8297           TEST_DrawLevelField(x, y);
8298
8299         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8300
8301         for (i = 1; i <= 3; i++)
8302         {
8303           int xx = x + i * dx;
8304           int yy = y + i * dy;
8305           int sx = SCREENX(xx);
8306           int sy = SCREENY(yy);
8307           int flame_graphic = graphic + (i - 1);
8308
8309           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8310             break;
8311
8312           if (MovDelay[x][y])
8313           {
8314             int flamed = MovingOrBlocked2Element(xx, yy);
8315
8316             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8317               Bang(xx, yy);
8318             else
8319               RemoveMovingField(xx, yy);
8320
8321             ChangeDelay[xx][yy] = 0;
8322
8323             Tile[xx][yy] = EL_FLAMES;
8324
8325             if (IN_SCR_FIELD(sx, sy))
8326             {
8327               TEST_DrawLevelFieldCrumbled(xx, yy);
8328               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8329             }
8330           }
8331           else
8332           {
8333             if (Tile[xx][yy] == EL_FLAMES)
8334               Tile[xx][yy] = EL_EMPTY;
8335             TEST_DrawLevelField(xx, yy);
8336           }
8337         }
8338       }
8339
8340       if (MovDelay[x][y])       // element still has to wait some time
8341       {
8342         PlayLevelSoundAction(x, y, ACTION_WAITING);
8343
8344         return;
8345       }
8346     }
8347
8348     // now make next step
8349
8350     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8351
8352     if (DONT_COLLIDE_WITH(element) &&
8353         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8354         !PLAYER_ENEMY_PROTECTED(newx, newy))
8355     {
8356       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8357
8358       return;
8359     }
8360
8361     else if (CAN_MOVE_INTO_ACID(element) &&
8362              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8363              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8364              (MovDir[x][y] == MV_DOWN ||
8365               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8366     {
8367       SplashAcid(newx, newy);
8368       Store[x][y] = EL_ACID;
8369     }
8370     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8371     {
8372       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8373           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8374           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8375           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8376       {
8377         RemoveField(x, y);
8378         TEST_DrawLevelField(x, y);
8379
8380         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8381         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8382           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8383
8384         game.friends_still_needed--;
8385         if (!game.friends_still_needed &&
8386             !game.GameOver &&
8387             game.all_players_gone)
8388           LevelSolved();
8389
8390         return;
8391       }
8392       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8393       {
8394         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8395           TEST_DrawLevelField(newx, newy);
8396         else
8397           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8398       }
8399       else if (!IS_FREE(newx, newy))
8400       {
8401         GfxAction[x][y] = ACTION_WAITING;
8402
8403         if (IS_PLAYER(x, y))
8404           DrawPlayerField(x, y);
8405         else
8406           TEST_DrawLevelField(x, y);
8407
8408         return;
8409       }
8410     }
8411     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8412     {
8413       if (IS_FOOD_PIG(Tile[newx][newy]))
8414       {
8415         if (IS_MOVING(newx, newy))
8416           RemoveMovingField(newx, newy);
8417         else
8418         {
8419           Tile[newx][newy] = EL_EMPTY;
8420           TEST_DrawLevelField(newx, newy);
8421         }
8422
8423         PlayLevelSound(x, y, SND_PIG_DIGGING);
8424       }
8425       else if (!IS_FREE(newx, newy))
8426       {
8427         if (IS_PLAYER(x, y))
8428           DrawPlayerField(x, y);
8429         else
8430           TEST_DrawLevelField(x, y);
8431
8432         return;
8433       }
8434     }
8435     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8436     {
8437       if (Store[x][y] != EL_EMPTY)
8438       {
8439         boolean can_clone = FALSE;
8440         int xx, yy;
8441
8442         // check if element to clone is still there
8443         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8444         {
8445           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8446           {
8447             can_clone = TRUE;
8448
8449             break;
8450           }
8451         }
8452
8453         // cannot clone or target field not free anymore -- do not clone
8454         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8455           Store[x][y] = EL_EMPTY;
8456       }
8457
8458       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8459       {
8460         if (IS_MV_DIAGONAL(MovDir[x][y]))
8461         {
8462           int diagonal_move_dir = MovDir[x][y];
8463           int stored = Store[x][y];
8464           int change_delay = 8;
8465           int graphic;
8466
8467           // android is moving diagonally
8468
8469           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8470
8471           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8472           GfxElement[x][y] = EL_EMC_ANDROID;
8473           GfxAction[x][y] = ACTION_SHRINKING;
8474           GfxDir[x][y] = diagonal_move_dir;
8475           ChangeDelay[x][y] = change_delay;
8476
8477           if (Store[x][y] == EL_EMPTY)
8478             Store[x][y] = GfxElementEmpty[x][y];
8479
8480           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8481                                    GfxDir[x][y]);
8482
8483           DrawLevelGraphicAnimation(x, y, graphic);
8484           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8485
8486           if (Tile[newx][newy] == EL_ACID)
8487           {
8488             SplashAcid(newx, newy);
8489
8490             return;
8491           }
8492
8493           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8494
8495           Store[newx][newy] = EL_EMC_ANDROID;
8496           GfxElement[newx][newy] = EL_EMC_ANDROID;
8497           GfxAction[newx][newy] = ACTION_GROWING;
8498           GfxDir[newx][newy] = diagonal_move_dir;
8499           ChangeDelay[newx][newy] = change_delay;
8500
8501           graphic = el_act_dir2img(GfxElement[newx][newy],
8502                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8503
8504           DrawLevelGraphicAnimation(newx, newy, graphic);
8505           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8506
8507           return;
8508         }
8509         else
8510         {
8511           Tile[newx][newy] = EL_EMPTY;
8512           TEST_DrawLevelField(newx, newy);
8513
8514           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8515         }
8516       }
8517       else if (!IS_FREE(newx, newy))
8518       {
8519         return;
8520       }
8521     }
8522     else if (IS_CUSTOM_ELEMENT(element) &&
8523              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8524     {
8525       if (!DigFieldByCE(newx, newy, element))
8526         return;
8527
8528       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8529       {
8530         RunnerVisit[x][y] = FrameCounter;
8531         PlayerVisit[x][y] /= 8;         // expire player visit path
8532       }
8533     }
8534     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8535     {
8536       if (!IS_FREE(newx, newy))
8537       {
8538         if (IS_PLAYER(x, y))
8539           DrawPlayerField(x, y);
8540         else
8541           TEST_DrawLevelField(x, y);
8542
8543         return;
8544       }
8545       else
8546       {
8547         boolean wanna_flame = !RND(10);
8548         int dx = newx - x, dy = newy - y;
8549         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8550         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8551         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8552                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8553         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8554                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8555
8556         if ((wanna_flame ||
8557              IS_CLASSIC_ENEMY(element1) ||
8558              IS_CLASSIC_ENEMY(element2)) &&
8559             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8560             element1 != EL_FLAMES && element2 != EL_FLAMES)
8561         {
8562           ResetGfxAnimation(x, y);
8563           GfxAction[x][y] = ACTION_ATTACKING;
8564
8565           if (IS_PLAYER(x, y))
8566             DrawPlayerField(x, y);
8567           else
8568             TEST_DrawLevelField(x, y);
8569
8570           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8571
8572           MovDelay[x][y] = 50;
8573
8574           Tile[newx][newy] = EL_FLAMES;
8575           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8576             Tile[newx1][newy1] = EL_FLAMES;
8577           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8578             Tile[newx2][newy2] = EL_FLAMES;
8579
8580           return;
8581         }
8582       }
8583     }
8584     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8585              Tile[newx][newy] == EL_DIAMOND)
8586     {
8587       if (IS_MOVING(newx, newy))
8588         RemoveMovingField(newx, newy);
8589       else
8590       {
8591         Tile[newx][newy] = EL_EMPTY;
8592         TEST_DrawLevelField(newx, newy);
8593       }
8594
8595       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8596     }
8597     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8598              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8599     {
8600       if (AmoebaNr[newx][newy])
8601       {
8602         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8603         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8604             Tile[newx][newy] == EL_BD_AMOEBA)
8605           AmoebaCnt[AmoebaNr[newx][newy]]--;
8606       }
8607
8608       if (IS_MOVING(newx, newy))
8609       {
8610         RemoveMovingField(newx, newy);
8611       }
8612       else
8613       {
8614         Tile[newx][newy] = EL_EMPTY;
8615         TEST_DrawLevelField(newx, newy);
8616       }
8617
8618       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8619     }
8620     else if ((element == EL_PACMAN || element == EL_MOLE)
8621              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8622     {
8623       if (AmoebaNr[newx][newy])
8624       {
8625         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8626         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8627             Tile[newx][newy] == EL_BD_AMOEBA)
8628           AmoebaCnt[AmoebaNr[newx][newy]]--;
8629       }
8630
8631       if (element == EL_MOLE)
8632       {
8633         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8634         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8635
8636         ResetGfxAnimation(x, y);
8637         GfxAction[x][y] = ACTION_DIGGING;
8638         TEST_DrawLevelField(x, y);
8639
8640         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8641
8642         return;                         // wait for shrinking amoeba
8643       }
8644       else      // element == EL_PACMAN
8645       {
8646         Tile[newx][newy] = EL_EMPTY;
8647         TEST_DrawLevelField(newx, newy);
8648         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8649       }
8650     }
8651     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8652              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8653               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8654     {
8655       // wait for shrinking amoeba to completely disappear
8656       return;
8657     }
8658     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8659     {
8660       // object was running against a wall
8661
8662       TurnRound(x, y);
8663
8664       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8665         DrawLevelElementAnimation(x, y, element);
8666
8667       if (DONT_TOUCH(element))
8668         TestIfBadThingTouchesPlayer(x, y);
8669
8670       return;
8671     }
8672
8673     InitMovingField(x, y, MovDir[x][y]);
8674
8675     PlayLevelSoundAction(x, y, ACTION_MOVING);
8676   }
8677
8678   if (MovDir[x][y])
8679     ContinueMoving(x, y);
8680 }
8681
8682 void ContinueMoving(int x, int y)
8683 {
8684   int element = Tile[x][y];
8685   struct ElementInfo *ei = &element_info[element];
8686   int direction = MovDir[x][y];
8687   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8688   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8689   int newx = x + dx, newy = y + dy;
8690   int stored = Store[x][y];
8691   int stored_new = Store[newx][newy];
8692   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8693   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8694   boolean last_line = (newy == lev_fieldy - 1);
8695   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8696
8697   if (pushed_by_player)         // special case: moving object pushed by player
8698   {
8699     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8700   }
8701   else if (use_step_delay)      // special case: moving object has step delay
8702   {
8703     if (!MovDelay[x][y])
8704       MovPos[x][y] += getElementMoveStepsize(x, y);
8705
8706     if (MovDelay[x][y])
8707       MovDelay[x][y]--;
8708     else
8709       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8710
8711     if (MovDelay[x][y])
8712     {
8713       TEST_DrawLevelField(x, y);
8714
8715       return;   // element is still waiting
8716     }
8717   }
8718   else                          // normal case: generically moving object
8719   {
8720     MovPos[x][y] += getElementMoveStepsize(x, y);
8721   }
8722
8723   if (ABS(MovPos[x][y]) < TILEX)
8724   {
8725     TEST_DrawLevelField(x, y);
8726
8727     return;     // element is still moving
8728   }
8729
8730   // element reached destination field
8731
8732   Tile[x][y] = EL_EMPTY;
8733   Tile[newx][newy] = element;
8734   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8735
8736   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8737   {
8738     element = Tile[newx][newy] = EL_ACID;
8739   }
8740   else if (element == EL_MOLE)
8741   {
8742     Tile[x][y] = EL_SAND;
8743
8744     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8745   }
8746   else if (element == EL_QUICKSAND_FILLING)
8747   {
8748     element = Tile[newx][newy] = get_next_element(element);
8749     Store[newx][newy] = Store[x][y];
8750   }
8751   else if (element == EL_QUICKSAND_EMPTYING)
8752   {
8753     Tile[x][y] = get_next_element(element);
8754     element = Tile[newx][newy] = Store[x][y];
8755   }
8756   else if (element == EL_QUICKSAND_FAST_FILLING)
8757   {
8758     element = Tile[newx][newy] = get_next_element(element);
8759     Store[newx][newy] = Store[x][y];
8760   }
8761   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8762   {
8763     Tile[x][y] = get_next_element(element);
8764     element = Tile[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_MAGIC_WALL_FILLING)
8767   {
8768     element = Tile[newx][newy] = get_next_element(element);
8769     if (!game.magic_wall_active)
8770       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8771     Store[newx][newy] = Store[x][y];
8772   }
8773   else if (element == EL_MAGIC_WALL_EMPTYING)
8774   {
8775     Tile[x][y] = get_next_element(element);
8776     if (!game.magic_wall_active)
8777       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8778     element = Tile[newx][newy] = Store[x][y];
8779
8780     InitField(newx, newy, FALSE);
8781   }
8782   else if (element == EL_BD_MAGIC_WALL_FILLING)
8783   {
8784     element = Tile[newx][newy] = get_next_element(element);
8785     if (!game.magic_wall_active)
8786       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8787     Store[newx][newy] = Store[x][y];
8788   }
8789   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8790   {
8791     Tile[x][y] = get_next_element(element);
8792     if (!game.magic_wall_active)
8793       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8794     element = Tile[newx][newy] = Store[x][y];
8795
8796     InitField(newx, newy, FALSE);
8797   }
8798   else if (element == EL_DC_MAGIC_WALL_FILLING)
8799   {
8800     element = Tile[newx][newy] = get_next_element(element);
8801     if (!game.magic_wall_active)
8802       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8803     Store[newx][newy] = Store[x][y];
8804   }
8805   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8806   {
8807     Tile[x][y] = get_next_element(element);
8808     if (!game.magic_wall_active)
8809       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8810     element = Tile[newx][newy] = Store[x][y];
8811
8812     InitField(newx, newy, FALSE);
8813   }
8814   else if (element == EL_AMOEBA_DROPPING)
8815   {
8816     Tile[x][y] = get_next_element(element);
8817     element = Tile[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_SOKOBAN_OBJECT)
8820   {
8821     if (Back[x][y])
8822       Tile[x][y] = Back[x][y];
8823
8824     if (Back[newx][newy])
8825       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8826
8827     Back[x][y] = Back[newx][newy] = 0;
8828   }
8829
8830   Store[x][y] = EL_EMPTY;
8831   MovPos[x][y] = 0;
8832   MovDir[x][y] = 0;
8833   MovDelay[x][y] = 0;
8834
8835   MovDelay[newx][newy] = 0;
8836
8837   if (CAN_CHANGE_OR_HAS_ACTION(element))
8838   {
8839     // copy element change control values to new field
8840     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8841     ChangePage[newx][newy]  = ChangePage[x][y];
8842     ChangeCount[newx][newy] = ChangeCount[x][y];
8843     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8844   }
8845
8846   CustomValue[newx][newy] = CustomValue[x][y];
8847
8848   ChangeDelay[x][y] = 0;
8849   ChangePage[x][y] = -1;
8850   ChangeCount[x][y] = 0;
8851   ChangeEvent[x][y] = -1;
8852
8853   CustomValue[x][y] = 0;
8854
8855   // copy animation control values to new field
8856   GfxFrame[newx][newy]  = GfxFrame[x][y];
8857   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8858   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8859   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8860
8861   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8862
8863   // some elements can leave other elements behind after moving
8864   if (ei->move_leave_element != EL_EMPTY &&
8865       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8866       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8867   {
8868     int move_leave_element = ei->move_leave_element;
8869
8870     // this makes it possible to leave the removed element again
8871     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8872       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8873
8874     Tile[x][y] = move_leave_element;
8875
8876     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8877       MovDir[x][y] = direction;
8878
8879     InitField(x, y, FALSE);
8880
8881     if (GFX_CRUMBLED(Tile[x][y]))
8882       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8883
8884     if (IS_PLAYER_ELEMENT(move_leave_element))
8885       RelocatePlayer(x, y, move_leave_element);
8886   }
8887
8888   // do this after checking for left-behind element
8889   ResetGfxAnimation(x, y);      // reset animation values for old field
8890
8891   if (!CAN_MOVE(element) ||
8892       (CAN_FALL(element) && direction == MV_DOWN &&
8893        (element == EL_SPRING ||
8894         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8895         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8896     GfxDir[x][y] = MovDir[newx][newy] = 0;
8897
8898   TEST_DrawLevelField(x, y);
8899   TEST_DrawLevelField(newx, newy);
8900
8901   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8902
8903   // prevent pushed element from moving on in pushed direction
8904   if (pushed_by_player && CAN_MOVE(element) &&
8905       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8906       !(element_info[element].move_pattern & direction))
8907     TurnRound(newx, newy);
8908
8909   // prevent elements on conveyor belt from moving on in last direction
8910   if (pushed_by_conveyor && CAN_FALL(element) &&
8911       direction & MV_HORIZONTAL)
8912     MovDir[newx][newy] = 0;
8913
8914   if (!pushed_by_player)
8915   {
8916     int nextx = newx + dx, nexty = newy + dy;
8917     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8918
8919     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8920
8921     if (CAN_FALL(element) && direction == MV_DOWN)
8922       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8923
8924     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8925       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8926
8927     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8928       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8929   }
8930
8931   if (DONT_TOUCH(element))      // object may be nasty to player or others
8932   {
8933     TestIfBadThingTouchesPlayer(newx, newy);
8934     TestIfBadThingTouchesFriend(newx, newy);
8935
8936     if (!IS_CUSTOM_ELEMENT(element))
8937       TestIfBadThingTouchesOtherBadThing(newx, newy);
8938   }
8939   else if (element == EL_PENGUIN)
8940     TestIfFriendTouchesBadThing(newx, newy);
8941
8942   if (DONT_GET_HIT_BY(element))
8943   {
8944     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8945   }
8946
8947   // give the player one last chance (one more frame) to move away
8948   if (CAN_FALL(element) && direction == MV_DOWN &&
8949       (last_line || (!IS_FREE(x, newy + 1) &&
8950                      (!IS_PLAYER(x, newy + 1) ||
8951                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8952     Impact(x, newy);
8953
8954   if (pushed_by_player && !game.use_change_when_pushing_bug)
8955   {
8956     int push_side = MV_DIR_OPPOSITE(direction);
8957     struct PlayerInfo *player = PLAYERINFO(x, y);
8958
8959     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8960                                player->index_bit, push_side);
8961     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8962                                         player->index_bit, push_side);
8963   }
8964
8965   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8966     MovDelay[newx][newy] = 1;
8967
8968   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8969
8970   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8971   TestIfElementHitsCustomElement(newx, newy, direction);
8972   TestIfPlayerTouchesCustomElement(newx, newy);
8973   TestIfElementTouchesCustomElement(newx, newy);
8974
8975   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8976       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8977     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8978                              MV_DIR_OPPOSITE(direction));
8979 }
8980
8981 int AmoebaNeighbourNr(int ax, int ay)
8982 {
8983   int i;
8984   int element = Tile[ax][ay];
8985   int group_nr = 0;
8986   struct XY *xy = xy_topdown;
8987
8988   for (i = 0; i < NUM_DIRECTIONS; i++)
8989   {
8990     int x = ax + xy[i].x;
8991     int y = ay + xy[i].y;
8992
8993     if (!IN_LEV_FIELD(x, y))
8994       continue;
8995
8996     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8997       group_nr = AmoebaNr[x][y];
8998   }
8999
9000   return group_nr;
9001 }
9002
9003 static void AmoebaMerge(int ax, int ay)
9004 {
9005   int i, x, y, xx, yy;
9006   int new_group_nr = AmoebaNr[ax][ay];
9007   struct XY *xy = xy_topdown;
9008
9009   if (new_group_nr == 0)
9010     return;
9011
9012   for (i = 0; i < NUM_DIRECTIONS; i++)
9013   {
9014     x = ax + xy[i].x;
9015     y = ay + xy[i].y;
9016
9017     if (!IN_LEV_FIELD(x, y))
9018       continue;
9019
9020     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9021          Tile[x][y] == EL_BD_AMOEBA ||
9022          Tile[x][y] == EL_AMOEBA_DEAD) &&
9023         AmoebaNr[x][y] != new_group_nr)
9024     {
9025       int old_group_nr = AmoebaNr[x][y];
9026
9027       if (old_group_nr == 0)
9028         return;
9029
9030       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9031       AmoebaCnt[old_group_nr] = 0;
9032       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9033       AmoebaCnt2[old_group_nr] = 0;
9034
9035       SCAN_PLAYFIELD(xx, yy)
9036       {
9037         if (AmoebaNr[xx][yy] == old_group_nr)
9038           AmoebaNr[xx][yy] = new_group_nr;
9039       }
9040     }
9041   }
9042 }
9043
9044 void AmoebaToDiamond(int ax, int ay)
9045 {
9046   int i, x, y;
9047
9048   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9049   {
9050     int group_nr = AmoebaNr[ax][ay];
9051
9052 #ifdef DEBUG
9053     if (group_nr == 0)
9054     {
9055       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9056       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9057
9058       return;
9059     }
9060 #endif
9061
9062     SCAN_PLAYFIELD(x, y)
9063     {
9064       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9065       {
9066         AmoebaNr[x][y] = 0;
9067         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9068       }
9069     }
9070
9071     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9072                             SND_AMOEBA_TURNING_TO_GEM :
9073                             SND_AMOEBA_TURNING_TO_ROCK));
9074     Bang(ax, ay);
9075   }
9076   else
9077   {
9078     struct XY *xy = xy_topdown;
9079
9080     for (i = 0; i < NUM_DIRECTIONS; i++)
9081     {
9082       x = ax + xy[i].x;
9083       y = ay + xy[i].y;
9084
9085       if (!IN_LEV_FIELD(x, y))
9086         continue;
9087
9088       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9089       {
9090         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9091                               SND_AMOEBA_TURNING_TO_GEM :
9092                               SND_AMOEBA_TURNING_TO_ROCK));
9093         Bang(x, y);
9094       }
9095     }
9096   }
9097 }
9098
9099 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9100 {
9101   int x, y;
9102   int group_nr = AmoebaNr[ax][ay];
9103   boolean done = FALSE;
9104
9105 #ifdef DEBUG
9106   if (group_nr == 0)
9107   {
9108     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9109     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9110
9111     return;
9112   }
9113 #endif
9114
9115   SCAN_PLAYFIELD(x, y)
9116   {
9117     if (AmoebaNr[x][y] == group_nr &&
9118         (Tile[x][y] == EL_AMOEBA_DEAD ||
9119          Tile[x][y] == EL_BD_AMOEBA ||
9120          Tile[x][y] == EL_AMOEBA_GROWING))
9121     {
9122       AmoebaNr[x][y] = 0;
9123       Tile[x][y] = new_element;
9124       InitField(x, y, FALSE);
9125       TEST_DrawLevelField(x, y);
9126       done = TRUE;
9127     }
9128   }
9129
9130   if (done)
9131     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9132                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9133                             SND_BD_AMOEBA_TURNING_TO_GEM));
9134 }
9135
9136 static void AmoebaGrowing(int x, int y)
9137 {
9138   static DelayCounter sound_delay = { 0 };
9139
9140   if (!MovDelay[x][y])          // start new growing cycle
9141   {
9142     MovDelay[x][y] = 7;
9143
9144     if (DelayReached(&sound_delay))
9145     {
9146       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9147       sound_delay.value = 30;
9148     }
9149   }
9150
9151   if (MovDelay[x][y])           // wait some time before growing bigger
9152   {
9153     MovDelay[x][y]--;
9154     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9155     {
9156       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9157                                            6 - MovDelay[x][y]);
9158
9159       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9160     }
9161
9162     if (!MovDelay[x][y])
9163     {
9164       Tile[x][y] = Store[x][y];
9165       Store[x][y] = 0;
9166       TEST_DrawLevelField(x, y);
9167     }
9168   }
9169 }
9170
9171 static void AmoebaShrinking(int x, int y)
9172 {
9173   static DelayCounter sound_delay = { 0 };
9174
9175   if (!MovDelay[x][y])          // start new shrinking cycle
9176   {
9177     MovDelay[x][y] = 7;
9178
9179     if (DelayReached(&sound_delay))
9180       sound_delay.value = 30;
9181   }
9182
9183   if (MovDelay[x][y])           // wait some time before shrinking
9184   {
9185     MovDelay[x][y]--;
9186     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9187     {
9188       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9189                                            6 - MovDelay[x][y]);
9190
9191       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9192     }
9193
9194     if (!MovDelay[x][y])
9195     {
9196       Tile[x][y] = EL_EMPTY;
9197       TEST_DrawLevelField(x, y);
9198
9199       // don't let mole enter this field in this cycle;
9200       // (give priority to objects falling to this field from above)
9201       Stop[x][y] = TRUE;
9202     }
9203   }
9204 }
9205
9206 static void AmoebaReproduce(int ax, int ay)
9207 {
9208   int i;
9209   int element = Tile[ax][ay];
9210   int graphic = el2img(element);
9211   int newax = ax, neway = ay;
9212   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9213   struct XY *xy = xy_topdown;
9214
9215   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9216   {
9217     Tile[ax][ay] = EL_AMOEBA_DEAD;
9218     TEST_DrawLevelField(ax, ay);
9219     return;
9220   }
9221
9222   if (IS_ANIMATED(graphic))
9223     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9224
9225   if (!MovDelay[ax][ay])        // start making new amoeba field
9226     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9227
9228   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9229   {
9230     MovDelay[ax][ay]--;
9231     if (MovDelay[ax][ay])
9232       return;
9233   }
9234
9235   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9236   {
9237     int start = RND(4);
9238     int x = ax + xy[start].x;
9239     int y = ay + xy[start].y;
9240
9241     if (!IN_LEV_FIELD(x, y))
9242       return;
9243
9244     if (IS_FREE(x, y) ||
9245         CAN_GROW_INTO(Tile[x][y]) ||
9246         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9247         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9248     {
9249       newax = x;
9250       neway = y;
9251     }
9252
9253     if (newax == ax && neway == ay)
9254       return;
9255   }
9256   else                          // normal or "filled" (BD style) amoeba
9257   {
9258     int start = RND(4);
9259     boolean waiting_for_player = FALSE;
9260
9261     for (i = 0; i < NUM_DIRECTIONS; i++)
9262     {
9263       int j = (start + i) % 4;
9264       int x = ax + xy[j].x;
9265       int y = ay + xy[j].y;
9266
9267       if (!IN_LEV_FIELD(x, y))
9268         continue;
9269
9270       if (IS_FREE(x, y) ||
9271           CAN_GROW_INTO(Tile[x][y]) ||
9272           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9273           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9274       {
9275         newax = x;
9276         neway = y;
9277         break;
9278       }
9279       else if (IS_PLAYER(x, y))
9280         waiting_for_player = TRUE;
9281     }
9282
9283     if (newax == ax && neway == ay)             // amoeba cannot grow
9284     {
9285       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9286       {
9287         Tile[ax][ay] = EL_AMOEBA_DEAD;
9288         TEST_DrawLevelField(ax, ay);
9289         AmoebaCnt[AmoebaNr[ax][ay]]--;
9290
9291         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9292         {
9293           if (element == EL_AMOEBA_FULL)
9294             AmoebaToDiamond(ax, ay);
9295           else if (element == EL_BD_AMOEBA)
9296             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9297         }
9298       }
9299       return;
9300     }
9301     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9302     {
9303       // amoeba gets larger by growing in some direction
9304
9305       int new_group_nr = AmoebaNr[ax][ay];
9306
9307 #ifdef DEBUG
9308   if (new_group_nr == 0)
9309   {
9310     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9311           newax, neway);
9312     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9313
9314     return;
9315   }
9316 #endif
9317
9318       AmoebaNr[newax][neway] = new_group_nr;
9319       AmoebaCnt[new_group_nr]++;
9320       AmoebaCnt2[new_group_nr]++;
9321
9322       // if amoeba touches other amoeba(s) after growing, unify them
9323       AmoebaMerge(newax, neway);
9324
9325       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9326       {
9327         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9328         return;
9329       }
9330     }
9331   }
9332
9333   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9334       (neway == lev_fieldy - 1 && newax != ax))
9335   {
9336     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9337     Store[newax][neway] = element;
9338   }
9339   else if (neway == ay || element == EL_EMC_DRIPPER)
9340   {
9341     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9342
9343     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9344   }
9345   else
9346   {
9347     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9348     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9349     Store[ax][ay] = EL_AMOEBA_DROP;
9350     ContinueMoving(ax, ay);
9351     return;
9352   }
9353
9354   TEST_DrawLevelField(newax, neway);
9355 }
9356
9357 static void Life(int ax, int ay)
9358 {
9359   int x1, y1, x2, y2;
9360   int life_time = 40;
9361   int element = Tile[ax][ay];
9362   int graphic = el2img(element);
9363   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9364                          level.biomaze);
9365   boolean changed = FALSE;
9366
9367   if (IS_ANIMATED(graphic))
9368     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9369
9370   if (Stop[ax][ay])
9371     return;
9372
9373   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9374     MovDelay[ax][ay] = life_time;
9375
9376   if (MovDelay[ax][ay])         // wait some time before next cycle
9377   {
9378     MovDelay[ax][ay]--;
9379     if (MovDelay[ax][ay])
9380       return;
9381   }
9382
9383   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9384   {
9385     int xx = ax+x1, yy = ay+y1;
9386     int old_element = Tile[xx][yy];
9387     int num_neighbours = 0;
9388
9389     if (!IN_LEV_FIELD(xx, yy))
9390       continue;
9391
9392     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9393     {
9394       int x = xx+x2, y = yy+y2;
9395
9396       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9397         continue;
9398
9399       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9400       boolean is_neighbour = FALSE;
9401
9402       if (level.use_life_bugs)
9403         is_neighbour =
9404           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9405            (IS_FREE(x, y)                             &&  Stop[x][y]));
9406       else
9407         is_neighbour =
9408           (Last[x][y] == element || is_player_cell);
9409
9410       if (is_neighbour)
9411         num_neighbours++;
9412     }
9413
9414     boolean is_free = FALSE;
9415
9416     if (level.use_life_bugs)
9417       is_free = (IS_FREE(xx, yy));
9418     else
9419       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9420
9421     if (xx == ax && yy == ay)           // field in the middle
9422     {
9423       if (num_neighbours < life_parameter[0] ||
9424           num_neighbours > life_parameter[1])
9425       {
9426         Tile[xx][yy] = EL_EMPTY;
9427         if (Tile[xx][yy] != old_element)
9428           TEST_DrawLevelField(xx, yy);
9429         Stop[xx][yy] = TRUE;
9430         changed = TRUE;
9431       }
9432     }
9433     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9434     {                                   // free border field
9435       if (num_neighbours >= life_parameter[2] &&
9436           num_neighbours <= life_parameter[3])
9437       {
9438         Tile[xx][yy] = element;
9439         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9440         if (Tile[xx][yy] != old_element)
9441           TEST_DrawLevelField(xx, yy);
9442         Stop[xx][yy] = TRUE;
9443         changed = TRUE;
9444       }
9445     }
9446   }
9447
9448   if (changed)
9449     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9450                    SND_GAME_OF_LIFE_GROWING);
9451 }
9452
9453 static void InitRobotWheel(int x, int y)
9454 {
9455   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9456 }
9457
9458 static void RunRobotWheel(int x, int y)
9459 {
9460   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9461 }
9462
9463 static void StopRobotWheel(int x, int y)
9464 {
9465   if (game.robot_wheel_x == x &&
9466       game.robot_wheel_y == y)
9467   {
9468     game.robot_wheel_x = -1;
9469     game.robot_wheel_y = -1;
9470     game.robot_wheel_active = FALSE;
9471   }
9472 }
9473
9474 static void InitTimegateWheel(int x, int y)
9475 {
9476   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9477 }
9478
9479 static void RunTimegateWheel(int x, int y)
9480 {
9481   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9482 }
9483
9484 static void InitMagicBallDelay(int x, int y)
9485 {
9486   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9487 }
9488
9489 static void ActivateMagicBall(int bx, int by)
9490 {
9491   int x, y;
9492
9493   if (level.ball_random)
9494   {
9495     int pos_border = RND(8);    // select one of the eight border elements
9496     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9497     int xx = pos_content % 3;
9498     int yy = pos_content / 3;
9499
9500     x = bx - 1 + xx;
9501     y = by - 1 + yy;
9502
9503     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9504       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9505   }
9506   else
9507   {
9508     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9509     {
9510       int xx = x - bx + 1;
9511       int yy = y - by + 1;
9512
9513       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9514         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9515     }
9516   }
9517
9518   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9519 }
9520
9521 static void CheckExit(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitEM(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_EM_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteel(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSteelEM(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0 ||
9599       game.sokoban_fields_still_needed > 0 ||
9600       game.sokoban_objects_still_needed > 0 ||
9601       game.lights_still_needed > 0)
9602   {
9603     int element = Tile[x][y];
9604     int graphic = el2img(element);
9605
9606     if (IS_ANIMATED(graphic))
9607       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9608
9609     return;
9610   }
9611
9612   // do not re-open exit door closed after last player
9613   if (game.all_players_gone)
9614     return;
9615
9616   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9617
9618   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9619 }
9620
9621 static void CheckExitSP(int x, int y)
9622 {
9623   if (game.gems_still_needed > 0)
9624   {
9625     int element = Tile[x][y];
9626     int graphic = el2img(element);
9627
9628     if (IS_ANIMATED(graphic))
9629       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9630
9631     return;
9632   }
9633
9634   // do not re-open exit door closed after last player
9635   if (game.all_players_gone)
9636     return;
9637
9638   Tile[x][y] = EL_SP_EXIT_OPENING;
9639
9640   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9641 }
9642
9643 static void CloseAllOpenTimegates(void)
9644 {
9645   int x, y;
9646
9647   SCAN_PLAYFIELD(x, y)
9648   {
9649     int element = Tile[x][y];
9650
9651     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9652     {
9653       Tile[x][y] = EL_TIMEGATE_CLOSING;
9654
9655       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9656     }
9657   }
9658 }
9659
9660 static void DrawTwinkleOnField(int x, int y)
9661 {
9662   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9663     return;
9664
9665   if (Tile[x][y] == EL_BD_DIAMOND)
9666     return;
9667
9668   if (MovDelay[x][y] == 0)      // next animation frame
9669     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9670
9671   if (MovDelay[x][y] != 0)      // wait some time before next frame
9672   {
9673     MovDelay[x][y]--;
9674
9675     DrawLevelElementAnimation(x, y, Tile[x][y]);
9676
9677     if (MovDelay[x][y] != 0)
9678     {
9679       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9680                                            10 - MovDelay[x][y]);
9681
9682       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9683     }
9684   }
9685 }
9686
9687 static void WallGrowing(int x, int y)
9688 {
9689   int delay = 6;
9690
9691   if (!MovDelay[x][y])          // next animation frame
9692     MovDelay[x][y] = 3 * delay;
9693
9694   if (MovDelay[x][y])           // wait some time before next frame
9695   {
9696     MovDelay[x][y]--;
9697
9698     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9699     {
9700       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9701       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9702
9703       DrawLevelGraphic(x, y, graphic, frame);
9704     }
9705
9706     if (!MovDelay[x][y])
9707     {
9708       if (MovDir[x][y] == MV_LEFT)
9709       {
9710         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9711           TEST_DrawLevelField(x - 1, y);
9712       }
9713       else if (MovDir[x][y] == MV_RIGHT)
9714       {
9715         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9716           TEST_DrawLevelField(x + 1, y);
9717       }
9718       else if (MovDir[x][y] == MV_UP)
9719       {
9720         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9721           TEST_DrawLevelField(x, y - 1);
9722       }
9723       else
9724       {
9725         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9726           TEST_DrawLevelField(x, y + 1);
9727       }
9728
9729       Tile[x][y] = Store[x][y];
9730       Store[x][y] = 0;
9731       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9732       TEST_DrawLevelField(x, y);
9733     }
9734   }
9735 }
9736
9737 static void CheckWallGrowing(int ax, int ay)
9738 {
9739   int element = Tile[ax][ay];
9740   int graphic = el2img(element);
9741   boolean free_top    = FALSE;
9742   boolean free_bottom = FALSE;
9743   boolean free_left   = FALSE;
9744   boolean free_right  = FALSE;
9745   boolean stop_top    = FALSE;
9746   boolean stop_bottom = FALSE;
9747   boolean stop_left   = FALSE;
9748   boolean stop_right  = FALSE;
9749   boolean new_wall    = FALSE;
9750
9751   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9752                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9753                            element == EL_EXPANDABLE_STEELWALL_ANY);
9754
9755   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9756                              element == EL_EXPANDABLE_WALL_ANY ||
9757                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9758                              element == EL_EXPANDABLE_STEELWALL_ANY);
9759
9760   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9761                              element == EL_EXPANDABLE_WALL_ANY ||
9762                              element == EL_EXPANDABLE_WALL ||
9763                              element == EL_BD_EXPANDABLE_WALL ||
9764                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9765                              element == EL_EXPANDABLE_STEELWALL_ANY);
9766
9767   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9768                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9769
9770   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9771                              element == EL_EXPANDABLE_WALL ||
9772                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9773
9774   int wall_growing = (is_steelwall ?
9775                       EL_EXPANDABLE_STEELWALL_GROWING :
9776                       EL_EXPANDABLE_WALL_GROWING);
9777
9778   int gfx_wall_growing_up    = (is_steelwall ?
9779                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9780                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9781   int gfx_wall_growing_down  = (is_steelwall ?
9782                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9783                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9784   int gfx_wall_growing_left  = (is_steelwall ?
9785                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9786                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9787   int gfx_wall_growing_right = (is_steelwall ?
9788                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9789                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9790
9791   if (IS_ANIMATED(graphic))
9792     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9793
9794   if (!MovDelay[ax][ay])        // start building new wall
9795     MovDelay[ax][ay] = 6;
9796
9797   if (MovDelay[ax][ay])         // wait some time before building new wall
9798   {
9799     MovDelay[ax][ay]--;
9800     if (MovDelay[ax][ay])
9801       return;
9802   }
9803
9804   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9805     free_top = TRUE;
9806   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9807     free_bottom = TRUE;
9808   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9809     free_left = TRUE;
9810   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9811     free_right = TRUE;
9812
9813   if (grow_vertical)
9814   {
9815     if (free_top)
9816     {
9817       Tile[ax][ay - 1] = wall_growing;
9818       Store[ax][ay - 1] = element;
9819       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9820
9821       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9822         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9823
9824       new_wall = TRUE;
9825     }
9826
9827     if (free_bottom)
9828     {
9829       Tile[ax][ay + 1] = wall_growing;
9830       Store[ax][ay + 1] = element;
9831       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9832
9833       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9834         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9835
9836       new_wall = TRUE;
9837     }
9838   }
9839
9840   if (grow_horizontal)
9841   {
9842     if (free_left)
9843     {
9844       Tile[ax - 1][ay] = wall_growing;
9845       Store[ax - 1][ay] = element;
9846       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9847
9848       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9849         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9850
9851       new_wall = TRUE;
9852     }
9853
9854     if (free_right)
9855     {
9856       Tile[ax + 1][ay] = wall_growing;
9857       Store[ax + 1][ay] = element;
9858       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9859
9860       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9861         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9862
9863       new_wall = TRUE;
9864     }
9865   }
9866
9867   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9868     TEST_DrawLevelField(ax, ay);
9869
9870   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9871     stop_top = TRUE;
9872   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9873     stop_bottom = TRUE;
9874   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9875     stop_left = TRUE;
9876   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9877     stop_right = TRUE;
9878
9879   if (((stop_top && stop_bottom) || stop_horizontal) &&
9880       ((stop_left && stop_right) || stop_vertical))
9881     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9882
9883   if (new_wall)
9884     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9885 }
9886
9887 static void CheckForDragon(int x, int y)
9888 {
9889   int i, j;
9890   boolean dragon_found = FALSE;
9891   struct XY *xy = xy_topdown;
9892
9893   for (i = 0; i < NUM_DIRECTIONS; i++)
9894   {
9895     for (j = 0; j < 4; j++)
9896     {
9897       int xx = x + j * xy[i].x;
9898       int yy = y + j * xy[i].y;
9899
9900       if (IN_LEV_FIELD(xx, yy) &&
9901           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9902       {
9903         if (Tile[xx][yy] == EL_DRAGON)
9904           dragon_found = TRUE;
9905       }
9906       else
9907         break;
9908     }
9909   }
9910
9911   if (!dragon_found)
9912   {
9913     for (i = 0; i < NUM_DIRECTIONS; i++)
9914     {
9915       for (j = 0; j < 3; j++)
9916       {
9917         int xx = x + j * xy[i].x;
9918         int yy = y + j * xy[i].y;
9919
9920         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9921         {
9922           Tile[xx][yy] = EL_EMPTY;
9923           TEST_DrawLevelField(xx, yy);
9924         }
9925         else
9926           break;
9927       }
9928     }
9929   }
9930 }
9931
9932 static void InitBuggyBase(int x, int y)
9933 {
9934   int element = Tile[x][y];
9935   int activating_delay = FRAMES_PER_SECOND / 4;
9936
9937   ChangeDelay[x][y] =
9938     (element == EL_SP_BUGGY_BASE ?
9939      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9940      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9941      activating_delay :
9942      element == EL_SP_BUGGY_BASE_ACTIVE ?
9943      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9944 }
9945
9946 static void WarnBuggyBase(int x, int y)
9947 {
9948   int i;
9949   struct XY *xy = xy_topdown;
9950
9951   for (i = 0; i < NUM_DIRECTIONS; i++)
9952   {
9953     int xx = x + xy[i].x;
9954     int yy = y + xy[i].y;
9955
9956     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9957     {
9958       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9959
9960       break;
9961     }
9962   }
9963 }
9964
9965 static void InitTrap(int x, int y)
9966 {
9967   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9968 }
9969
9970 static void ActivateTrap(int x, int y)
9971 {
9972   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9973 }
9974
9975 static void ChangeActiveTrap(int x, int y)
9976 {
9977   int graphic = IMG_TRAP_ACTIVE;
9978
9979   // if new animation frame was drawn, correct crumbled sand border
9980   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9981     TEST_DrawLevelFieldCrumbled(x, y);
9982 }
9983
9984 static int getSpecialActionElement(int element, int number, int base_element)
9985 {
9986   return (element != EL_EMPTY ? element :
9987           number != -1 ? base_element + number - 1 :
9988           EL_EMPTY);
9989 }
9990
9991 static int getModifiedActionNumber(int value_old, int operator, int operand,
9992                                    int value_min, int value_max)
9993 {
9994   int value_new = (operator == CA_MODE_SET      ? operand :
9995                    operator == CA_MODE_ADD      ? value_old + operand :
9996                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9997                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9998                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9999                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10000                    value_old);
10001
10002   return (value_new < value_min ? value_min :
10003           value_new > value_max ? value_max :
10004           value_new);
10005 }
10006
10007 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10008 {
10009   struct ElementInfo *ei = &element_info[element];
10010   struct ElementChangeInfo *change = &ei->change_page[page];
10011   int target_element = change->target_element;
10012   int action_type = change->action_type;
10013   int action_mode = change->action_mode;
10014   int action_arg = change->action_arg;
10015   int action_element = change->action_element;
10016   int i;
10017
10018   if (!change->has_action)
10019     return;
10020
10021   // ---------- determine action paramater values -----------------------------
10022
10023   int level_time_value =
10024     (level.time > 0 ? TimeLeft :
10025      TimePlayed);
10026
10027   int action_arg_element_raw =
10028     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10029      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10030      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10031      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10032      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10033      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10034      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10035      EL_EMPTY);
10036   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10037
10038   int action_arg_direction =
10039     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10040      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10041      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10042      change->actual_trigger_side :
10043      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10044      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10045      MV_NONE);
10046
10047   int action_arg_number_min =
10048     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10049      CA_ARG_MIN);
10050
10051   int action_arg_number_max =
10052     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10053      action_type == CA_SET_LEVEL_GEMS ? 999 :
10054      action_type == CA_SET_LEVEL_TIME ? 9999 :
10055      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10056      action_type == CA_SET_CE_VALUE ? 9999 :
10057      action_type == CA_SET_CE_SCORE ? 9999 :
10058      CA_ARG_MAX);
10059
10060   int action_arg_number_reset =
10061     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10062      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10063      action_type == CA_SET_LEVEL_TIME ? level.time :
10064      action_type == CA_SET_LEVEL_SCORE ? 0 :
10065      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10066      action_type == CA_SET_CE_SCORE ? 0 :
10067      0);
10068
10069   int action_arg_number =
10070     (action_arg <= CA_ARG_MAX ? action_arg :
10071      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10072      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10073      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10074      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10075      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10076      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10077      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10078      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10079      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10080      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10081      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10082      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10083      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10084      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10085      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10086      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10087      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10088      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10089      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10090      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10091      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10092      -1);
10093
10094   int action_arg_number_old =
10095     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10096      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10097      action_type == CA_SET_LEVEL_SCORE ? game.score :
10098      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10099      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10100      0);
10101
10102   int action_arg_number_new =
10103     getModifiedActionNumber(action_arg_number_old,
10104                             action_mode, action_arg_number,
10105                             action_arg_number_min, action_arg_number_max);
10106
10107   int trigger_player_bits =
10108     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10109      change->actual_trigger_player_bits : change->trigger_player);
10110
10111   int action_arg_player_bits =
10112     (action_arg >= CA_ARG_PLAYER_1 &&
10113      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10114      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10115      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10116      PLAYER_BITS_ANY);
10117
10118   // ---------- execute action  -----------------------------------------------
10119
10120   switch (action_type)
10121   {
10122     case CA_NO_ACTION:
10123     {
10124       return;
10125     }
10126
10127     // ---------- level actions  ----------------------------------------------
10128
10129     case CA_RESTART_LEVEL:
10130     {
10131       game.restart_level = TRUE;
10132
10133       break;
10134     }
10135
10136     case CA_SHOW_ENVELOPE:
10137     {
10138       int element = getSpecialActionElement(action_arg_element,
10139                                             action_arg_number, EL_ENVELOPE_1);
10140
10141       if (IS_ENVELOPE(element))
10142         local_player->show_envelope = element;
10143
10144       break;
10145     }
10146
10147     case CA_SET_LEVEL_TIME:
10148     {
10149       if (level.time > 0)       // only modify limited time value
10150       {
10151         TimeLeft = action_arg_number_new;
10152
10153         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10154
10155         DisplayGameControlValues();
10156
10157         if (!TimeLeft && game.time_limit)
10158           for (i = 0; i < MAX_PLAYERS; i++)
10159             KillPlayer(&stored_player[i]);
10160       }
10161
10162       break;
10163     }
10164
10165     case CA_SET_LEVEL_SCORE:
10166     {
10167       game.score = action_arg_number_new;
10168
10169       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10170
10171       DisplayGameControlValues();
10172
10173       break;
10174     }
10175
10176     case CA_SET_LEVEL_GEMS:
10177     {
10178       game.gems_still_needed = action_arg_number_new;
10179
10180       game.snapshot.collected_item = TRUE;
10181
10182       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10183
10184       DisplayGameControlValues();
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_WIND:
10190     {
10191       game.wind_direction = action_arg_direction;
10192
10193       break;
10194     }
10195
10196     case CA_SET_LEVEL_RANDOM_SEED:
10197     {
10198       // ensure that setting a new random seed while playing is predictable
10199       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10200
10201       break;
10202     }
10203
10204     // ---------- player actions  ---------------------------------------------
10205
10206     case CA_MOVE_PLAYER:
10207     case CA_MOVE_PLAYER_NEW:
10208     {
10209       // automatically move to the next field in specified direction
10210       for (i = 0; i < MAX_PLAYERS; i++)
10211         if (trigger_player_bits & (1 << i))
10212           if (action_type == CA_MOVE_PLAYER ||
10213               stored_player[i].MovPos == 0)
10214             stored_player[i].programmed_action = action_arg_direction;
10215
10216       break;
10217     }
10218
10219     case CA_EXIT_PLAYER:
10220     {
10221       for (i = 0; i < MAX_PLAYERS; i++)
10222         if (action_arg_player_bits & (1 << i))
10223           ExitPlayer(&stored_player[i]);
10224
10225       if (game.players_still_needed == 0)
10226         LevelSolved();
10227
10228       break;
10229     }
10230
10231     case CA_KILL_PLAYER:
10232     {
10233       for (i = 0; i < MAX_PLAYERS; i++)
10234         if (action_arg_player_bits & (1 << i))
10235           KillPlayer(&stored_player[i]);
10236
10237       break;
10238     }
10239
10240     case CA_SET_PLAYER_KEYS:
10241     {
10242       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10243       int element = getSpecialActionElement(action_arg_element,
10244                                             action_arg_number, EL_KEY_1);
10245
10246       if (IS_KEY(element))
10247       {
10248         for (i = 0; i < MAX_PLAYERS; i++)
10249         {
10250           if (trigger_player_bits & (1 << i))
10251           {
10252             stored_player[i].key[KEY_NR(element)] = key_state;
10253
10254             DrawGameDoorValues();
10255           }
10256         }
10257       }
10258
10259       break;
10260     }
10261
10262     case CA_SET_PLAYER_SPEED:
10263     {
10264       for (i = 0; i < MAX_PLAYERS; i++)
10265       {
10266         if (trigger_player_bits & (1 << i))
10267         {
10268           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10269
10270           if (action_arg == CA_ARG_SPEED_FASTER &&
10271               stored_player[i].cannot_move)
10272           {
10273             action_arg_number = STEPSIZE_VERY_SLOW;
10274           }
10275           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10276                    action_arg == CA_ARG_SPEED_FASTER)
10277           {
10278             action_arg_number = 2;
10279             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10280                            CA_MODE_MULTIPLY);
10281           }
10282           else if (action_arg == CA_ARG_NUMBER_RESET)
10283           {
10284             action_arg_number = level.initial_player_stepsize[i];
10285           }
10286
10287           move_stepsize =
10288             getModifiedActionNumber(move_stepsize,
10289                                     action_mode,
10290                                     action_arg_number,
10291                                     action_arg_number_min,
10292                                     action_arg_number_max);
10293
10294           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10295         }
10296       }
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_SHIELD:
10302     {
10303       for (i = 0; i < MAX_PLAYERS; i++)
10304       {
10305         if (trigger_player_bits & (1 << i))
10306         {
10307           if (action_arg == CA_ARG_SHIELD_OFF)
10308           {
10309             stored_player[i].shield_normal_time_left = 0;
10310             stored_player[i].shield_deadly_time_left = 0;
10311           }
10312           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10313           {
10314             stored_player[i].shield_normal_time_left = 999999;
10315           }
10316           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10317           {
10318             stored_player[i].shield_normal_time_left = 999999;
10319             stored_player[i].shield_deadly_time_left = 999999;
10320           }
10321         }
10322       }
10323
10324       break;
10325     }
10326
10327     case CA_SET_PLAYER_GRAVITY:
10328     {
10329       for (i = 0; i < MAX_PLAYERS; i++)
10330       {
10331         if (trigger_player_bits & (1 << i))
10332         {
10333           stored_player[i].gravity =
10334             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10335              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10336              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10337              stored_player[i].gravity);
10338         }
10339       }
10340
10341       break;
10342     }
10343
10344     case CA_SET_PLAYER_ARTWORK:
10345     {
10346       for (i = 0; i < MAX_PLAYERS; i++)
10347       {
10348         if (trigger_player_bits & (1 << i))
10349         {
10350           int artwork_element = action_arg_element;
10351
10352           if (action_arg == CA_ARG_ELEMENT_RESET)
10353             artwork_element =
10354               (level.use_artwork_element[i] ? level.artwork_element[i] :
10355                stored_player[i].element_nr);
10356
10357           if (stored_player[i].artwork_element != artwork_element)
10358             stored_player[i].Frame = 0;
10359
10360           stored_player[i].artwork_element = artwork_element;
10361
10362           SetPlayerWaiting(&stored_player[i], FALSE);
10363
10364           // set number of special actions for bored and sleeping animation
10365           stored_player[i].num_special_action_bored =
10366             get_num_special_action(artwork_element,
10367                                    ACTION_BORING_1, ACTION_BORING_LAST);
10368           stored_player[i].num_special_action_sleeping =
10369             get_num_special_action(artwork_element,
10370                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10371         }
10372       }
10373
10374       break;
10375     }
10376
10377     case CA_SET_PLAYER_INVENTORY:
10378     {
10379       for (i = 0; i < MAX_PLAYERS; i++)
10380       {
10381         struct PlayerInfo *player = &stored_player[i];
10382         int j, k;
10383
10384         if (trigger_player_bits & (1 << i))
10385         {
10386           int inventory_element = action_arg_element;
10387
10388           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10389               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10390               action_arg == CA_ARG_ELEMENT_ACTION)
10391           {
10392             int element = inventory_element;
10393             int collect_count = element_info[element].collect_count_initial;
10394
10395             if (!IS_CUSTOM_ELEMENT(element))
10396               collect_count = 1;
10397
10398             if (collect_count == 0)
10399               player->inventory_infinite_element = element;
10400             else
10401               for (k = 0; k < collect_count; k++)
10402                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10403                   player->inventory_element[player->inventory_size++] =
10404                     element;
10405           }
10406           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10407                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10408                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10409           {
10410             if (player->inventory_infinite_element != EL_UNDEFINED &&
10411                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10412                                      action_arg_element_raw))
10413               player->inventory_infinite_element = EL_UNDEFINED;
10414
10415             for (k = 0, j = 0; j < player->inventory_size; j++)
10416             {
10417               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10418                                         action_arg_element_raw))
10419                 player->inventory_element[k++] = player->inventory_element[j];
10420             }
10421
10422             player->inventory_size = k;
10423           }
10424           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10425           {
10426             if (player->inventory_size > 0)
10427             {
10428               for (j = 0; j < player->inventory_size - 1; j++)
10429                 player->inventory_element[j] = player->inventory_element[j + 1];
10430
10431               player->inventory_size--;
10432             }
10433           }
10434           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10435           {
10436             if (player->inventory_size > 0)
10437               player->inventory_size--;
10438           }
10439           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10440           {
10441             player->inventory_infinite_element = EL_UNDEFINED;
10442             player->inventory_size = 0;
10443           }
10444           else if (action_arg == CA_ARG_INVENTORY_RESET)
10445           {
10446             player->inventory_infinite_element = EL_UNDEFINED;
10447             player->inventory_size = 0;
10448
10449             if (level.use_initial_inventory[i])
10450             {
10451               for (j = 0; j < level.initial_inventory_size[i]; j++)
10452               {
10453                 int element = level.initial_inventory_content[i][j];
10454                 int collect_count = element_info[element].collect_count_initial;
10455
10456                 if (!IS_CUSTOM_ELEMENT(element))
10457                   collect_count = 1;
10458
10459                 if (collect_count == 0)
10460                   player->inventory_infinite_element = element;
10461                 else
10462                   for (k = 0; k < collect_count; k++)
10463                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10464                       player->inventory_element[player->inventory_size++] =
10465                         element;
10466               }
10467             }
10468           }
10469         }
10470       }
10471
10472       break;
10473     }
10474
10475     // ---------- CE actions  -------------------------------------------------
10476
10477     case CA_SET_CE_VALUE:
10478     {
10479       int last_ce_value = CustomValue[x][y];
10480
10481       CustomValue[x][y] = action_arg_number_new;
10482
10483       if (CustomValue[x][y] != last_ce_value)
10484       {
10485         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10486         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10487
10488         if (CustomValue[x][y] == 0)
10489         {
10490           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10491           ChangeCount[x][y] = 0;        // allow at least one more change
10492
10493           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10494           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10495         }
10496       }
10497
10498       break;
10499     }
10500
10501     case CA_SET_CE_SCORE:
10502     {
10503       int last_ce_score = ei->collect_score;
10504
10505       ei->collect_score = action_arg_number_new;
10506
10507       if (ei->collect_score != last_ce_score)
10508       {
10509         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10510         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10511
10512         if (ei->collect_score == 0)
10513         {
10514           int xx, yy;
10515
10516           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10517           ChangeCount[x][y] = 0;        // allow at least one more change
10518
10519           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10520           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10521
10522           /*
10523             This is a very special case that seems to be a mixture between
10524             CheckElementChange() and CheckTriggeredElementChange(): while
10525             the first one only affects single elements that are triggered
10526             directly, the second one affects multiple elements in the playfield
10527             that are triggered indirectly by another element. This is a third
10528             case: Changing the CE score always affects multiple identical CEs,
10529             so every affected CE must be checked, not only the single CE for
10530             which the CE score was changed in the first place (as every instance
10531             of that CE shares the same CE score, and therefore also can change)!
10532           */
10533           SCAN_PLAYFIELD(xx, yy)
10534           {
10535             if (Tile[xx][yy] == element)
10536               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10537                                  CE_SCORE_GETS_ZERO);
10538           }
10539         }
10540       }
10541
10542       break;
10543     }
10544
10545     case CA_SET_CE_ARTWORK:
10546     {
10547       int artwork_element = action_arg_element;
10548       boolean reset_frame = FALSE;
10549       int xx, yy;
10550
10551       if (action_arg == CA_ARG_ELEMENT_RESET)
10552         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10553                            element);
10554
10555       if (ei->gfx_element != artwork_element)
10556         reset_frame = TRUE;
10557
10558       ei->gfx_element = artwork_element;
10559
10560       SCAN_PLAYFIELD(xx, yy)
10561       {
10562         if (Tile[xx][yy] == element)
10563         {
10564           if (reset_frame)
10565           {
10566             ResetGfxAnimation(xx, yy);
10567             ResetRandomAnimationValue(xx, yy);
10568           }
10569
10570           TEST_DrawLevelField(xx, yy);
10571         }
10572       }
10573
10574       break;
10575     }
10576
10577     // ---------- engine actions  ---------------------------------------------
10578
10579     case CA_SET_ENGINE_SCAN_MODE:
10580     {
10581       InitPlayfieldScanMode(action_arg);
10582
10583       break;
10584     }
10585
10586     default:
10587       break;
10588   }
10589 }
10590
10591 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10592 {
10593   int old_element = Tile[x][y];
10594   int new_element = GetElementFromGroupElement(element);
10595   int previous_move_direction = MovDir[x][y];
10596   int last_ce_value = CustomValue[x][y];
10597   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10598   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10599   boolean add_player_onto_element = (new_element_is_player &&
10600                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10601                                      IS_WALKABLE(old_element));
10602
10603   if (!add_player_onto_element)
10604   {
10605     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10606       RemoveMovingField(x, y);
10607     else
10608       RemoveField(x, y);
10609
10610     Tile[x][y] = new_element;
10611
10612     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10613       MovDir[x][y] = previous_move_direction;
10614
10615     if (element_info[new_element].use_last_ce_value)
10616       CustomValue[x][y] = last_ce_value;
10617
10618     InitField_WithBug1(x, y, FALSE);
10619
10620     new_element = Tile[x][y];   // element may have changed
10621
10622     ResetGfxAnimation(x, y);
10623     ResetRandomAnimationValue(x, y);
10624
10625     TEST_DrawLevelField(x, y);
10626
10627     if (GFX_CRUMBLED(new_element))
10628       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10629   }
10630
10631   // check if element under the player changes from accessible to unaccessible
10632   // (needed for special case of dropping element which then changes)
10633   // (must be checked after creating new element for walkable group elements)
10634   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10635       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10636   {
10637     Bang(x, y);
10638
10639     return;
10640   }
10641
10642   // "ChangeCount" not set yet to allow "entered by player" change one time
10643   if (new_element_is_player)
10644     RelocatePlayer(x, y, new_element);
10645
10646   if (is_change)
10647     ChangeCount[x][y]++;        // count number of changes in the same frame
10648
10649   TestIfBadThingTouchesPlayer(x, y);
10650   TestIfPlayerTouchesCustomElement(x, y);
10651   TestIfElementTouchesCustomElement(x, y);
10652 }
10653
10654 static void CreateField(int x, int y, int element)
10655 {
10656   CreateFieldExt(x, y, element, FALSE);
10657 }
10658
10659 static void CreateElementFromChange(int x, int y, int element)
10660 {
10661   element = GET_VALID_RUNTIME_ELEMENT(element);
10662
10663   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10664   {
10665     int old_element = Tile[x][y];
10666
10667     // prevent changed element from moving in same engine frame
10668     // unless both old and new element can either fall or move
10669     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10670         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10671       Stop[x][y] = TRUE;
10672   }
10673
10674   CreateFieldExt(x, y, element, TRUE);
10675 }
10676
10677 static boolean ChangeElement(int x, int y, int element, int page)
10678 {
10679   struct ElementInfo *ei = &element_info[element];
10680   struct ElementChangeInfo *change = &ei->change_page[page];
10681   int ce_value = CustomValue[x][y];
10682   int ce_score = ei->collect_score;
10683   int target_element;
10684   int old_element = Tile[x][y];
10685
10686   // always use default change event to prevent running into a loop
10687   if (ChangeEvent[x][y] == -1)
10688     ChangeEvent[x][y] = CE_DELAY;
10689
10690   if (ChangeEvent[x][y] == CE_DELAY)
10691   {
10692     // reset actual trigger element, trigger player and action element
10693     change->actual_trigger_element = EL_EMPTY;
10694     change->actual_trigger_player = EL_EMPTY;
10695     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10696     change->actual_trigger_side = CH_SIDE_NONE;
10697     change->actual_trigger_ce_value = 0;
10698     change->actual_trigger_ce_score = 0;
10699   }
10700
10701   // do not change elements more than a specified maximum number of changes
10702   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10703     return FALSE;
10704
10705   ChangeCount[x][y]++;          // count number of changes in the same frame
10706
10707   if (change->explode)
10708   {
10709     Bang(x, y);
10710
10711     return TRUE;
10712   }
10713
10714   if (change->use_target_content)
10715   {
10716     boolean complete_replace = TRUE;
10717     boolean can_replace[3][3];
10718     int xx, yy;
10719
10720     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10721     {
10722       boolean is_empty;
10723       boolean is_walkable;
10724       boolean is_diggable;
10725       boolean is_collectible;
10726       boolean is_removable;
10727       boolean is_destructible;
10728       int ex = x + xx - 1;
10729       int ey = y + yy - 1;
10730       int content_element = change->target_content.e[xx][yy];
10731       int e;
10732
10733       can_replace[xx][yy] = TRUE;
10734
10735       if (ex == x && ey == y)   // do not check changing element itself
10736         continue;
10737
10738       if (content_element == EL_EMPTY_SPACE)
10739       {
10740         can_replace[xx][yy] = FALSE;    // do not replace border with space
10741
10742         continue;
10743       }
10744
10745       if (!IN_LEV_FIELD(ex, ey))
10746       {
10747         can_replace[xx][yy] = FALSE;
10748         complete_replace = FALSE;
10749
10750         continue;
10751       }
10752
10753       e = Tile[ex][ey];
10754
10755       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10756         e = MovingOrBlocked2Element(ex, ey);
10757
10758       is_empty = (IS_FREE(ex, ey) ||
10759                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10760
10761       is_walkable     = (is_empty || IS_WALKABLE(e));
10762       is_diggable     = (is_empty || IS_DIGGABLE(e));
10763       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10764       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10765       is_removable    = (is_diggable || is_collectible);
10766
10767       can_replace[xx][yy] =
10768         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10769           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10770           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10771           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10772           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10773           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10774          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10775
10776       if (!can_replace[xx][yy])
10777         complete_replace = FALSE;
10778     }
10779
10780     if (!change->only_if_complete || complete_replace)
10781     {
10782       boolean something_has_changed = FALSE;
10783
10784       if (change->only_if_complete && change->use_random_replace &&
10785           RND(100) < change->random_percentage)
10786         return FALSE;
10787
10788       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10789       {
10790         int ex = x + xx - 1;
10791         int ey = y + yy - 1;
10792         int content_element;
10793
10794         if (can_replace[xx][yy] && (!change->use_random_replace ||
10795                                     RND(100) < change->random_percentage))
10796         {
10797           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10798             RemoveMovingField(ex, ey);
10799
10800           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10801
10802           content_element = change->target_content.e[xx][yy];
10803           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10804                                               ce_value, ce_score);
10805
10806           CreateElementFromChange(ex, ey, target_element);
10807
10808           something_has_changed = TRUE;
10809
10810           // for symmetry reasons, freeze newly created border elements
10811           if (ex != x || ey != y)
10812             Stop[ex][ey] = TRUE;        // no more moving in this frame
10813         }
10814       }
10815
10816       if (something_has_changed)
10817       {
10818         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10819         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10820       }
10821     }
10822   }
10823   else
10824   {
10825     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10826                                         ce_value, ce_score);
10827
10828     if (element == EL_DIAGONAL_GROWING ||
10829         element == EL_DIAGONAL_SHRINKING)
10830     {
10831       target_element = Store[x][y];
10832
10833       Store[x][y] = EL_EMPTY;
10834     }
10835
10836     // special case: element changes to player (and may be kept if walkable)
10837     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10838       CreateElementFromChange(x, y, EL_EMPTY);
10839
10840     CreateElementFromChange(x, y, target_element);
10841
10842     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10843     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10844   }
10845
10846   // this uses direct change before indirect change
10847   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10848
10849   return TRUE;
10850 }
10851
10852 static void HandleElementChange(int x, int y, int page)
10853 {
10854   int element = MovingOrBlocked2Element(x, y);
10855   struct ElementInfo *ei = &element_info[element];
10856   struct ElementChangeInfo *change = &ei->change_page[page];
10857   boolean handle_action_before_change = FALSE;
10858
10859 #ifdef DEBUG
10860   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10861       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10862   {
10863     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10864           x, y, element, element_info[element].token_name);
10865     Debug("game:playing:HandleElementChange", "This should never happen!");
10866   }
10867 #endif
10868
10869   // this can happen with classic bombs on walkable, changing elements
10870   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10871   {
10872     return;
10873   }
10874
10875   if (ChangeDelay[x][y] == 0)           // initialize element change
10876   {
10877     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10878
10879     if (change->can_change)
10880     {
10881       // !!! not clear why graphic animation should be reset at all here !!!
10882       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10883       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10884
10885       /*
10886         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10887
10888         When using an animation frame delay of 1 (this only happens with
10889         "sp_zonk.moving.left/right" in the classic graphics), the default
10890         (non-moving) animation shows wrong animation frames (while the
10891         moving animation, like "sp_zonk.moving.left/right", is correct,
10892         so this graphical bug never shows up with the classic graphics).
10893         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10894         be drawn instead of the correct frames 0,1,2,3. This is caused by
10895         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10896         an element change: First when the change delay ("ChangeDelay[][]")
10897         counter has reached zero after decrementing, then a second time in
10898         the next frame (after "GfxFrame[][]" was already incremented) when
10899         "ChangeDelay[][]" is reset to the initial delay value again.
10900
10901         This causes frame 0 to be drawn twice, while the last frame won't
10902         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10903
10904         As some animations may already be cleverly designed around this bug
10905         (at least the "Snake Bite" snake tail animation does this), it cannot
10906         simply be fixed here without breaking such existing animations.
10907         Unfortunately, it cannot easily be detected if a graphics set was
10908         designed "before" or "after" the bug was fixed. As a workaround,
10909         a new graphics set option "game.graphics_engine_version" was added
10910         to be able to specify the game's major release version for which the
10911         graphics set was designed, which can then be used to decide if the
10912         bugfix should be used (version 4 and above) or not (version 3 or
10913         below, or if no version was specified at all, as with old sets).
10914
10915         (The wrong/fixed animation frames can be tested with the test level set
10916         "test_gfxframe" and level "000", which contains a specially prepared
10917         custom element at level position (x/y) == (11/9) which uses the zonk
10918         animation mentioned above. Using "game.graphics_engine_version: 4"
10919         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10920         This can also be seen from the debug output for this test element.)
10921       */
10922
10923       // when a custom element is about to change (for example by change delay),
10924       // do not reset graphic animation when the custom element is moving
10925       if (game.graphics_engine_version < 4 &&
10926           !IS_MOVING(x, y))
10927       {
10928         ResetGfxAnimation(x, y);
10929         ResetRandomAnimationValue(x, y);
10930       }
10931
10932       if (change->pre_change_function)
10933         change->pre_change_function(x, y);
10934     }
10935   }
10936
10937   ChangeDelay[x][y]--;
10938
10939   if (ChangeDelay[x][y] != 0)           // continue element change
10940   {
10941     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10942
10943     // also needed if CE can not change, but has CE delay with CE action
10944     if (IS_ANIMATED(graphic))
10945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10946
10947     if (change->can_change)
10948     {
10949       if (change->change_function)
10950         change->change_function(x, y);
10951     }
10952   }
10953   else                                  // finish element change
10954   {
10955     if (ChangePage[x][y] != -1)         // remember page from delayed change
10956     {
10957       page = ChangePage[x][y];
10958       ChangePage[x][y] = -1;
10959
10960       change = &ei->change_page[page];
10961     }
10962
10963     if (IS_MOVING(x, y))                // never change a running system ;-)
10964     {
10965       ChangeDelay[x][y] = 1;            // try change after next move step
10966       ChangePage[x][y] = page;          // remember page to use for change
10967
10968       return;
10969     }
10970
10971     // special case: set new level random seed before changing element
10972     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10973       handle_action_before_change = TRUE;
10974
10975     if (change->has_action && handle_action_before_change)
10976       ExecuteCustomElementAction(x, y, element, page);
10977
10978     if (change->can_change)
10979     {
10980       if (ChangeElement(x, y, element, page))
10981       {
10982         if (change->post_change_function)
10983           change->post_change_function(x, y);
10984       }
10985     }
10986
10987     if (change->has_action && !handle_action_before_change)
10988       ExecuteCustomElementAction(x, y, element, page);
10989   }
10990 }
10991
10992 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10993                                               int trigger_element,
10994                                               int trigger_event,
10995                                               int trigger_player,
10996                                               int trigger_side,
10997                                               int trigger_page)
10998 {
10999   boolean change_done_any = FALSE;
11000   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11001   int i;
11002
11003   if (!(trigger_events[trigger_element][trigger_event]))
11004     return FALSE;
11005
11006   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11007
11008   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11009   {
11010     int element = EL_CUSTOM_START + i;
11011     boolean change_done = FALSE;
11012     int p;
11013
11014     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11015         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11016       continue;
11017
11018     for (p = 0; p < element_info[element].num_change_pages; p++)
11019     {
11020       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11021
11022       if (change->can_change_or_has_action &&
11023           change->has_event[trigger_event] &&
11024           change->trigger_side & trigger_side &&
11025           change->trigger_player & trigger_player &&
11026           change->trigger_page & trigger_page_bits &&
11027           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11028       {
11029         change->actual_trigger_element = trigger_element;
11030         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11031         change->actual_trigger_player_bits = trigger_player;
11032         change->actual_trigger_side = trigger_side;
11033         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11034         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11035
11036         if ((change->can_change && !change_done) || change->has_action)
11037         {
11038           int x, y;
11039
11040           SCAN_PLAYFIELD(x, y)
11041           {
11042             if (Tile[x][y] == element)
11043             {
11044               if (change->can_change && !change_done)
11045               {
11046                 // if element already changed in this frame, not only prevent
11047                 // another element change (checked in ChangeElement()), but
11048                 // also prevent additional element actions for this element
11049
11050                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11051                     !level.use_action_after_change_bug)
11052                   continue;
11053
11054                 ChangeDelay[x][y] = 1;
11055                 ChangeEvent[x][y] = trigger_event;
11056
11057                 HandleElementChange(x, y, p);
11058               }
11059               else if (change->has_action)
11060               {
11061                 // if element already changed in this frame, not only prevent
11062                 // another element change (checked in ChangeElement()), but
11063                 // also prevent additional element actions for this element
11064
11065                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11066                     !level.use_action_after_change_bug)
11067                   continue;
11068
11069                 ExecuteCustomElementAction(x, y, element, p);
11070                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11071               }
11072             }
11073           }
11074
11075           if (change->can_change)
11076           {
11077             change_done = TRUE;
11078             change_done_any = TRUE;
11079           }
11080         }
11081       }
11082     }
11083   }
11084
11085   RECURSION_LOOP_DETECTION_END();
11086
11087   return change_done_any;
11088 }
11089
11090 static boolean CheckElementChangeExt(int x, int y,
11091                                      int element,
11092                                      int trigger_element,
11093                                      int trigger_event,
11094                                      int trigger_player,
11095                                      int trigger_side)
11096 {
11097   boolean change_done = FALSE;
11098   int p;
11099
11100   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11101       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11102     return FALSE;
11103
11104   if (Tile[x][y] == EL_BLOCKED)
11105   {
11106     Blocked2Moving(x, y, &x, &y);
11107     element = Tile[x][y];
11108   }
11109
11110   // check if element has already changed or is about to change after moving
11111   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11112        Tile[x][y] != element) ||
11113
11114       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11115        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11116         ChangePage[x][y] != -1)))
11117     return FALSE;
11118
11119   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11120
11121   for (p = 0; p < element_info[element].num_change_pages; p++)
11122   {
11123     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11124
11125     /* check trigger element for all events where the element that is checked
11126        for changing interacts with a directly adjacent element -- this is
11127        different to element changes that affect other elements to change on the
11128        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11129     boolean check_trigger_element =
11130       (trigger_event == CE_NEXT_TO_X ||
11131        trigger_event == CE_TOUCHING_X ||
11132        trigger_event == CE_HITTING_X ||
11133        trigger_event == CE_HIT_BY_X ||
11134        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11135
11136     if (change->can_change_or_has_action &&
11137         change->has_event[trigger_event] &&
11138         change->trigger_side & trigger_side &&
11139         change->trigger_player & trigger_player &&
11140         (!check_trigger_element ||
11141          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11142     {
11143       change->actual_trigger_element = trigger_element;
11144       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11145       change->actual_trigger_player_bits = trigger_player;
11146       change->actual_trigger_side = trigger_side;
11147       change->actual_trigger_ce_value = CustomValue[x][y];
11148       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11149
11150       // special case: trigger element not at (x,y) position for some events
11151       if (check_trigger_element)
11152       {
11153         static struct
11154         {
11155           int dx, dy;
11156         } move_xy[] =
11157           {
11158             {  0,  0 },
11159             { -1,  0 },
11160             { +1,  0 },
11161             {  0,  0 },
11162             {  0, -1 },
11163             {  0,  0 }, { 0, 0 }, { 0, 0 },
11164             {  0, +1 }
11165           };
11166
11167         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11168         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11169
11170         change->actual_trigger_ce_value = CustomValue[xx][yy];
11171         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11172       }
11173
11174       if (change->can_change && !change_done)
11175       {
11176         ChangeDelay[x][y] = 1;
11177         ChangeEvent[x][y] = trigger_event;
11178
11179         HandleElementChange(x, y, p);
11180
11181         change_done = TRUE;
11182       }
11183       else if (change->has_action)
11184       {
11185         ExecuteCustomElementAction(x, y, element, p);
11186         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11187       }
11188     }
11189   }
11190
11191   RECURSION_LOOP_DETECTION_END();
11192
11193   return change_done;
11194 }
11195
11196 static void PlayPlayerSound(struct PlayerInfo *player)
11197 {
11198   int jx = player->jx, jy = player->jy;
11199   int sound_element = player->artwork_element;
11200   int last_action = player->last_action_waiting;
11201   int action = player->action_waiting;
11202
11203   if (player->is_waiting)
11204   {
11205     if (action != last_action)
11206       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11207     else
11208       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11209   }
11210   else
11211   {
11212     if (action != last_action)
11213       StopSound(element_info[sound_element].sound[last_action]);
11214
11215     if (last_action == ACTION_SLEEPING)
11216       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11217   }
11218 }
11219
11220 static void PlayAllPlayersSound(void)
11221 {
11222   int i;
11223
11224   for (i = 0; i < MAX_PLAYERS; i++)
11225     if (stored_player[i].active)
11226       PlayPlayerSound(&stored_player[i]);
11227 }
11228
11229 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11230 {
11231   boolean last_waiting = player->is_waiting;
11232   int move_dir = player->MovDir;
11233
11234   player->dir_waiting = move_dir;
11235   player->last_action_waiting = player->action_waiting;
11236
11237   if (is_waiting)
11238   {
11239     if (!last_waiting)          // not waiting -> waiting
11240     {
11241       player->is_waiting = TRUE;
11242
11243       player->frame_counter_bored =
11244         FrameCounter +
11245         game.player_boring_delay_fixed +
11246         GetSimpleRandom(game.player_boring_delay_random);
11247       player->frame_counter_sleeping =
11248         FrameCounter +
11249         game.player_sleeping_delay_fixed +
11250         GetSimpleRandom(game.player_sleeping_delay_random);
11251
11252       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11253     }
11254
11255     if (game.player_sleeping_delay_fixed +
11256         game.player_sleeping_delay_random > 0 &&
11257         player->anim_delay_counter == 0 &&
11258         player->post_delay_counter == 0 &&
11259         FrameCounter >= player->frame_counter_sleeping)
11260       player->is_sleeping = TRUE;
11261     else if (game.player_boring_delay_fixed +
11262              game.player_boring_delay_random > 0 &&
11263              FrameCounter >= player->frame_counter_bored)
11264       player->is_bored = TRUE;
11265
11266     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11267                               player->is_bored ? ACTION_BORING :
11268                               ACTION_WAITING);
11269
11270     if (player->is_sleeping && player->use_murphy)
11271     {
11272       // special case for sleeping Murphy when leaning against non-free tile
11273
11274       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11275           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11276            !IS_MOVING(player->jx - 1, player->jy)))
11277         move_dir = MV_LEFT;
11278       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11279                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11280                 !IS_MOVING(player->jx + 1, player->jy)))
11281         move_dir = MV_RIGHT;
11282       else
11283         player->is_sleeping = FALSE;
11284
11285       player->dir_waiting = move_dir;
11286     }
11287
11288     if (player->is_sleeping)
11289     {
11290       if (player->num_special_action_sleeping > 0)
11291       {
11292         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11293         {
11294           int last_special_action = player->special_action_sleeping;
11295           int num_special_action = player->num_special_action_sleeping;
11296           int special_action =
11297             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11298              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11299              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11300              last_special_action + 1 : ACTION_SLEEPING);
11301           int special_graphic =
11302             el_act_dir2img(player->artwork_element, special_action, move_dir);
11303
11304           player->anim_delay_counter =
11305             graphic_info[special_graphic].anim_delay_fixed +
11306             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11307           player->post_delay_counter =
11308             graphic_info[special_graphic].post_delay_fixed +
11309             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11310
11311           player->special_action_sleeping = special_action;
11312         }
11313
11314         if (player->anim_delay_counter > 0)
11315         {
11316           player->action_waiting = player->special_action_sleeping;
11317           player->anim_delay_counter--;
11318         }
11319         else if (player->post_delay_counter > 0)
11320         {
11321           player->post_delay_counter--;
11322         }
11323       }
11324     }
11325     else if (player->is_bored)
11326     {
11327       if (player->num_special_action_bored > 0)
11328       {
11329         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11330         {
11331           int special_action =
11332             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11333           int special_graphic =
11334             el_act_dir2img(player->artwork_element, special_action, move_dir);
11335
11336           player->anim_delay_counter =
11337             graphic_info[special_graphic].anim_delay_fixed +
11338             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11339           player->post_delay_counter =
11340             graphic_info[special_graphic].post_delay_fixed +
11341             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11342
11343           player->special_action_bored = special_action;
11344         }
11345
11346         if (player->anim_delay_counter > 0)
11347         {
11348           player->action_waiting = player->special_action_bored;
11349           player->anim_delay_counter--;
11350         }
11351         else if (player->post_delay_counter > 0)
11352         {
11353           player->post_delay_counter--;
11354         }
11355       }
11356     }
11357   }
11358   else if (last_waiting)        // waiting -> not waiting
11359   {
11360     player->is_waiting = FALSE;
11361     player->is_bored = FALSE;
11362     player->is_sleeping = FALSE;
11363
11364     player->frame_counter_bored = -1;
11365     player->frame_counter_sleeping = -1;
11366
11367     player->anim_delay_counter = 0;
11368     player->post_delay_counter = 0;
11369
11370     player->dir_waiting = player->MovDir;
11371     player->action_waiting = ACTION_DEFAULT;
11372
11373     player->special_action_bored = ACTION_DEFAULT;
11374     player->special_action_sleeping = ACTION_DEFAULT;
11375   }
11376 }
11377
11378 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11379 {
11380   if ((!player->is_moving  && player->was_moving) ||
11381       (player->MovPos == 0 && player->was_moving) ||
11382       (player->is_snapping && !player->was_snapping) ||
11383       (player->is_dropping && !player->was_dropping))
11384   {
11385     if (!CheckSaveEngineSnapshotToList())
11386       return;
11387
11388     player->was_moving = FALSE;
11389     player->was_snapping = TRUE;
11390     player->was_dropping = TRUE;
11391   }
11392   else
11393   {
11394     if (player->is_moving)
11395       player->was_moving = TRUE;
11396
11397     if (!player->is_snapping)
11398       player->was_snapping = FALSE;
11399
11400     if (!player->is_dropping)
11401       player->was_dropping = FALSE;
11402   }
11403
11404   static struct MouseActionInfo mouse_action_last = { 0 };
11405   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11406   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11407
11408   if (new_released)
11409     CheckSaveEngineSnapshotToList();
11410
11411   mouse_action_last = mouse_action;
11412 }
11413
11414 static void CheckSingleStepMode(struct PlayerInfo *player)
11415 {
11416   if (tape.single_step && tape.recording && !tape.pausing)
11417   {
11418     // as it is called "single step mode", just return to pause mode when the
11419     // player stopped moving after one tile (or never starts moving at all)
11420     // (reverse logic needed here in case single step mode used in team mode)
11421     if (player->is_moving ||
11422         player->is_pushing ||
11423         player->is_dropping_pressed ||
11424         player->effective_mouse_action.button)
11425       game.enter_single_step_mode = FALSE;
11426   }
11427
11428   CheckSaveEngineSnapshot(player);
11429 }
11430
11431 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11432 {
11433   int left      = player_action & JOY_LEFT;
11434   int right     = player_action & JOY_RIGHT;
11435   int up        = player_action & JOY_UP;
11436   int down      = player_action & JOY_DOWN;
11437   int button1   = player_action & JOY_BUTTON_1;
11438   int button2   = player_action & JOY_BUTTON_2;
11439   int dx        = (left ? -1 : right ? 1 : 0);
11440   int dy        = (up   ? -1 : down  ? 1 : 0);
11441
11442   if (!player->active || tape.pausing)
11443     return 0;
11444
11445   if (player_action)
11446   {
11447     if (button1)
11448       SnapField(player, dx, dy);
11449     else
11450     {
11451       if (button2)
11452         DropElement(player);
11453
11454       MovePlayer(player, dx, dy);
11455     }
11456
11457     CheckSingleStepMode(player);
11458
11459     SetPlayerWaiting(player, FALSE);
11460
11461     return player_action;
11462   }
11463   else
11464   {
11465     // no actions for this player (no input at player's configured device)
11466
11467     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11468     SnapField(player, 0, 0);
11469     CheckGravityMovementWhenNotMoving(player);
11470
11471     if (player->MovPos == 0)
11472       SetPlayerWaiting(player, TRUE);
11473
11474     if (player->MovPos == 0)    // needed for tape.playing
11475       player->is_moving = FALSE;
11476
11477     player->is_dropping = FALSE;
11478     player->is_dropping_pressed = FALSE;
11479     player->drop_pressed_delay = 0;
11480
11481     CheckSingleStepMode(player);
11482
11483     return 0;
11484   }
11485 }
11486
11487 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11488                                          byte *tape_action)
11489 {
11490   if (!tape.use_mouse_actions)
11491     return;
11492
11493   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11494   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11495   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11496 }
11497
11498 static void SetTapeActionFromMouseAction(byte *tape_action,
11499                                          struct MouseActionInfo *mouse_action)
11500 {
11501   if (!tape.use_mouse_actions)
11502     return;
11503
11504   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11505   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11506   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11507 }
11508
11509 static void CheckLevelSolved(void)
11510 {
11511   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11512   {
11513     if (game_em.level_solved &&
11514         !game_em.game_over)                             // game won
11515     {
11516       LevelSolved();
11517
11518       game_em.game_over = TRUE;
11519
11520       game.all_players_gone = TRUE;
11521     }
11522
11523     if (game_em.game_over)                              // game lost
11524       game.all_players_gone = TRUE;
11525   }
11526   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11527   {
11528     if (game_sp.level_solved &&
11529         !game_sp.game_over)                             // game won
11530     {
11531       LevelSolved();
11532
11533       game_sp.game_over = TRUE;
11534
11535       game.all_players_gone = TRUE;
11536     }
11537
11538     if (game_sp.game_over)                              // game lost
11539       game.all_players_gone = TRUE;
11540   }
11541   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11542   {
11543     if (game_mm.level_solved &&
11544         !game_mm.game_over)                             // game won
11545     {
11546       LevelSolved();
11547
11548       game_mm.game_over = TRUE;
11549
11550       game.all_players_gone = TRUE;
11551     }
11552
11553     if (game_mm.game_over)                              // game lost
11554       game.all_players_gone = TRUE;
11555   }
11556 }
11557
11558 static void CheckLevelTime_StepCounter(void)
11559 {
11560   int i;
11561
11562   TimePlayed++;
11563
11564   if (TimeLeft > 0)
11565   {
11566     TimeLeft--;
11567
11568     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11569       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11570
11571     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11572
11573     DisplayGameControlValues();
11574
11575     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11576       for (i = 0; i < MAX_PLAYERS; i++)
11577         KillPlayer(&stored_player[i]);
11578   }
11579   else if (game.no_level_time_limit && !game.all_players_gone)
11580   {
11581     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11582
11583     DisplayGameControlValues();
11584   }
11585 }
11586
11587 static void CheckLevelTime(void)
11588 {
11589   int i;
11590
11591   if (TimeFrames >= FRAMES_PER_SECOND)
11592   {
11593     TimeFrames = 0;
11594     TapeTime++;
11595
11596     for (i = 0; i < MAX_PLAYERS; i++)
11597     {
11598       struct PlayerInfo *player = &stored_player[i];
11599
11600       if (SHIELD_ON(player))
11601       {
11602         player->shield_normal_time_left--;
11603
11604         if (player->shield_deadly_time_left > 0)
11605           player->shield_deadly_time_left--;
11606       }
11607     }
11608
11609     if (!game.LevelSolved && !level.use_step_counter)
11610     {
11611       TimePlayed++;
11612
11613       if (TimeLeft > 0)
11614       {
11615         TimeLeft--;
11616
11617         if (TimeLeft <= 10 && game.time_limit)
11618           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11619
11620         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11621            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11622
11623         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11624
11625         if (!TimeLeft && game.time_limit)
11626         {
11627           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11628             game_em.lev->killed_out_of_time = TRUE;
11629           else
11630             for (i = 0; i < MAX_PLAYERS; i++)
11631               KillPlayer(&stored_player[i]);
11632         }
11633       }
11634       else if (game.no_level_time_limit && !game.all_players_gone)
11635       {
11636         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11637       }
11638
11639       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11640     }
11641
11642     if (tape.recording || tape.playing)
11643       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11644   }
11645
11646   if (tape.recording || tape.playing)
11647     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11648
11649   UpdateAndDisplayGameControlValues();
11650 }
11651
11652 void AdvanceFrameAndPlayerCounters(int player_nr)
11653 {
11654   int i;
11655
11656   // advance frame counters (global frame counter and time frame counter)
11657   FrameCounter++;
11658   TimeFrames++;
11659
11660   // advance player counters (counters for move delay, move animation etc.)
11661   for (i = 0; i < MAX_PLAYERS; i++)
11662   {
11663     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11664     int move_delay_value = stored_player[i].move_delay_value;
11665     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11666
11667     if (!advance_player_counters)       // not all players may be affected
11668       continue;
11669
11670     if (move_frames == 0)       // less than one move per game frame
11671     {
11672       int stepsize = TILEX / move_delay_value;
11673       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11674       int count = (stored_player[i].is_moving ?
11675                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11676
11677       if (count % delay == 0)
11678         move_frames = 1;
11679     }
11680
11681     stored_player[i].Frame += move_frames;
11682
11683     if (stored_player[i].MovPos != 0)
11684       stored_player[i].StepFrame += move_frames;
11685
11686     if (stored_player[i].move_delay > 0)
11687       stored_player[i].move_delay--;
11688
11689     // due to bugs in previous versions, counter must count up, not down
11690     if (stored_player[i].push_delay != -1)
11691       stored_player[i].push_delay++;
11692
11693     if (stored_player[i].drop_delay > 0)
11694       stored_player[i].drop_delay--;
11695
11696     if (stored_player[i].is_dropping_pressed)
11697       stored_player[i].drop_pressed_delay++;
11698   }
11699 }
11700
11701 void AdvanceFrameCounter(void)
11702 {
11703   FrameCounter++;
11704 }
11705
11706 void AdvanceGfxFrame(void)
11707 {
11708   int x, y;
11709
11710   SCAN_PLAYFIELD(x, y)
11711   {
11712     GfxFrame[x][y]++;
11713   }
11714 }
11715
11716 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11717                               struct MouseActionInfo *mouse_action_last)
11718 {
11719   if (mouse_action->button)
11720   {
11721     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11722     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11723     int x = mouse_action->lx;
11724     int y = mouse_action->ly;
11725     int element = Tile[x][y];
11726
11727     if (new_button)
11728     {
11729       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11730       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11731                                          ch_button);
11732     }
11733
11734     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11735     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11736                                        ch_button);
11737
11738     if (level.use_step_counter)
11739     {
11740       boolean counted_click = FALSE;
11741
11742       // element clicked that can change when clicked/pressed
11743       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11744           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11745            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11746         counted_click = TRUE;
11747
11748       // element clicked that can trigger change when clicked/pressed
11749       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11750           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11751         counted_click = TRUE;
11752
11753       if (new_button && counted_click)
11754         CheckLevelTime_StepCounter();
11755     }
11756   }
11757 }
11758
11759 void StartGameActions(boolean init_network_game, boolean record_tape,
11760                       int random_seed)
11761 {
11762   unsigned int new_random_seed = InitRND(random_seed);
11763
11764   if (record_tape)
11765     TapeStartRecording(new_random_seed);
11766
11767   if (setup.auto_pause_on_start && !tape.pausing)
11768     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11769
11770   if (init_network_game)
11771   {
11772     SendToServer_LevelFile();
11773     SendToServer_StartPlaying();
11774
11775     return;
11776   }
11777
11778   InitGame();
11779 }
11780
11781 static void GameActionsExt(void)
11782 {
11783 #if 0
11784   static unsigned int game_frame_delay = 0;
11785 #endif
11786   unsigned int game_frame_delay_value;
11787   byte *recorded_player_action;
11788   byte summarized_player_action = 0;
11789   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11790   int i;
11791
11792   // detect endless loops, caused by custom element programming
11793   if (recursion_loop_detected && recursion_loop_depth == 0)
11794   {
11795     char *message = getStringCat3("Internal Error! Element ",
11796                                   EL_NAME(recursion_loop_element),
11797                                   " caused endless loop! Quit the game?");
11798
11799     Warn("element '%s' caused endless loop in game engine",
11800          EL_NAME(recursion_loop_element));
11801
11802     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11803
11804     recursion_loop_detected = FALSE;    // if game should be continued
11805
11806     free(message);
11807
11808     return;
11809   }
11810
11811   if (game.restart_level)
11812     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11813
11814   CheckLevelSolved();
11815
11816   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11817     GameWon();
11818
11819   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11820     TapeStop();
11821
11822   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11823     return;
11824
11825   game_frame_delay_value =
11826     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11827
11828   if (tape.playing && tape.warp_forward && !tape.pausing)
11829     game_frame_delay_value = 0;
11830
11831   SetVideoFrameDelay(game_frame_delay_value);
11832
11833   // (de)activate virtual buttons depending on current game status
11834   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11835   {
11836     if (game.all_players_gone)  // if no players there to be controlled anymore
11837       SetOverlayActive(FALSE);
11838     else if (!tape.playing)     // if game continues after tape stopped playing
11839       SetOverlayActive(TRUE);
11840   }
11841
11842 #if 0
11843 #if 0
11844   // ---------- main game synchronization point ----------
11845
11846   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11847
11848   Debug("game:playing:skip", "skip == %d", skip);
11849
11850 #else
11851   // ---------- main game synchronization point ----------
11852
11853   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11854 #endif
11855 #endif
11856
11857   if (network_playing && !network_player_action_received)
11858   {
11859     // try to get network player actions in time
11860
11861     // last chance to get network player actions without main loop delay
11862     HandleNetworking();
11863
11864     // game was quit by network peer
11865     if (game_status != GAME_MODE_PLAYING)
11866       return;
11867
11868     // check if network player actions still missing and game still running
11869     if (!network_player_action_received && !checkGameEnded())
11870       return;           // failed to get network player actions in time
11871
11872     // do not yet reset "network_player_action_received" (for tape.pausing)
11873   }
11874
11875   if (tape.pausing)
11876     return;
11877
11878   // at this point we know that we really continue executing the game
11879
11880   network_player_action_received = FALSE;
11881
11882   // when playing tape, read previously recorded player input from tape data
11883   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11884
11885   local_player->effective_mouse_action = local_player->mouse_action;
11886
11887   if (recorded_player_action != NULL)
11888     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11889                                  recorded_player_action);
11890
11891   // TapePlayAction() may return NULL when toggling to "pause before death"
11892   if (tape.pausing)
11893     return;
11894
11895   if (tape.set_centered_player)
11896   {
11897     game.centered_player_nr_next = tape.centered_player_nr_next;
11898     game.set_centered_player = TRUE;
11899   }
11900
11901   for (i = 0; i < MAX_PLAYERS; i++)
11902   {
11903     summarized_player_action |= stored_player[i].action;
11904
11905     if (!network_playing && (game.team_mode || tape.playing))
11906       stored_player[i].effective_action = stored_player[i].action;
11907   }
11908
11909   if (network_playing && !checkGameEnded())
11910     SendToServer_MovePlayer(summarized_player_action);
11911
11912   // summarize all actions at local players mapped input device position
11913   // (this allows using different input devices in single player mode)
11914   if (!network.enabled && !game.team_mode)
11915     stored_player[map_player_action[local_player->index_nr]].effective_action =
11916       summarized_player_action;
11917
11918   // summarize all actions at centered player in local team mode
11919   if (tape.recording &&
11920       setup.team_mode && !network.enabled &&
11921       setup.input_on_focus &&
11922       game.centered_player_nr != -1)
11923   {
11924     for (i = 0; i < MAX_PLAYERS; i++)
11925       stored_player[map_player_action[i]].effective_action =
11926         (i == game.centered_player_nr ? summarized_player_action : 0);
11927   }
11928
11929   if (recorded_player_action != NULL)
11930     for (i = 0; i < MAX_PLAYERS; i++)
11931       stored_player[i].effective_action = recorded_player_action[i];
11932
11933   for (i = 0; i < MAX_PLAYERS; i++)
11934   {
11935     tape_action[i] = stored_player[i].effective_action;
11936
11937     /* (this may happen in the RND game engine if a player was not present on
11938        the playfield on level start, but appeared later from a custom element */
11939     if (setup.team_mode &&
11940         tape.recording &&
11941         tape_action[i] &&
11942         !tape.player_participates[i])
11943       tape.player_participates[i] = TRUE;
11944   }
11945
11946   SetTapeActionFromMouseAction(tape_action,
11947                                &local_player->effective_mouse_action);
11948
11949   // only record actions from input devices, but not programmed actions
11950   if (tape.recording)
11951     TapeRecordAction(tape_action);
11952
11953   // remember if game was played (especially after tape stopped playing)
11954   if (!tape.playing && summarized_player_action && !checkGameFailed())
11955     game.GamePlayed = TRUE;
11956
11957 #if USE_NEW_PLAYER_ASSIGNMENTS
11958   // !!! also map player actions in single player mode !!!
11959   // if (game.team_mode)
11960   if (1)
11961   {
11962     byte mapped_action[MAX_PLAYERS];
11963
11964 #if DEBUG_PLAYER_ACTIONS
11965     for (i = 0; i < MAX_PLAYERS; i++)
11966       DebugContinued("", "%d, ", stored_player[i].effective_action);
11967 #endif
11968
11969     for (i = 0; i < MAX_PLAYERS; i++)
11970       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11971
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973       stored_player[i].effective_action = mapped_action[i];
11974
11975 #if DEBUG_PLAYER_ACTIONS
11976     DebugContinued("", "=> ");
11977     for (i = 0; i < MAX_PLAYERS; i++)
11978       DebugContinued("", "%d, ", stored_player[i].effective_action);
11979     DebugContinued("game:playing:player", "\n");
11980 #endif
11981   }
11982 #if DEBUG_PLAYER_ACTIONS
11983   else
11984   {
11985     for (i = 0; i < MAX_PLAYERS; i++)
11986       DebugContinued("", "%d, ", stored_player[i].effective_action);
11987     DebugContinued("game:playing:player", "\n");
11988   }
11989 #endif
11990 #endif
11991
11992   for (i = 0; i < MAX_PLAYERS; i++)
11993   {
11994     // allow engine snapshot in case of changed movement attempt
11995     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11996         (stored_player[i].effective_action & KEY_MOTION))
11997       game.snapshot.changed_action = TRUE;
11998
11999     // allow engine snapshot in case of snapping/dropping attempt
12000     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12001         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12002       game.snapshot.changed_action = TRUE;
12003
12004     game.snapshot.last_action[i] = stored_player[i].effective_action;
12005   }
12006
12007   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12008   {
12009     GameActions_EM_Main();
12010   }
12011   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12012   {
12013     GameActions_SP_Main();
12014   }
12015   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12016   {
12017     GameActions_MM_Main();
12018   }
12019   else
12020   {
12021     GameActions_RND_Main();
12022   }
12023
12024   BlitScreenToBitmap(backbuffer);
12025
12026   CheckLevelSolved();
12027   CheckLevelTime();
12028
12029   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12030
12031   if (global.show_frames_per_second)
12032   {
12033     static unsigned int fps_counter = 0;
12034     static int fps_frames = 0;
12035     unsigned int fps_delay_ms = Counter() - fps_counter;
12036
12037     fps_frames++;
12038
12039     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12040     {
12041       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12042
12043       fps_frames = 0;
12044       fps_counter = Counter();
12045
12046       // always draw FPS to screen after FPS value was updated
12047       redraw_mask |= REDRAW_FPS;
12048     }
12049
12050     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12051     if (GetDrawDeactivationMask() == REDRAW_NONE)
12052       redraw_mask |= REDRAW_FPS;
12053   }
12054 }
12055
12056 static void GameActions_CheckSaveEngineSnapshot(void)
12057 {
12058   if (!game.snapshot.save_snapshot)
12059     return;
12060
12061   // clear flag for saving snapshot _before_ saving snapshot
12062   game.snapshot.save_snapshot = FALSE;
12063
12064   SaveEngineSnapshotToList();
12065 }
12066
12067 void GameActions(void)
12068 {
12069   GameActionsExt();
12070
12071   GameActions_CheckSaveEngineSnapshot();
12072 }
12073
12074 void GameActions_EM_Main(void)
12075 {
12076   byte effective_action[MAX_PLAYERS];
12077   int i;
12078
12079   for (i = 0; i < MAX_PLAYERS; i++)
12080     effective_action[i] = stored_player[i].effective_action;
12081
12082   GameActions_EM(effective_action);
12083 }
12084
12085 void GameActions_SP_Main(void)
12086 {
12087   byte effective_action[MAX_PLAYERS];
12088   int i;
12089
12090   for (i = 0; i < MAX_PLAYERS; i++)
12091     effective_action[i] = stored_player[i].effective_action;
12092
12093   GameActions_SP(effective_action);
12094
12095   for (i = 0; i < MAX_PLAYERS; i++)
12096   {
12097     if (stored_player[i].force_dropping)
12098       stored_player[i].action |= KEY_BUTTON_DROP;
12099
12100     stored_player[i].force_dropping = FALSE;
12101   }
12102 }
12103
12104 void GameActions_MM_Main(void)
12105 {
12106   AdvanceGfxFrame();
12107
12108   GameActions_MM(local_player->effective_mouse_action);
12109 }
12110
12111 void GameActions_RND_Main(void)
12112 {
12113   GameActions_RND();
12114 }
12115
12116 void GameActions_RND(void)
12117 {
12118   static struct MouseActionInfo mouse_action_last = { 0 };
12119   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12120   int magic_wall_x = 0, magic_wall_y = 0;
12121   int i, x, y, element, graphic, last_gfx_frame;
12122
12123   InitPlayfieldScanModeVars();
12124
12125   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12126   {
12127     SCAN_PLAYFIELD(x, y)
12128     {
12129       ChangeCount[x][y] = 0;
12130       ChangeEvent[x][y] = -1;
12131     }
12132   }
12133
12134   if (game.set_centered_player)
12135   {
12136     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12137
12138     // switching to "all players" only possible if all players fit to screen
12139     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12140     {
12141       game.centered_player_nr_next = game.centered_player_nr;
12142       game.set_centered_player = FALSE;
12143     }
12144
12145     // do not switch focus to non-existing (or non-active) player
12146     if (game.centered_player_nr_next >= 0 &&
12147         !stored_player[game.centered_player_nr_next].active)
12148     {
12149       game.centered_player_nr_next = game.centered_player_nr;
12150       game.set_centered_player = FALSE;
12151     }
12152   }
12153
12154   if (game.set_centered_player &&
12155       ScreenMovPos == 0)        // screen currently aligned at tile position
12156   {
12157     int sx, sy;
12158
12159     if (game.centered_player_nr_next == -1)
12160     {
12161       setScreenCenteredToAllPlayers(&sx, &sy);
12162     }
12163     else
12164     {
12165       sx = stored_player[game.centered_player_nr_next].jx;
12166       sy = stored_player[game.centered_player_nr_next].jy;
12167     }
12168
12169     game.centered_player_nr = game.centered_player_nr_next;
12170     game.set_centered_player = FALSE;
12171
12172     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12173     DrawGameDoorValues();
12174   }
12175
12176   // check single step mode (set flag and clear again if any player is active)
12177   game.enter_single_step_mode =
12178     (tape.single_step && tape.recording && !tape.pausing);
12179
12180   for (i = 0; i < MAX_PLAYERS; i++)
12181   {
12182     int actual_player_action = stored_player[i].effective_action;
12183
12184 #if 1
12185     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12186        - rnd_equinox_tetrachloride 048
12187        - rnd_equinox_tetrachloride_ii 096
12188        - rnd_emanuel_schmieg 002
12189        - doctor_sloan_ww 001, 020
12190     */
12191     if (stored_player[i].MovPos == 0)
12192       CheckGravityMovement(&stored_player[i]);
12193 #endif
12194
12195     // overwrite programmed action with tape action
12196     if (stored_player[i].programmed_action)
12197       actual_player_action = stored_player[i].programmed_action;
12198
12199     PlayerActions(&stored_player[i], actual_player_action);
12200
12201     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12202   }
12203
12204   // single step pause mode may already have been toggled by "ScrollPlayer()"
12205   if (game.enter_single_step_mode && !tape.pausing)
12206     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12207
12208   ScrollScreen(NULL, SCROLL_GO_ON);
12209
12210   /* for backwards compatibility, the following code emulates a fixed bug that
12211      occured when pushing elements (causing elements that just made their last
12212      pushing step to already (if possible) make their first falling step in the
12213      same game frame, which is bad); this code is also needed to use the famous
12214      "spring push bug" which is used in older levels and might be wanted to be
12215      used also in newer levels, but in this case the buggy pushing code is only
12216      affecting the "spring" element and no other elements */
12217
12218   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12219   {
12220     for (i = 0; i < MAX_PLAYERS; i++)
12221     {
12222       struct PlayerInfo *player = &stored_player[i];
12223       int x = player->jx;
12224       int y = player->jy;
12225
12226       if (player->active && player->is_pushing && player->is_moving &&
12227           IS_MOVING(x, y) &&
12228           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12229            Tile[x][y] == EL_SPRING))
12230       {
12231         ContinueMoving(x, y);
12232
12233         // continue moving after pushing (this is actually a bug)
12234         if (!IS_MOVING(x, y))
12235           Stop[x][y] = FALSE;
12236       }
12237     }
12238   }
12239
12240   SCAN_PLAYFIELD(x, y)
12241   {
12242     Last[x][y] = Tile[x][y];
12243
12244     ChangeCount[x][y] = 0;
12245     ChangeEvent[x][y] = -1;
12246
12247     // this must be handled before main playfield loop
12248     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12249     {
12250       MovDelay[x][y]--;
12251       if (MovDelay[x][y] <= 0)
12252         RemoveField(x, y);
12253     }
12254
12255     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12256     {
12257       MovDelay[x][y]--;
12258       if (MovDelay[x][y] <= 0)
12259       {
12260         int element = Store[x][y];
12261         int move_direction = MovDir[x][y];
12262         int player_index_bit = Store2[x][y];
12263
12264         Store[x][y] = 0;
12265         Store2[x][y] = 0;
12266
12267         RemoveField(x, y);
12268         TEST_DrawLevelField(x, y);
12269
12270         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12271
12272         if (IS_ENVELOPE(element))
12273           local_player->show_envelope = element;
12274       }
12275     }
12276
12277 #if DEBUG
12278     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12279     {
12280       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12281             x, y);
12282       Debug("game:playing:GameActions_RND", "This should never happen!");
12283
12284       ChangePage[x][y] = -1;
12285     }
12286 #endif
12287
12288     Stop[x][y] = FALSE;
12289     if (WasJustMoving[x][y] > 0)
12290       WasJustMoving[x][y]--;
12291     if (WasJustFalling[x][y] > 0)
12292       WasJustFalling[x][y]--;
12293     if (CheckCollision[x][y] > 0)
12294       CheckCollision[x][y]--;
12295     if (CheckImpact[x][y] > 0)
12296       CheckImpact[x][y]--;
12297
12298     GfxFrame[x][y]++;
12299
12300     /* reset finished pushing action (not done in ContinueMoving() to allow
12301        continuous pushing animation for elements with zero push delay) */
12302     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12303     {
12304       ResetGfxAnimation(x, y);
12305       TEST_DrawLevelField(x, y);
12306     }
12307
12308 #if DEBUG
12309     if (IS_BLOCKED(x, y))
12310     {
12311       int oldx, oldy;
12312
12313       Blocked2Moving(x, y, &oldx, &oldy);
12314       if (!IS_MOVING(oldx, oldy))
12315       {
12316         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12317         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12318         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12319         Debug("game:playing:GameActions_RND", "This should never happen!");
12320       }
12321     }
12322 #endif
12323   }
12324
12325   HandleMouseAction(&mouse_action, &mouse_action_last);
12326
12327   SCAN_PLAYFIELD(x, y)
12328   {
12329     element = Tile[x][y];
12330     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12331     last_gfx_frame = GfxFrame[x][y];
12332
12333     if (element == EL_EMPTY)
12334       graphic = el2img(GfxElementEmpty[x][y]);
12335
12336     ResetGfxFrame(x, y);
12337
12338     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12339       DrawLevelGraphicAnimation(x, y, graphic);
12340
12341     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12342         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12343       ResetRandomAnimationValue(x, y);
12344
12345     SetRandomAnimationValue(x, y);
12346
12347     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12348
12349     if (IS_INACTIVE(element))
12350     {
12351       if (IS_ANIMATED(graphic))
12352         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12353
12354       continue;
12355     }
12356
12357     // this may take place after moving, so 'element' may have changed
12358     if (IS_CHANGING(x, y) &&
12359         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12360     {
12361       int page = element_info[element].event_page_nr[CE_DELAY];
12362
12363       HandleElementChange(x, y, page);
12364
12365       element = Tile[x][y];
12366       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12367     }
12368
12369     CheckNextToConditions(x, y);
12370
12371     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12372     {
12373       StartMoving(x, y);
12374
12375       element = Tile[x][y];
12376       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12377
12378       if (IS_ANIMATED(graphic) &&
12379           !IS_MOVING(x, y) &&
12380           !Stop[x][y])
12381         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12382
12383       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12384         TEST_DrawTwinkleOnField(x, y);
12385     }
12386     else if (element == EL_ACID)
12387     {
12388       if (!Stop[x][y])
12389         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12390     }
12391     else if ((element == EL_EXIT_OPEN ||
12392               element == EL_EM_EXIT_OPEN ||
12393               element == EL_SP_EXIT_OPEN ||
12394               element == EL_STEEL_EXIT_OPEN ||
12395               element == EL_EM_STEEL_EXIT_OPEN ||
12396               element == EL_SP_TERMINAL ||
12397               element == EL_SP_TERMINAL_ACTIVE ||
12398               element == EL_EXTRA_TIME ||
12399               element == EL_SHIELD_NORMAL ||
12400               element == EL_SHIELD_DEADLY) &&
12401              IS_ANIMATED(graphic))
12402       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12403     else if (IS_MOVING(x, y))
12404       ContinueMoving(x, y);
12405     else if (IS_ACTIVE_BOMB(element))
12406       CheckDynamite(x, y);
12407     else if (element == EL_AMOEBA_GROWING)
12408       AmoebaGrowing(x, y);
12409     else if (element == EL_AMOEBA_SHRINKING)
12410       AmoebaShrinking(x, y);
12411
12412 #if !USE_NEW_AMOEBA_CODE
12413     else if (IS_AMOEBALIVE(element))
12414       AmoebaReproduce(x, y);
12415 #endif
12416
12417     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12418       Life(x, y);
12419     else if (element == EL_EXIT_CLOSED)
12420       CheckExit(x, y);
12421     else if (element == EL_EM_EXIT_CLOSED)
12422       CheckExitEM(x, y);
12423     else if (element == EL_STEEL_EXIT_CLOSED)
12424       CheckExitSteel(x, y);
12425     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12426       CheckExitSteelEM(x, y);
12427     else if (element == EL_SP_EXIT_CLOSED)
12428       CheckExitSP(x, y);
12429     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12430              element == EL_EXPANDABLE_STEELWALL_GROWING)
12431       WallGrowing(x, y);
12432     else if (element == EL_EXPANDABLE_WALL ||
12433              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12434              element == EL_EXPANDABLE_WALL_VERTICAL ||
12435              element == EL_EXPANDABLE_WALL_ANY ||
12436              element == EL_BD_EXPANDABLE_WALL ||
12437              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12438              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12439              element == EL_EXPANDABLE_STEELWALL_ANY)
12440       CheckWallGrowing(x, y);
12441     else if (element == EL_FLAMES)
12442       CheckForDragon(x, y);
12443     else if (element == EL_EXPLOSION)
12444       ; // drawing of correct explosion animation is handled separately
12445     else if (element == EL_ELEMENT_SNAPPING ||
12446              element == EL_DIAGONAL_SHRINKING ||
12447              element == EL_DIAGONAL_GROWING)
12448     {
12449       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12450
12451       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12452     }
12453     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12454       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12455
12456     if (IS_BELT_ACTIVE(element))
12457       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12458
12459     if (game.magic_wall_active)
12460     {
12461       int jx = local_player->jx, jy = local_player->jy;
12462
12463       // play the element sound at the position nearest to the player
12464       if ((element == EL_MAGIC_WALL_FULL ||
12465            element == EL_MAGIC_WALL_ACTIVE ||
12466            element == EL_MAGIC_WALL_EMPTYING ||
12467            element == EL_BD_MAGIC_WALL_FULL ||
12468            element == EL_BD_MAGIC_WALL_ACTIVE ||
12469            element == EL_BD_MAGIC_WALL_EMPTYING ||
12470            element == EL_DC_MAGIC_WALL_FULL ||
12471            element == EL_DC_MAGIC_WALL_ACTIVE ||
12472            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12473           ABS(x - jx) + ABS(y - jy) <
12474           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12475       {
12476         magic_wall_x = x;
12477         magic_wall_y = y;
12478       }
12479     }
12480   }
12481
12482 #if USE_NEW_AMOEBA_CODE
12483   // new experimental amoeba growth stuff
12484   if (!(FrameCounter % 8))
12485   {
12486     static unsigned int random = 1684108901;
12487
12488     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12489     {
12490       x = RND(lev_fieldx);
12491       y = RND(lev_fieldy);
12492       element = Tile[x][y];
12493
12494       if (!IS_PLAYER(x, y) &&
12495           (element == EL_EMPTY ||
12496            CAN_GROW_INTO(element) ||
12497            element == EL_QUICKSAND_EMPTY ||
12498            element == EL_QUICKSAND_FAST_EMPTY ||
12499            element == EL_ACID_SPLASH_LEFT ||
12500            element == EL_ACID_SPLASH_RIGHT))
12501       {
12502         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12503             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12504             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12505             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12506           Tile[x][y] = EL_AMOEBA_DROP;
12507       }
12508
12509       random = random * 129 + 1;
12510     }
12511   }
12512 #endif
12513
12514   game.explosions_delayed = FALSE;
12515
12516   SCAN_PLAYFIELD(x, y)
12517   {
12518     element = Tile[x][y];
12519
12520     if (ExplodeField[x][y])
12521       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12522     else if (element == EL_EXPLOSION)
12523       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12524
12525     ExplodeField[x][y] = EX_TYPE_NONE;
12526   }
12527
12528   game.explosions_delayed = TRUE;
12529
12530   if (game.magic_wall_active)
12531   {
12532     if (!(game.magic_wall_time_left % 4))
12533     {
12534       int element = Tile[magic_wall_x][magic_wall_y];
12535
12536       if (element == EL_BD_MAGIC_WALL_FULL ||
12537           element == EL_BD_MAGIC_WALL_ACTIVE ||
12538           element == EL_BD_MAGIC_WALL_EMPTYING)
12539         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12540       else if (element == EL_DC_MAGIC_WALL_FULL ||
12541                element == EL_DC_MAGIC_WALL_ACTIVE ||
12542                element == EL_DC_MAGIC_WALL_EMPTYING)
12543         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12544       else
12545         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12546     }
12547
12548     if (game.magic_wall_time_left > 0)
12549     {
12550       game.magic_wall_time_left--;
12551
12552       if (!game.magic_wall_time_left)
12553       {
12554         SCAN_PLAYFIELD(x, y)
12555         {
12556           element = Tile[x][y];
12557
12558           if (element == EL_MAGIC_WALL_ACTIVE ||
12559               element == EL_MAGIC_WALL_FULL)
12560           {
12561             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12562             TEST_DrawLevelField(x, y);
12563           }
12564           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12565                    element == EL_BD_MAGIC_WALL_FULL)
12566           {
12567             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12568             TEST_DrawLevelField(x, y);
12569           }
12570           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12571                    element == EL_DC_MAGIC_WALL_FULL)
12572           {
12573             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12574             TEST_DrawLevelField(x, y);
12575           }
12576         }
12577
12578         game.magic_wall_active = FALSE;
12579       }
12580     }
12581   }
12582
12583   if (game.light_time_left > 0)
12584   {
12585     game.light_time_left--;
12586
12587     if (game.light_time_left == 0)
12588       RedrawAllLightSwitchesAndInvisibleElements();
12589   }
12590
12591   if (game.timegate_time_left > 0)
12592   {
12593     game.timegate_time_left--;
12594
12595     if (game.timegate_time_left == 0)
12596       CloseAllOpenTimegates();
12597   }
12598
12599   if (game.lenses_time_left > 0)
12600   {
12601     game.lenses_time_left--;
12602
12603     if (game.lenses_time_left == 0)
12604       RedrawAllInvisibleElementsForLenses();
12605   }
12606
12607   if (game.magnify_time_left > 0)
12608   {
12609     game.magnify_time_left--;
12610
12611     if (game.magnify_time_left == 0)
12612       RedrawAllInvisibleElementsForMagnifier();
12613   }
12614
12615   for (i = 0; i < MAX_PLAYERS; i++)
12616   {
12617     struct PlayerInfo *player = &stored_player[i];
12618
12619     if (SHIELD_ON(player))
12620     {
12621       if (player->shield_deadly_time_left)
12622         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12623       else if (player->shield_normal_time_left)
12624         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12625     }
12626   }
12627
12628 #if USE_DELAYED_GFX_REDRAW
12629   SCAN_PLAYFIELD(x, y)
12630   {
12631     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12632     {
12633       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12634          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12635
12636       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12637         DrawLevelField(x, y);
12638
12639       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12640         DrawLevelFieldCrumbled(x, y);
12641
12642       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12643         DrawLevelFieldCrumbledNeighbours(x, y);
12644
12645       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12646         DrawTwinkleOnField(x, y);
12647     }
12648
12649     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12650   }
12651 #endif
12652
12653   DrawAllPlayers();
12654   PlayAllPlayersSound();
12655
12656   for (i = 0; i < MAX_PLAYERS; i++)
12657   {
12658     struct PlayerInfo *player = &stored_player[i];
12659
12660     if (player->show_envelope != 0 && (!player->active ||
12661                                        player->MovPos == 0))
12662     {
12663       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12664
12665       player->show_envelope = 0;
12666     }
12667   }
12668
12669   // use random number generator in every frame to make it less predictable
12670   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12671     RND(1);
12672
12673   mouse_action_last = mouse_action;
12674 }
12675
12676 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12677 {
12678   int min_x = x, min_y = y, max_x = x, max_y = y;
12679   int scr_fieldx = getScreenFieldSizeX();
12680   int scr_fieldy = getScreenFieldSizeY();
12681   int i;
12682
12683   for (i = 0; i < MAX_PLAYERS; i++)
12684   {
12685     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12686
12687     if (!stored_player[i].active || &stored_player[i] == player)
12688       continue;
12689
12690     min_x = MIN(min_x, jx);
12691     min_y = MIN(min_y, jy);
12692     max_x = MAX(max_x, jx);
12693     max_y = MAX(max_y, jy);
12694   }
12695
12696   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12697 }
12698
12699 static boolean AllPlayersInVisibleScreen(void)
12700 {
12701   int i;
12702
12703   for (i = 0; i < MAX_PLAYERS; i++)
12704   {
12705     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12706
12707     if (!stored_player[i].active)
12708       continue;
12709
12710     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12711       return FALSE;
12712   }
12713
12714   return TRUE;
12715 }
12716
12717 void ScrollLevel(int dx, int dy)
12718 {
12719   int scroll_offset = 2 * TILEX_VAR;
12720   int x, y;
12721
12722   BlitBitmap(drawto_field, drawto_field,
12723              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12724              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12725              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12726              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12727              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12728              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12729
12730   if (dx != 0)
12731   {
12732     x = (dx == 1 ? BX1 : BX2);
12733     for (y = BY1; y <= BY2; y++)
12734       DrawScreenField(x, y);
12735   }
12736
12737   if (dy != 0)
12738   {
12739     y = (dy == 1 ? BY1 : BY2);
12740     for (x = BX1; x <= BX2; x++)
12741       DrawScreenField(x, y);
12742   }
12743
12744   redraw_mask |= REDRAW_FIELD;
12745 }
12746
12747 static boolean canFallDown(struct PlayerInfo *player)
12748 {
12749   int jx = player->jx, jy = player->jy;
12750
12751   return (IN_LEV_FIELD(jx, jy + 1) &&
12752           (IS_FREE(jx, jy + 1) ||
12753            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12754           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12755           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12756 }
12757
12758 static boolean canPassField(int x, int y, int move_dir)
12759 {
12760   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12761   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12762   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12763   int nextx = x + dx;
12764   int nexty = y + dy;
12765   int element = Tile[x][y];
12766
12767   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12768           !CAN_MOVE(element) &&
12769           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12770           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12771           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12772 }
12773
12774 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12775 {
12776   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12777   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12778   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12779   int newx = x + dx;
12780   int newy = y + dy;
12781
12782   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12783           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12784           (IS_DIGGABLE(Tile[newx][newy]) ||
12785            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12786            canPassField(newx, newy, move_dir)));
12787 }
12788
12789 static void CheckGravityMovement(struct PlayerInfo *player)
12790 {
12791   if (player->gravity && !player->programmed_action)
12792   {
12793     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12794     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12795     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12796     int jx = player->jx, jy = player->jy;
12797     boolean player_is_moving_to_valid_field =
12798       (!player_is_snapping &&
12799        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12800         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12801     boolean player_can_fall_down = canFallDown(player);
12802
12803     if (player_can_fall_down &&
12804         !player_is_moving_to_valid_field)
12805       player->programmed_action = MV_DOWN;
12806   }
12807 }
12808
12809 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12810 {
12811   return CheckGravityMovement(player);
12812
12813   if (player->gravity && !player->programmed_action)
12814   {
12815     int jx = player->jx, jy = player->jy;
12816     boolean field_under_player_is_free =
12817       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12818     boolean player_is_standing_on_valid_field =
12819       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12820        (IS_WALKABLE(Tile[jx][jy]) &&
12821         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12822
12823     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12824       player->programmed_action = MV_DOWN;
12825   }
12826 }
12827
12828 /*
12829   MovePlayerOneStep()
12830   -----------------------------------------------------------------------------
12831   dx, dy:               direction (non-diagonal) to try to move the player to
12832   real_dx, real_dy:     direction as read from input device (can be diagonal)
12833 */
12834
12835 boolean MovePlayerOneStep(struct PlayerInfo *player,
12836                           int dx, int dy, int real_dx, int real_dy)
12837 {
12838   int jx = player->jx, jy = player->jy;
12839   int new_jx = jx + dx, new_jy = jy + dy;
12840   int can_move;
12841   boolean player_can_move = !player->cannot_move;
12842
12843   if (!player->active || (!dx && !dy))
12844     return MP_NO_ACTION;
12845
12846   player->MovDir = (dx < 0 ? MV_LEFT :
12847                     dx > 0 ? MV_RIGHT :
12848                     dy < 0 ? MV_UP :
12849                     dy > 0 ? MV_DOWN :  MV_NONE);
12850
12851   if (!IN_LEV_FIELD(new_jx, new_jy))
12852     return MP_NO_ACTION;
12853
12854   if (!player_can_move)
12855   {
12856     if (player->MovPos == 0)
12857     {
12858       player->is_moving = FALSE;
12859       player->is_digging = FALSE;
12860       player->is_collecting = FALSE;
12861       player->is_snapping = FALSE;
12862       player->is_pushing = FALSE;
12863     }
12864   }
12865
12866   if (!network.enabled && game.centered_player_nr == -1 &&
12867       !AllPlayersInSight(player, new_jx, new_jy))
12868     return MP_NO_ACTION;
12869
12870   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12871   if (can_move != MP_MOVING)
12872     return can_move;
12873
12874   // check if DigField() has caused relocation of the player
12875   if (player->jx != jx || player->jy != jy)
12876     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12877
12878   StorePlayer[jx][jy] = 0;
12879   player->last_jx = jx;
12880   player->last_jy = jy;
12881   player->jx = new_jx;
12882   player->jy = new_jy;
12883   StorePlayer[new_jx][new_jy] = player->element_nr;
12884
12885   if (player->move_delay_value_next != -1)
12886   {
12887     player->move_delay_value = player->move_delay_value_next;
12888     player->move_delay_value_next = -1;
12889   }
12890
12891   player->MovPos =
12892     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12893
12894   player->step_counter++;
12895
12896   PlayerVisit[jx][jy] = FrameCounter;
12897
12898   player->is_moving = TRUE;
12899
12900 #if 1
12901   // should better be called in MovePlayer(), but this breaks some tapes
12902   ScrollPlayer(player, SCROLL_INIT);
12903 #endif
12904
12905   return MP_MOVING;
12906 }
12907
12908 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12909 {
12910   int jx = player->jx, jy = player->jy;
12911   int old_jx = jx, old_jy = jy;
12912   int moved = MP_NO_ACTION;
12913
12914   if (!player->active)
12915     return FALSE;
12916
12917   if (!dx && !dy)
12918   {
12919     if (player->MovPos == 0)
12920     {
12921       player->is_moving = FALSE;
12922       player->is_digging = FALSE;
12923       player->is_collecting = FALSE;
12924       player->is_snapping = FALSE;
12925       player->is_pushing = FALSE;
12926     }
12927
12928     return FALSE;
12929   }
12930
12931   if (player->move_delay > 0)
12932     return FALSE;
12933
12934   player->move_delay = -1;              // set to "uninitialized" value
12935
12936   // store if player is automatically moved to next field
12937   player->is_auto_moving = (player->programmed_action != MV_NONE);
12938
12939   // remove the last programmed player action
12940   player->programmed_action = 0;
12941
12942   if (player->MovPos)
12943   {
12944     // should only happen if pre-1.2 tape recordings are played
12945     // this is only for backward compatibility
12946
12947     int original_move_delay_value = player->move_delay_value;
12948
12949 #if DEBUG
12950     Debug("game:playing:MovePlayer",
12951           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12952           tape.counter);
12953 #endif
12954
12955     // scroll remaining steps with finest movement resolution
12956     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12957
12958     while (player->MovPos)
12959     {
12960       ScrollPlayer(player, SCROLL_GO_ON);
12961       ScrollScreen(NULL, SCROLL_GO_ON);
12962
12963       AdvanceFrameAndPlayerCounters(player->index_nr);
12964
12965       DrawAllPlayers();
12966       BackToFront_WithFrameDelay(0);
12967     }
12968
12969     player->move_delay_value = original_move_delay_value;
12970   }
12971
12972   player->is_active = FALSE;
12973
12974   if (player->last_move_dir & MV_HORIZONTAL)
12975   {
12976     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12977       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12978   }
12979   else
12980   {
12981     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12982       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12983   }
12984
12985   if (!moved && !player->is_active)
12986   {
12987     player->is_moving = FALSE;
12988     player->is_digging = FALSE;
12989     player->is_collecting = FALSE;
12990     player->is_snapping = FALSE;
12991     player->is_pushing = FALSE;
12992   }
12993
12994   jx = player->jx;
12995   jy = player->jy;
12996
12997   if (moved & MP_MOVING && !ScreenMovPos &&
12998       (player->index_nr == game.centered_player_nr ||
12999        game.centered_player_nr == -1))
13000   {
13001     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13002
13003     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13004     {
13005       // actual player has left the screen -- scroll in that direction
13006       if (jx != old_jx)         // player has moved horizontally
13007         scroll_x += (jx - old_jx);
13008       else                      // player has moved vertically
13009         scroll_y += (jy - old_jy);
13010     }
13011     else
13012     {
13013       int offset_raw = game.scroll_delay_value;
13014
13015       if (jx != old_jx)         // player has moved horizontally
13016       {
13017         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13018         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13019         int new_scroll_x = jx - MIDPOSX + offset_x;
13020
13021         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13022             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13023           scroll_x = new_scroll_x;
13024
13025         // don't scroll over playfield boundaries
13026         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13027
13028         // don't scroll more than one field at a time
13029         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13030
13031         // don't scroll against the player's moving direction
13032         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13033             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13034           scroll_x = old_scroll_x;
13035       }
13036       else                      // player has moved vertically
13037       {
13038         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13039         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13040         int new_scroll_y = jy - MIDPOSY + offset_y;
13041
13042         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13043             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13044           scroll_y = new_scroll_y;
13045
13046         // don't scroll over playfield boundaries
13047         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13048
13049         // don't scroll more than one field at a time
13050         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13051
13052         // don't scroll against the player's moving direction
13053         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13054             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13055           scroll_y = old_scroll_y;
13056       }
13057     }
13058
13059     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13060     {
13061       if (!network.enabled && game.centered_player_nr == -1 &&
13062           !AllPlayersInVisibleScreen())
13063       {
13064         scroll_x = old_scroll_x;
13065         scroll_y = old_scroll_y;
13066       }
13067       else
13068       {
13069         ScrollScreen(player, SCROLL_INIT);
13070         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13071       }
13072     }
13073   }
13074
13075   player->StepFrame = 0;
13076
13077   if (moved & MP_MOVING)
13078   {
13079     if (old_jx != jx && old_jy == jy)
13080       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13081     else if (old_jx == jx && old_jy != jy)
13082       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13083
13084     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13085
13086     player->last_move_dir = player->MovDir;
13087     player->is_moving = TRUE;
13088     player->is_snapping = FALSE;
13089     player->is_switching = FALSE;
13090     player->is_dropping = FALSE;
13091     player->is_dropping_pressed = FALSE;
13092     player->drop_pressed_delay = 0;
13093
13094 #if 0
13095     // should better be called here than above, but this breaks some tapes
13096     ScrollPlayer(player, SCROLL_INIT);
13097 #endif
13098   }
13099   else
13100   {
13101     CheckGravityMovementWhenNotMoving(player);
13102
13103     player->is_moving = FALSE;
13104
13105     /* at this point, the player is allowed to move, but cannot move right now
13106        (e.g. because of something blocking the way) -- ensure that the player
13107        is also allowed to move in the next frame (in old versions before 3.1.1,
13108        the player was forced to wait again for eight frames before next try) */
13109
13110     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13111       player->move_delay = 0;   // allow direct movement in the next frame
13112   }
13113
13114   if (player->move_delay == -1)         // not yet initialized by DigField()
13115     player->move_delay = player->move_delay_value;
13116
13117   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13118   {
13119     TestIfPlayerTouchesBadThing(jx, jy);
13120     TestIfPlayerTouchesCustomElement(jx, jy);
13121   }
13122
13123   if (!player->active)
13124     RemovePlayer(player);
13125
13126   return moved;
13127 }
13128
13129 void ScrollPlayer(struct PlayerInfo *player, int mode)
13130 {
13131   int jx = player->jx, jy = player->jy;
13132   int last_jx = player->last_jx, last_jy = player->last_jy;
13133   int move_stepsize = TILEX / player->move_delay_value;
13134
13135   if (!player->active)
13136     return;
13137
13138   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13139     return;
13140
13141   if (mode == SCROLL_INIT)
13142   {
13143     player->actual_frame_counter.count = FrameCounter;
13144     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13145
13146     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13147         Tile[last_jx][last_jy] == EL_EMPTY)
13148     {
13149       int last_field_block_delay = 0;   // start with no blocking at all
13150       int block_delay_adjustment = player->block_delay_adjustment;
13151
13152       // if player blocks last field, add delay for exactly one move
13153       if (player->block_last_field)
13154       {
13155         last_field_block_delay += player->move_delay_value;
13156
13157         // when blocking enabled, prevent moving up despite gravity
13158         if (player->gravity && player->MovDir == MV_UP)
13159           block_delay_adjustment = -1;
13160       }
13161
13162       // add block delay adjustment (also possible when not blocking)
13163       last_field_block_delay += block_delay_adjustment;
13164
13165       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13166       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13167     }
13168
13169     if (player->MovPos != 0)    // player has not yet reached destination
13170       return;
13171   }
13172   else if (!FrameReached(&player->actual_frame_counter))
13173     return;
13174
13175   if (player->MovPos != 0)
13176   {
13177     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13178     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13179
13180     // before DrawPlayer() to draw correct player graphic for this case
13181     if (player->MovPos == 0)
13182       CheckGravityMovement(player);
13183   }
13184
13185   if (player->MovPos == 0)      // player reached destination field
13186   {
13187     if (player->move_delay_reset_counter > 0)
13188     {
13189       player->move_delay_reset_counter--;
13190
13191       if (player->move_delay_reset_counter == 0)
13192       {
13193         // continue with normal speed after quickly moving through gate
13194         HALVE_PLAYER_SPEED(player);
13195
13196         // be able to make the next move without delay
13197         player->move_delay = 0;
13198       }
13199     }
13200
13201     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13202         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13203         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13204         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13205         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13206         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13207         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13208         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13209     {
13210       ExitPlayer(player);
13211
13212       if (game.players_still_needed == 0 &&
13213           (game.friends_still_needed == 0 ||
13214            IS_SP_ELEMENT(Tile[jx][jy])))
13215         LevelSolved();
13216     }
13217
13218     player->last_jx = jx;
13219     player->last_jy = jy;
13220
13221     // this breaks one level: "machine", level 000
13222     {
13223       int move_direction = player->MovDir;
13224       int enter_side = MV_DIR_OPPOSITE(move_direction);
13225       int leave_side = move_direction;
13226       int old_jx = last_jx;
13227       int old_jy = last_jy;
13228       int old_element = Tile[old_jx][old_jy];
13229       int new_element = Tile[jx][jy];
13230
13231       if (IS_CUSTOM_ELEMENT(old_element))
13232         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13233                                    CE_LEFT_BY_PLAYER,
13234                                    player->index_bit, leave_side);
13235
13236       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13237                                           CE_PLAYER_LEAVES_X,
13238                                           player->index_bit, leave_side);
13239
13240       // needed because pushed element has not yet reached its destination,
13241       // so it would trigger a change event at its previous field location
13242       if (!player->is_pushing)
13243       {
13244         if (IS_CUSTOM_ELEMENT(new_element))
13245           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13246                                      player->index_bit, enter_side);
13247
13248         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13249                                             CE_PLAYER_ENTERS_X,
13250                                             player->index_bit, enter_side);
13251       }
13252
13253       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13254                                         CE_MOVE_OF_X, move_direction);
13255     }
13256
13257     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13258     {
13259       TestIfPlayerTouchesBadThing(jx, jy);
13260       TestIfPlayerTouchesCustomElement(jx, jy);
13261
13262       // needed because pushed element has not yet reached its destination,
13263       // so it would trigger a change event at its previous field location
13264       if (!player->is_pushing)
13265         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13266
13267       if (level.finish_dig_collect &&
13268           (player->is_digging || player->is_collecting))
13269       {
13270         int last_element = player->last_removed_element;
13271         int move_direction = player->MovDir;
13272         int enter_side = MV_DIR_OPPOSITE(move_direction);
13273         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13274                             CE_PLAYER_COLLECTS_X);
13275
13276         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13277                                             player->index_bit, enter_side);
13278
13279         player->last_removed_element = EL_UNDEFINED;
13280       }
13281
13282       if (!player->active)
13283         RemovePlayer(player);
13284     }
13285
13286     if (level.use_step_counter)
13287       CheckLevelTime_StepCounter();
13288
13289     if (tape.single_step && tape.recording && !tape.pausing &&
13290         !player->programmed_action)
13291       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13292
13293     if (!player->programmed_action)
13294       CheckSaveEngineSnapshot(player);
13295   }
13296 }
13297
13298 void ScrollScreen(struct PlayerInfo *player, int mode)
13299 {
13300   static DelayCounter screen_frame_counter = { 0 };
13301
13302   if (mode == SCROLL_INIT)
13303   {
13304     // set scrolling step size according to actual player's moving speed
13305     ScrollStepSize = TILEX / player->move_delay_value;
13306
13307     screen_frame_counter.count = FrameCounter;
13308     screen_frame_counter.value = 1;
13309
13310     ScreenMovDir = player->MovDir;
13311     ScreenMovPos = player->MovPos;
13312     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13313     return;
13314   }
13315   else if (!FrameReached(&screen_frame_counter))
13316     return;
13317
13318   if (ScreenMovPos)
13319   {
13320     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13321     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13322     redraw_mask |= REDRAW_FIELD;
13323   }
13324   else
13325     ScreenMovDir = MV_NONE;
13326 }
13327
13328 void CheckNextToConditions(int x, int y)
13329 {
13330   int element = Tile[x][y];
13331
13332   if (IS_PLAYER(x, y))
13333     TestIfPlayerNextToCustomElement(x, y);
13334
13335   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13336       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13337     TestIfElementNextToCustomElement(x, y);
13338 }
13339
13340 void TestIfPlayerNextToCustomElement(int x, int y)
13341 {
13342   struct XY *xy = xy_topdown;
13343   static int trigger_sides[4][2] =
13344   {
13345     // center side       border side
13346     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13347     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13348     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13349     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13350   };
13351   int i;
13352
13353   if (!IS_PLAYER(x, y))
13354     return;
13355
13356   struct PlayerInfo *player = PLAYERINFO(x, y);
13357
13358   if (player->is_moving)
13359     return;
13360
13361   for (i = 0; i < NUM_DIRECTIONS; i++)
13362   {
13363     int xx = x + xy[i].x;
13364     int yy = y + xy[i].y;
13365     int border_side = trigger_sides[i][1];
13366     int border_element;
13367
13368     if (!IN_LEV_FIELD(xx, yy))
13369       continue;
13370
13371     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13372       continue;         // center and border element not connected
13373
13374     border_element = Tile[xx][yy];
13375
13376     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13377                                player->index_bit, border_side);
13378     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13379                                         CE_PLAYER_NEXT_TO_X,
13380                                         player->index_bit, border_side);
13381
13382     /* use player element that is initially defined in the level playfield,
13383        not the player element that corresponds to the runtime player number
13384        (example: a level that contains EL_PLAYER_3 as the only player would
13385        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13386
13387     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13388                              CE_NEXT_TO_X, border_side);
13389   }
13390 }
13391
13392 void TestIfPlayerTouchesCustomElement(int x, int y)
13393 {
13394   struct XY *xy = xy_topdown;
13395   static int trigger_sides[4][2] =
13396   {
13397     // center side       border side
13398     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13399     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13400     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13401     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13402   };
13403   static int touch_dir[4] =
13404   {
13405     MV_LEFT | MV_RIGHT,
13406     MV_UP   | MV_DOWN,
13407     MV_UP   | MV_DOWN,
13408     MV_LEFT | MV_RIGHT
13409   };
13410   int center_element = Tile[x][y];      // should always be non-moving!
13411   int i;
13412
13413   for (i = 0; i < NUM_DIRECTIONS; i++)
13414   {
13415     int xx = x + xy[i].x;
13416     int yy = y + xy[i].y;
13417     int center_side = trigger_sides[i][0];
13418     int border_side = trigger_sides[i][1];
13419     int border_element;
13420
13421     if (!IN_LEV_FIELD(xx, yy))
13422       continue;
13423
13424     if (IS_PLAYER(x, y))                // player found at center element
13425     {
13426       struct PlayerInfo *player = PLAYERINFO(x, y);
13427
13428       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13429         border_element = Tile[xx][yy];          // may be moving!
13430       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13431         border_element = Tile[xx][yy];
13432       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13433         border_element = MovingOrBlocked2Element(xx, yy);
13434       else
13435         continue;               // center and border element do not touch
13436
13437       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13438                                  player->index_bit, border_side);
13439       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13440                                           CE_PLAYER_TOUCHES_X,
13441                                           player->index_bit, border_side);
13442
13443       {
13444         /* use player element that is initially defined in the level playfield,
13445            not the player element that corresponds to the runtime player number
13446            (example: a level that contains EL_PLAYER_3 as the only player would
13447            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13448         int player_element = PLAYERINFO(x, y)->initial_element;
13449
13450         CheckElementChangeBySide(xx, yy, border_element, player_element,
13451                                  CE_TOUCHING_X, border_side);
13452       }
13453     }
13454     else if (IS_PLAYER(xx, yy))         // player found at border element
13455     {
13456       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13457
13458       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13459       {
13460         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13461           continue;             // center and border element do not touch
13462       }
13463
13464       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13465                                  player->index_bit, center_side);
13466       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13467                                           CE_PLAYER_TOUCHES_X,
13468                                           player->index_bit, center_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(xx, yy)->initial_element;
13476
13477         CheckElementChangeBySide(x, y, center_element, player_element,
13478                                  CE_TOUCHING_X, center_side);
13479       }
13480
13481       break;
13482     }
13483   }
13484 }
13485
13486 void TestIfElementNextToCustomElement(int x, int y)
13487 {
13488   struct XY *xy = xy_topdown;
13489   static int trigger_sides[4][2] =
13490   {
13491     // center side      border side
13492     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13493     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13494     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13495     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13496   };
13497   int center_element = Tile[x][y];      // should always be non-moving!
13498   int i;
13499
13500   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13501     return;
13502
13503   for (i = 0; i < NUM_DIRECTIONS; i++)
13504   {
13505     int xx = x + xy[i].x;
13506     int yy = y + xy[i].y;
13507     int border_side = trigger_sides[i][1];
13508     int border_element;
13509
13510     if (!IN_LEV_FIELD(xx, yy))
13511       continue;
13512
13513     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13514       continue;                 // center and border element not connected
13515
13516     border_element = Tile[xx][yy];
13517
13518     // check for change of center element (but change it only once)
13519     if (CheckElementChangeBySide(x, y, center_element, border_element,
13520                                  CE_NEXT_TO_X, border_side))
13521       break;
13522   }
13523 }
13524
13525 void TestIfElementTouchesCustomElement(int x, int y)
13526 {
13527   struct XY *xy = xy_topdown;
13528   static int trigger_sides[4][2] =
13529   {
13530     // center side      border side
13531     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13532     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13533     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13534     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13535   };
13536   static int touch_dir[4] =
13537   {
13538     MV_LEFT | MV_RIGHT,
13539     MV_UP   | MV_DOWN,
13540     MV_UP   | MV_DOWN,
13541     MV_LEFT | MV_RIGHT
13542   };
13543   boolean change_center_element = FALSE;
13544   int center_element = Tile[x][y];      // should always be non-moving!
13545   int border_element_old[NUM_DIRECTIONS];
13546   int i;
13547
13548   for (i = 0; i < NUM_DIRECTIONS; i++)
13549   {
13550     int xx = x + xy[i].x;
13551     int yy = y + xy[i].y;
13552     int border_element;
13553
13554     border_element_old[i] = -1;
13555
13556     if (!IN_LEV_FIELD(xx, yy))
13557       continue;
13558
13559     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13560       border_element = Tile[xx][yy];    // may be moving!
13561     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13562       border_element = Tile[xx][yy];
13563     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13564       border_element = MovingOrBlocked2Element(xx, yy);
13565     else
13566       continue;                 // center and border element do not touch
13567
13568     border_element_old[i] = border_element;
13569   }
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i].x;
13574     int yy = y + xy[i].y;
13575     int center_side = trigger_sides[i][0];
13576     int border_element = border_element_old[i];
13577
13578     if (border_element == -1)
13579       continue;
13580
13581     // check for change of border element
13582     CheckElementChangeBySide(xx, yy, border_element, center_element,
13583                              CE_TOUCHING_X, center_side);
13584
13585     // (center element cannot be player, so we dont have to check this here)
13586   }
13587
13588   for (i = 0; i < NUM_DIRECTIONS; i++)
13589   {
13590     int xx = x + xy[i].x;
13591     int yy = y + xy[i].y;
13592     int border_side = trigger_sides[i][1];
13593     int border_element = border_element_old[i];
13594
13595     if (border_element == -1)
13596       continue;
13597
13598     // check for change of center element (but change it only once)
13599     if (!change_center_element)
13600       change_center_element =
13601         CheckElementChangeBySide(x, y, center_element, border_element,
13602                                  CE_TOUCHING_X, border_side);
13603
13604     if (IS_PLAYER(xx, yy))
13605     {
13606       /* use player element that is initially defined in the level playfield,
13607          not the player element that corresponds to the runtime player number
13608          (example: a level that contains EL_PLAYER_3 as the only player would
13609          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13610       int player_element = PLAYERINFO(xx, yy)->initial_element;
13611
13612       CheckElementChangeBySide(x, y, center_element, player_element,
13613                                CE_TOUCHING_X, border_side);
13614     }
13615   }
13616 }
13617
13618 void TestIfElementHitsCustomElement(int x, int y, int direction)
13619 {
13620   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13621   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13622   int hitx = x + dx, hity = y + dy;
13623   int hitting_element = Tile[x][y];
13624   int touched_element;
13625
13626   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13627     return;
13628
13629   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13630                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13631
13632   if (IN_LEV_FIELD(hitx, hity))
13633   {
13634     int opposite_direction = MV_DIR_OPPOSITE(direction);
13635     int hitting_side = direction;
13636     int touched_side = opposite_direction;
13637     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13638                           MovDir[hitx][hity] != direction ||
13639                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13640
13641     object_hit = TRUE;
13642
13643     if (object_hit)
13644     {
13645       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13646                                CE_HITTING_X, touched_side);
13647
13648       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13649                                CE_HIT_BY_X, hitting_side);
13650
13651       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13652                                CE_HIT_BY_SOMETHING, opposite_direction);
13653
13654       if (IS_PLAYER(hitx, hity))
13655       {
13656         /* use player element that is initially defined in the level playfield,
13657            not the player element that corresponds to the runtime player number
13658            (example: a level that contains EL_PLAYER_3 as the only player would
13659            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13660         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13661
13662         CheckElementChangeBySide(x, y, hitting_element, player_element,
13663                                  CE_HITTING_X, touched_side);
13664       }
13665     }
13666   }
13667
13668   // "hitting something" is also true when hitting the playfield border
13669   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13670                            CE_HITTING_SOMETHING, direction);
13671 }
13672
13673 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13674 {
13675   int i, kill_x = -1, kill_y = -1;
13676
13677   int bad_element = -1;
13678   struct XY *test_xy = xy_topdown;
13679   static int test_dir[4] =
13680   {
13681     MV_UP,
13682     MV_LEFT,
13683     MV_RIGHT,
13684     MV_DOWN
13685   };
13686
13687   for (i = 0; i < NUM_DIRECTIONS; i++)
13688   {
13689     int test_x, test_y, test_move_dir, test_element;
13690
13691     test_x = good_x + test_xy[i].x;
13692     test_y = good_y + test_xy[i].y;
13693
13694     if (!IN_LEV_FIELD(test_x, test_y))
13695       continue;
13696
13697     test_move_dir =
13698       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13699
13700     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13701
13702     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13703        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13704     */
13705     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13706         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13707     {
13708       kill_x = test_x;
13709       kill_y = test_y;
13710       bad_element = test_element;
13711
13712       break;
13713     }
13714   }
13715
13716   if (kill_x != -1 || kill_y != -1)
13717   {
13718     if (IS_PLAYER(good_x, good_y))
13719     {
13720       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13721
13722       if (player->shield_deadly_time_left > 0 &&
13723           !IS_INDESTRUCTIBLE(bad_element))
13724         Bang(kill_x, kill_y);
13725       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13726         KillPlayer(player);
13727     }
13728     else
13729       Bang(good_x, good_y);
13730   }
13731 }
13732
13733 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13734 {
13735   int i, kill_x = -1, kill_y = -1;
13736   int bad_element = Tile[bad_x][bad_y];
13737   struct XY *test_xy = xy_topdown;
13738   static int touch_dir[4] =
13739   {
13740     MV_LEFT | MV_RIGHT,
13741     MV_UP   | MV_DOWN,
13742     MV_UP   | MV_DOWN,
13743     MV_LEFT | MV_RIGHT
13744   };
13745   static int test_dir[4] =
13746   {
13747     MV_UP,
13748     MV_LEFT,
13749     MV_RIGHT,
13750     MV_DOWN
13751   };
13752
13753   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13754     return;
13755
13756   for (i = 0; i < NUM_DIRECTIONS; i++)
13757   {
13758     int test_x, test_y, test_move_dir, test_element;
13759
13760     test_x = bad_x + test_xy[i].x;
13761     test_y = bad_y + test_xy[i].y;
13762
13763     if (!IN_LEV_FIELD(test_x, test_y))
13764       continue;
13765
13766     test_move_dir =
13767       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13768
13769     test_element = Tile[test_x][test_y];
13770
13771     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13772        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13773     */
13774     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13775         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13776     {
13777       // good thing is player or penguin that does not move away
13778       if (IS_PLAYER(test_x, test_y))
13779       {
13780         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13781
13782         if (bad_element == EL_ROBOT && player->is_moving)
13783           continue;     // robot does not kill player if he is moving
13784
13785         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13786         {
13787           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13788             continue;           // center and border element do not touch
13789         }
13790
13791         kill_x = test_x;
13792         kill_y = test_y;
13793
13794         break;
13795       }
13796       else if (test_element == EL_PENGUIN)
13797       {
13798         kill_x = test_x;
13799         kill_y = test_y;
13800
13801         break;
13802       }
13803     }
13804   }
13805
13806   if (kill_x != -1 || kill_y != -1)
13807   {
13808     if (IS_PLAYER(kill_x, kill_y))
13809     {
13810       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13811
13812       if (player->shield_deadly_time_left > 0 &&
13813           !IS_INDESTRUCTIBLE(bad_element))
13814         Bang(bad_x, bad_y);
13815       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13816         KillPlayer(player);
13817     }
13818     else
13819       Bang(kill_x, kill_y);
13820   }
13821 }
13822
13823 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13824 {
13825   int bad_element = Tile[bad_x][bad_y];
13826   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13827   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13828   int test_x = bad_x + dx, test_y = bad_y + dy;
13829   int test_move_dir, test_element;
13830   int kill_x = -1, kill_y = -1;
13831
13832   if (!IN_LEV_FIELD(test_x, test_y))
13833     return;
13834
13835   test_move_dir =
13836     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13837
13838   test_element = Tile[test_x][test_y];
13839
13840   if (test_move_dir != bad_move_dir)
13841   {
13842     // good thing can be player or penguin that does not move away
13843     if (IS_PLAYER(test_x, test_y))
13844     {
13845       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13846
13847       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13848          player as being hit when he is moving towards the bad thing, because
13849          the "get hit by" condition would be lost after the player stops) */
13850       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13851         return;         // player moves away from bad thing
13852
13853       kill_x = test_x;
13854       kill_y = test_y;
13855     }
13856     else if (test_element == EL_PENGUIN)
13857     {
13858       kill_x = test_x;
13859       kill_y = test_y;
13860     }
13861   }
13862
13863   if (kill_x != -1 || kill_y != -1)
13864   {
13865     if (IS_PLAYER(kill_x, kill_y))
13866     {
13867       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13868
13869       if (player->shield_deadly_time_left > 0 &&
13870           !IS_INDESTRUCTIBLE(bad_element))
13871         Bang(bad_x, bad_y);
13872       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13873         KillPlayer(player);
13874     }
13875     else
13876       Bang(kill_x, kill_y);
13877   }
13878 }
13879
13880 void TestIfPlayerTouchesBadThing(int x, int y)
13881 {
13882   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13883 }
13884
13885 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13886 {
13887   TestIfGoodThingHitsBadThing(x, y, move_dir);
13888 }
13889
13890 void TestIfBadThingTouchesPlayer(int x, int y)
13891 {
13892   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13893 }
13894
13895 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13896 {
13897   TestIfBadThingHitsGoodThing(x, y, move_dir);
13898 }
13899
13900 void TestIfFriendTouchesBadThing(int x, int y)
13901 {
13902   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13903 }
13904
13905 void TestIfBadThingTouchesFriend(int x, int y)
13906 {
13907   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13908 }
13909
13910 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13911 {
13912   int i, kill_x = bad_x, kill_y = bad_y;
13913   struct XY *xy = xy_topdown;
13914
13915   for (i = 0; i < NUM_DIRECTIONS; i++)
13916   {
13917     int x, y, element;
13918
13919     x = bad_x + xy[i].x;
13920     y = bad_y + xy[i].y;
13921     if (!IN_LEV_FIELD(x, y))
13922       continue;
13923
13924     element = Tile[x][y];
13925     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13926         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13927     {
13928       kill_x = x;
13929       kill_y = y;
13930       break;
13931     }
13932   }
13933
13934   if (kill_x != bad_x || kill_y != bad_y)
13935     Bang(bad_x, bad_y);
13936 }
13937
13938 void KillPlayer(struct PlayerInfo *player)
13939 {
13940   int jx = player->jx, jy = player->jy;
13941
13942   if (!player->active)
13943     return;
13944
13945 #if 0
13946   Debug("game:playing:KillPlayer",
13947         "0: killed == %d, active == %d, reanimated == %d",
13948         player->killed, player->active, player->reanimated);
13949 #endif
13950
13951   /* the following code was introduced to prevent an infinite loop when calling
13952      -> Bang()
13953      -> CheckTriggeredElementChangeExt()
13954      -> ExecuteCustomElementAction()
13955      -> KillPlayer()
13956      -> (infinitely repeating the above sequence of function calls)
13957      which occurs when killing the player while having a CE with the setting
13958      "kill player X when explosion of <player X>"; the solution using a new
13959      field "player->killed" was chosen for backwards compatibility, although
13960      clever use of the fields "player->active" etc. would probably also work */
13961 #if 1
13962   if (player->killed)
13963     return;
13964 #endif
13965
13966   player->killed = TRUE;
13967
13968   // remove accessible field at the player's position
13969   RemoveField(jx, jy);
13970
13971   // deactivate shield (else Bang()/Explode() would not work right)
13972   player->shield_normal_time_left = 0;
13973   player->shield_deadly_time_left = 0;
13974
13975 #if 0
13976   Debug("game:playing:KillPlayer",
13977         "1: killed == %d, active == %d, reanimated == %d",
13978         player->killed, player->active, player->reanimated);
13979 #endif
13980
13981   Bang(jx, jy);
13982
13983 #if 0
13984   Debug("game:playing:KillPlayer",
13985         "2: killed == %d, active == %d, reanimated == %d",
13986         player->killed, player->active, player->reanimated);
13987 #endif
13988
13989   if (player->reanimated)       // killed player may have been reanimated
13990     player->killed = player->reanimated = FALSE;
13991   else
13992     BuryPlayer(player);
13993 }
13994
13995 static void KillPlayerUnlessEnemyProtected(int x, int y)
13996 {
13997   if (!PLAYER_ENEMY_PROTECTED(x, y))
13998     KillPlayer(PLAYERINFO(x, y));
13999 }
14000
14001 static void KillPlayerUnlessExplosionProtected(int x, int y)
14002 {
14003   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14004     KillPlayer(PLAYERINFO(x, y));
14005 }
14006
14007 void BuryPlayer(struct PlayerInfo *player)
14008 {
14009   int jx = player->jx, jy = player->jy;
14010
14011   if (!player->active)
14012     return;
14013
14014   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14015
14016   RemovePlayer(player);
14017
14018   player->buried = TRUE;
14019
14020   if (game.all_players_gone)
14021     game.GameOver = TRUE;
14022 }
14023
14024 void RemovePlayer(struct PlayerInfo *player)
14025 {
14026   int jx = player->jx, jy = player->jy;
14027   int i, found = FALSE;
14028
14029   player->present = FALSE;
14030   player->active = FALSE;
14031
14032   // required for some CE actions (even if the player is not active anymore)
14033   player->MovPos = 0;
14034
14035   if (!ExplodeField[jx][jy])
14036     StorePlayer[jx][jy] = 0;
14037
14038   if (player->is_moving)
14039     TEST_DrawLevelField(player->last_jx, player->last_jy);
14040
14041   for (i = 0; i < MAX_PLAYERS; i++)
14042     if (stored_player[i].active)
14043       found = TRUE;
14044
14045   if (!found)
14046   {
14047     game.all_players_gone = TRUE;
14048     game.GameOver = TRUE;
14049   }
14050
14051   game.exit_x = game.robot_wheel_x = jx;
14052   game.exit_y = game.robot_wheel_y = jy;
14053 }
14054
14055 void ExitPlayer(struct PlayerInfo *player)
14056 {
14057   DrawPlayer(player);   // needed here only to cleanup last field
14058   RemovePlayer(player);
14059
14060   if (game.players_still_needed > 0)
14061     game.players_still_needed--;
14062 }
14063
14064 static void SetFieldForSnapping(int x, int y, int element, int direction,
14065                                 int player_index_bit)
14066 {
14067   struct ElementInfo *ei = &element_info[element];
14068   int direction_bit = MV_DIR_TO_BIT(direction);
14069   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14070   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14071                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14072
14073   Tile[x][y] = EL_ELEMENT_SNAPPING;
14074   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14075   MovDir[x][y] = direction;
14076   Store[x][y] = element;
14077   Store2[x][y] = player_index_bit;
14078
14079   ResetGfxAnimation(x, y);
14080
14081   GfxElement[x][y] = element;
14082   GfxAction[x][y] = action;
14083   GfxDir[x][y] = direction;
14084   GfxFrame[x][y] = -1;
14085 }
14086
14087 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14088                                    int player_index_bit)
14089 {
14090   TestIfElementTouchesCustomElement(x, y);      // for empty space
14091
14092   if (level.finish_dig_collect)
14093   {
14094     int dig_side = MV_DIR_OPPOSITE(direction);
14095     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14096                         CE_PLAYER_COLLECTS_X);
14097
14098     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14099                                         player_index_bit, dig_side);
14100     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14101                                         player_index_bit, dig_side);
14102   }
14103 }
14104
14105 /*
14106   =============================================================================
14107   checkDiagonalPushing()
14108   -----------------------------------------------------------------------------
14109   check if diagonal input device direction results in pushing of object
14110   (by checking if the alternative direction is walkable, diggable, ...)
14111   =============================================================================
14112 */
14113
14114 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14115                                     int x, int y, int real_dx, int real_dy)
14116 {
14117   int jx, jy, dx, dy, xx, yy;
14118
14119   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14120     return TRUE;
14121
14122   // diagonal direction: check alternative direction
14123   jx = player->jx;
14124   jy = player->jy;
14125   dx = x - jx;
14126   dy = y - jy;
14127   xx = jx + (dx == 0 ? real_dx : 0);
14128   yy = jy + (dy == 0 ? real_dy : 0);
14129
14130   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14131 }
14132
14133 /*
14134   =============================================================================
14135   DigField()
14136   -----------------------------------------------------------------------------
14137   x, y:                 field next to player (non-diagonal) to try to dig to
14138   real_dx, real_dy:     direction as read from input device (can be diagonal)
14139   =============================================================================
14140 */
14141
14142 static int DigField(struct PlayerInfo *player,
14143                     int oldx, int oldy, int x, int y,
14144                     int real_dx, int real_dy, int mode)
14145 {
14146   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14147   boolean player_was_pushing = player->is_pushing;
14148   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14149   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14150   int jx = oldx, jy = oldy;
14151   int dx = x - jx, dy = y - jy;
14152   int nextx = x + dx, nexty = y + dy;
14153   int move_direction = (dx == -1 ? MV_LEFT  :
14154                         dx == +1 ? MV_RIGHT :
14155                         dy == -1 ? MV_UP    :
14156                         dy == +1 ? MV_DOWN  : MV_NONE);
14157   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14158   int dig_side = MV_DIR_OPPOSITE(move_direction);
14159   int old_element = Tile[jx][jy];
14160   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14161   int collect_count;
14162
14163   if (is_player)                // function can also be called by EL_PENGUIN
14164   {
14165     if (player->MovPos == 0)
14166     {
14167       player->is_digging = FALSE;
14168       player->is_collecting = FALSE;
14169     }
14170
14171     if (player->MovPos == 0)    // last pushing move finished
14172       player->is_pushing = FALSE;
14173
14174     if (mode == DF_NO_PUSH)     // player just stopped pushing
14175     {
14176       player->is_switching = FALSE;
14177       player->push_delay = -1;
14178
14179       return MP_NO_ACTION;
14180     }
14181   }
14182   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14183     old_element = Back[jx][jy];
14184
14185   // in case of element dropped at player position, check background
14186   else if (Back[jx][jy] != EL_EMPTY &&
14187            game.engine_version >= VERSION_IDENT(2,2,0,0))
14188     old_element = Back[jx][jy];
14189
14190   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14191     return MP_NO_ACTION;        // field has no opening in this direction
14192
14193   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14194     return MP_NO_ACTION;        // field has no opening in this direction
14195
14196   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14197   {
14198     SplashAcid(x, y);
14199
14200     Tile[jx][jy] = player->artwork_element;
14201     InitMovingField(jx, jy, MV_DOWN);
14202     Store[jx][jy] = EL_ACID;
14203     ContinueMoving(jx, jy);
14204     BuryPlayer(player);
14205
14206     return MP_DONT_RUN_INTO;
14207   }
14208
14209   if (player_can_move && DONT_RUN_INTO(element))
14210   {
14211     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14212
14213     return MP_DONT_RUN_INTO;
14214   }
14215
14216   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14217     return MP_NO_ACTION;
14218
14219   collect_count = element_info[element].collect_count_initial;
14220
14221   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14222     return MP_NO_ACTION;
14223
14224   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14225     player_can_move = player_can_move_or_snap;
14226
14227   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14228       game.engine_version >= VERSION_IDENT(2,2,0,0))
14229   {
14230     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14231                                player->index_bit, dig_side);
14232     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14233                                         player->index_bit, dig_side);
14234
14235     if (element == EL_DC_LANDMINE)
14236       Bang(x, y);
14237
14238     if (Tile[x][y] != element)          // field changed by snapping
14239       return MP_ACTION;
14240
14241     return MP_NO_ACTION;
14242   }
14243
14244   if (player->gravity && is_player && !player->is_auto_moving &&
14245       canFallDown(player) && move_direction != MV_DOWN &&
14246       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14247     return MP_NO_ACTION;        // player cannot walk here due to gravity
14248
14249   if (player_can_move &&
14250       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14251   {
14252     int sound_element = SND_ELEMENT(element);
14253     int sound_action = ACTION_WALKING;
14254
14255     if (IS_RND_GATE(element))
14256     {
14257       if (!player->key[RND_GATE_NR(element)])
14258         return MP_NO_ACTION;
14259     }
14260     else if (IS_RND_GATE_GRAY(element))
14261     {
14262       if (!player->key[RND_GATE_GRAY_NR(element)])
14263         return MP_NO_ACTION;
14264     }
14265     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14266     {
14267       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14268         return MP_NO_ACTION;
14269     }
14270     else if (element == EL_EXIT_OPEN ||
14271              element == EL_EM_EXIT_OPEN ||
14272              element == EL_EM_EXIT_OPENING ||
14273              element == EL_STEEL_EXIT_OPEN ||
14274              element == EL_EM_STEEL_EXIT_OPEN ||
14275              element == EL_EM_STEEL_EXIT_OPENING ||
14276              element == EL_SP_EXIT_OPEN ||
14277              element == EL_SP_EXIT_OPENING)
14278     {
14279       sound_action = ACTION_PASSING;    // player is passing exit
14280     }
14281     else if (element == EL_EMPTY)
14282     {
14283       sound_action = ACTION_MOVING;             // nothing to walk on
14284     }
14285
14286     // play sound from background or player, whatever is available
14287     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14288       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14289     else
14290       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14291   }
14292   else if (player_can_move &&
14293            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14294   {
14295     if (!ACCESS_FROM(element, opposite_direction))
14296       return MP_NO_ACTION;      // field not accessible from this direction
14297
14298     if (CAN_MOVE(element))      // only fixed elements can be passed!
14299       return MP_NO_ACTION;
14300
14301     if (IS_EM_GATE(element))
14302     {
14303       if (!player->key[EM_GATE_NR(element)])
14304         return MP_NO_ACTION;
14305     }
14306     else if (IS_EM_GATE_GRAY(element))
14307     {
14308       if (!player->key[EM_GATE_GRAY_NR(element)])
14309         return MP_NO_ACTION;
14310     }
14311     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14312     {
14313       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14314         return MP_NO_ACTION;
14315     }
14316     else if (IS_EMC_GATE(element))
14317     {
14318       if (!player->key[EMC_GATE_NR(element)])
14319         return MP_NO_ACTION;
14320     }
14321     else if (IS_EMC_GATE_GRAY(element))
14322     {
14323       if (!player->key[EMC_GATE_GRAY_NR(element)])
14324         return MP_NO_ACTION;
14325     }
14326     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14327     {
14328       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14329         return MP_NO_ACTION;
14330     }
14331     else if (element == EL_DC_GATE_WHITE ||
14332              element == EL_DC_GATE_WHITE_GRAY ||
14333              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14334     {
14335       if (player->num_white_keys == 0)
14336         return MP_NO_ACTION;
14337
14338       player->num_white_keys--;
14339     }
14340     else if (IS_SP_PORT(element))
14341     {
14342       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14343           element == EL_SP_GRAVITY_PORT_RIGHT ||
14344           element == EL_SP_GRAVITY_PORT_UP ||
14345           element == EL_SP_GRAVITY_PORT_DOWN)
14346         player->gravity = !player->gravity;
14347       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14348                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14349                element == EL_SP_GRAVITY_ON_PORT_UP ||
14350                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14351         player->gravity = TRUE;
14352       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14353                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14354                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14355                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14356         player->gravity = FALSE;
14357     }
14358
14359     // automatically move to the next field with double speed
14360     player->programmed_action = move_direction;
14361
14362     if (player->move_delay_reset_counter == 0)
14363     {
14364       player->move_delay_reset_counter = 2;     // two double speed steps
14365
14366       DOUBLE_PLAYER_SPEED(player);
14367     }
14368
14369     PlayLevelSoundAction(x, y, ACTION_PASSING);
14370   }
14371   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14372   {
14373     RemoveField(x, y);
14374
14375     if (mode != DF_SNAP)
14376     {
14377       GfxElement[x][y] = GFX_ELEMENT(element);
14378       player->is_digging = TRUE;
14379     }
14380
14381     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14382
14383     // use old behaviour for old levels (digging)
14384     if (!level.finish_dig_collect)
14385     {
14386       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14387                                           player->index_bit, dig_side);
14388
14389       // if digging triggered player relocation, finish digging tile
14390       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14391         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14392     }
14393
14394     if (mode == DF_SNAP)
14395     {
14396       if (level.block_snap_field)
14397         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14398       else
14399         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14400
14401       // use old behaviour for old levels (snapping)
14402       if (!level.finish_dig_collect)
14403         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14404                                             player->index_bit, dig_side);
14405     }
14406   }
14407   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14408   {
14409     RemoveField(x, y);
14410
14411     if (is_player && mode != DF_SNAP)
14412     {
14413       GfxElement[x][y] = element;
14414       player->is_collecting = TRUE;
14415     }
14416
14417     if (element == EL_SPEED_PILL)
14418     {
14419       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14420     }
14421     else if (element == EL_EXTRA_TIME && level.time > 0)
14422     {
14423       TimeLeft += level.extra_time;
14424
14425       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14426
14427       DisplayGameControlValues();
14428     }
14429     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14430     {
14431       int shield_time = (element == EL_SHIELD_DEADLY ?
14432                          level.shield_deadly_time :
14433                          level.shield_normal_time);
14434
14435       player->shield_normal_time_left += shield_time;
14436       if (element == EL_SHIELD_DEADLY)
14437         player->shield_deadly_time_left += shield_time;
14438     }
14439     else if (element == EL_DYNAMITE ||
14440              element == EL_EM_DYNAMITE ||
14441              element == EL_SP_DISK_RED)
14442     {
14443       if (player->inventory_size < MAX_INVENTORY_SIZE)
14444         player->inventory_element[player->inventory_size++] = element;
14445
14446       DrawGameDoorValues();
14447     }
14448     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14449     {
14450       player->dynabomb_count++;
14451       player->dynabombs_left++;
14452     }
14453     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14454     {
14455       player->dynabomb_size++;
14456     }
14457     else if (element == EL_DYNABOMB_INCREASE_POWER)
14458     {
14459       player->dynabomb_xl = TRUE;
14460     }
14461     else if (IS_KEY(element))
14462     {
14463       player->key[KEY_NR(element)] = TRUE;
14464
14465       DrawGameDoorValues();
14466     }
14467     else if (element == EL_DC_KEY_WHITE)
14468     {
14469       player->num_white_keys++;
14470
14471       // display white keys?
14472       // DrawGameDoorValues();
14473     }
14474     else if (IS_ENVELOPE(element))
14475     {
14476       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14477
14478       if (!wait_for_snapping)
14479         player->show_envelope = element;
14480     }
14481     else if (element == EL_EMC_LENSES)
14482     {
14483       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14484
14485       RedrawAllInvisibleElementsForLenses();
14486     }
14487     else if (element == EL_EMC_MAGNIFIER)
14488     {
14489       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14490
14491       RedrawAllInvisibleElementsForMagnifier();
14492     }
14493     else if (IS_DROPPABLE(element) ||
14494              IS_THROWABLE(element))     // can be collected and dropped
14495     {
14496       int i;
14497
14498       if (collect_count == 0)
14499         player->inventory_infinite_element = element;
14500       else
14501         for (i = 0; i < collect_count; i++)
14502           if (player->inventory_size < MAX_INVENTORY_SIZE)
14503             player->inventory_element[player->inventory_size++] = element;
14504
14505       DrawGameDoorValues();
14506     }
14507     else if (collect_count > 0)
14508     {
14509       game.gems_still_needed -= collect_count;
14510       if (game.gems_still_needed < 0)
14511         game.gems_still_needed = 0;
14512
14513       game.snapshot.collected_item = TRUE;
14514
14515       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14516
14517       DisplayGameControlValues();
14518     }
14519
14520     RaiseScoreElement(element);
14521     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14522
14523     // use old behaviour for old levels (collecting)
14524     if (!level.finish_dig_collect && is_player)
14525     {
14526       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14527                                           player->index_bit, dig_side);
14528
14529       // if collecting triggered player relocation, finish collecting tile
14530       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14531         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14532     }
14533
14534     if (mode == DF_SNAP)
14535     {
14536       if (level.block_snap_field)
14537         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14538       else
14539         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14540
14541       // use old behaviour for old levels (snapping)
14542       if (!level.finish_dig_collect)
14543         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14544                                             player->index_bit, dig_side);
14545     }
14546   }
14547   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14548   {
14549     if (mode == DF_SNAP && element != EL_BD_ROCK)
14550       return MP_NO_ACTION;
14551
14552     if (CAN_FALL(element) && dy)
14553       return MP_NO_ACTION;
14554
14555     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14556         !(element == EL_SPRING && level.use_spring_bug))
14557       return MP_NO_ACTION;
14558
14559     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14560         ((move_direction & MV_VERTICAL &&
14561           ((element_info[element].move_pattern & MV_LEFT &&
14562             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14563            (element_info[element].move_pattern & MV_RIGHT &&
14564             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14565          (move_direction & MV_HORIZONTAL &&
14566           ((element_info[element].move_pattern & MV_UP &&
14567             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14568            (element_info[element].move_pattern & MV_DOWN &&
14569             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14570       return MP_NO_ACTION;
14571
14572     // do not push elements already moving away faster than player
14573     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14574         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14575       return MP_NO_ACTION;
14576
14577     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14578     {
14579       if (player->push_delay_value == -1 || !player_was_pushing)
14580         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14581     }
14582     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14583     {
14584       if (player->push_delay_value == -1)
14585         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14586     }
14587     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14588     {
14589       if (!player->is_pushing)
14590         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14591     }
14592
14593     player->is_pushing = TRUE;
14594     player->is_active = TRUE;
14595
14596     if (!(IN_LEV_FIELD(nextx, nexty) &&
14597           (IS_FREE(nextx, nexty) ||
14598            (IS_SB_ELEMENT(element) &&
14599             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14600            (IS_CUSTOM_ELEMENT(element) &&
14601             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14602       return MP_NO_ACTION;
14603
14604     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14605       return MP_NO_ACTION;
14606
14607     if (player->push_delay == -1)       // new pushing; restart delay
14608       player->push_delay = 0;
14609
14610     if (player->push_delay < player->push_delay_value &&
14611         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14612         element != EL_SPRING && element != EL_BALLOON)
14613     {
14614       // make sure that there is no move delay before next try to push
14615       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14616         player->move_delay = 0;
14617
14618       return MP_NO_ACTION;
14619     }
14620
14621     if (IS_CUSTOM_ELEMENT(element) &&
14622         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14623     {
14624       if (!DigFieldByCE(nextx, nexty, element))
14625         return MP_NO_ACTION;
14626     }
14627
14628     if (IS_SB_ELEMENT(element))
14629     {
14630       boolean sokoban_task_solved = FALSE;
14631
14632       if (element == EL_SOKOBAN_FIELD_FULL)
14633       {
14634         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14635
14636         IncrementSokobanFieldsNeeded();
14637         IncrementSokobanObjectsNeeded();
14638       }
14639
14640       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14641       {
14642         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14643
14644         DecrementSokobanFieldsNeeded();
14645         DecrementSokobanObjectsNeeded();
14646
14647         // sokoban object was pushed from empty field to sokoban field
14648         if (Back[x][y] == EL_EMPTY)
14649           sokoban_task_solved = TRUE;
14650       }
14651
14652       Tile[x][y] = EL_SOKOBAN_OBJECT;
14653
14654       if (Back[x][y] == Back[nextx][nexty])
14655         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14656       else if (Back[x][y] != 0)
14657         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14658                                     ACTION_EMPTYING);
14659       else
14660         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14661                                     ACTION_FILLING);
14662
14663       if (sokoban_task_solved &&
14664           game.sokoban_fields_still_needed == 0 &&
14665           game.sokoban_objects_still_needed == 0 &&
14666           level.auto_exit_sokoban)
14667       {
14668         game.players_still_needed = 0;
14669
14670         LevelSolved();
14671
14672         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14673       }
14674     }
14675     else
14676       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14677
14678     InitMovingField(x, y, move_direction);
14679     GfxAction[x][y] = ACTION_PUSHING;
14680
14681     if (mode == DF_SNAP)
14682       ContinueMoving(x, y);
14683     else
14684       MovPos[x][y] = (dx != 0 ? dx : dy);
14685
14686     Pushed[x][y] = TRUE;
14687     Pushed[nextx][nexty] = TRUE;
14688
14689     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14690       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14691     else
14692       player->push_delay_value = -1;    // get new value later
14693
14694     // check for element change _after_ element has been pushed
14695     if (game.use_change_when_pushing_bug)
14696     {
14697       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14698                                  player->index_bit, dig_side);
14699       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14700                                           player->index_bit, dig_side);
14701     }
14702   }
14703   else if (IS_SWITCHABLE(element))
14704   {
14705     if (PLAYER_SWITCHING(player, x, y))
14706     {
14707       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14708                                           player->index_bit, dig_side);
14709
14710       return MP_ACTION;
14711     }
14712
14713     player->is_switching = TRUE;
14714     player->switch_x = x;
14715     player->switch_y = y;
14716
14717     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14718
14719     if (element == EL_ROBOT_WHEEL)
14720     {
14721       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14722
14723       game.robot_wheel_x = x;
14724       game.robot_wheel_y = y;
14725       game.robot_wheel_active = TRUE;
14726
14727       TEST_DrawLevelField(x, y);
14728     }
14729     else if (element == EL_SP_TERMINAL)
14730     {
14731       int xx, yy;
14732
14733       SCAN_PLAYFIELD(xx, yy)
14734       {
14735         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14736         {
14737           Bang(xx, yy);
14738         }
14739         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14740         {
14741           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14742
14743           ResetGfxAnimation(xx, yy);
14744           TEST_DrawLevelField(xx, yy);
14745         }
14746       }
14747     }
14748     else if (IS_BELT_SWITCH(element))
14749     {
14750       ToggleBeltSwitch(x, y);
14751     }
14752     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14753              element == EL_SWITCHGATE_SWITCH_DOWN ||
14754              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14755              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14756     {
14757       ToggleSwitchgateSwitch();
14758     }
14759     else if (element == EL_LIGHT_SWITCH ||
14760              element == EL_LIGHT_SWITCH_ACTIVE)
14761     {
14762       ToggleLightSwitch(x, y);
14763     }
14764     else if (element == EL_TIMEGATE_SWITCH ||
14765              element == EL_DC_TIMEGATE_SWITCH)
14766     {
14767       ActivateTimegateSwitch(x, y);
14768     }
14769     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14770              element == EL_BALLOON_SWITCH_RIGHT ||
14771              element == EL_BALLOON_SWITCH_UP    ||
14772              element == EL_BALLOON_SWITCH_DOWN  ||
14773              element == EL_BALLOON_SWITCH_NONE  ||
14774              element == EL_BALLOON_SWITCH_ANY)
14775     {
14776       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14777                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14778                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14779                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14780                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14781                              move_direction);
14782     }
14783     else if (element == EL_LAMP)
14784     {
14785       Tile[x][y] = EL_LAMP_ACTIVE;
14786       game.lights_still_needed--;
14787
14788       ResetGfxAnimation(x, y);
14789       TEST_DrawLevelField(x, y);
14790     }
14791     else if (element == EL_TIME_ORB_FULL)
14792     {
14793       Tile[x][y] = EL_TIME_ORB_EMPTY;
14794
14795       if (level.time > 0 || level.use_time_orb_bug)
14796       {
14797         TimeLeft += level.time_orb_time;
14798         game.no_level_time_limit = FALSE;
14799
14800         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14801
14802         DisplayGameControlValues();
14803       }
14804
14805       ResetGfxAnimation(x, y);
14806       TEST_DrawLevelField(x, y);
14807     }
14808     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14809              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14810     {
14811       int xx, yy;
14812
14813       game.ball_active = !game.ball_active;
14814
14815       SCAN_PLAYFIELD(xx, yy)
14816       {
14817         int e = Tile[xx][yy];
14818
14819         if (game.ball_active)
14820         {
14821           if (e == EL_EMC_MAGIC_BALL)
14822             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14823           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14824             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14825         }
14826         else
14827         {
14828           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14829             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14830           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14831             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14832         }
14833       }
14834     }
14835
14836     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14837                                         player->index_bit, dig_side);
14838
14839     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14840                                         player->index_bit, dig_side);
14841
14842     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14843                                         player->index_bit, dig_side);
14844
14845     return MP_ACTION;
14846   }
14847   else
14848   {
14849     if (!PLAYER_SWITCHING(player, x, y))
14850     {
14851       player->is_switching = TRUE;
14852       player->switch_x = x;
14853       player->switch_y = y;
14854
14855       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14856                                  player->index_bit, dig_side);
14857       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14858                                           player->index_bit, dig_side);
14859
14860       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14861                                  player->index_bit, dig_side);
14862       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14863                                           player->index_bit, dig_side);
14864     }
14865
14866     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14867                                player->index_bit, dig_side);
14868     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14869                                         player->index_bit, dig_side);
14870
14871     return MP_NO_ACTION;
14872   }
14873
14874   player->push_delay = -1;
14875
14876   if (is_player)                // function can also be called by EL_PENGUIN
14877   {
14878     if (Tile[x][y] != element)          // really digged/collected something
14879     {
14880       player->is_collecting = !player->is_digging;
14881       player->is_active = TRUE;
14882
14883       player->last_removed_element = element;
14884     }
14885   }
14886
14887   return MP_MOVING;
14888 }
14889
14890 static boolean DigFieldByCE(int x, int y, int digging_element)
14891 {
14892   int element = Tile[x][y];
14893
14894   if (!IS_FREE(x, y))
14895   {
14896     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14897                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14898                   ACTION_BREAKING);
14899
14900     // no element can dig solid indestructible elements
14901     if (IS_INDESTRUCTIBLE(element) &&
14902         !IS_DIGGABLE(element) &&
14903         !IS_COLLECTIBLE(element))
14904       return FALSE;
14905
14906     if (AmoebaNr[x][y] &&
14907         (element == EL_AMOEBA_FULL ||
14908          element == EL_BD_AMOEBA ||
14909          element == EL_AMOEBA_GROWING))
14910     {
14911       AmoebaCnt[AmoebaNr[x][y]]--;
14912       AmoebaCnt2[AmoebaNr[x][y]]--;
14913     }
14914
14915     if (IS_MOVING(x, y))
14916       RemoveMovingField(x, y);
14917     else
14918     {
14919       RemoveField(x, y);
14920       TEST_DrawLevelField(x, y);
14921     }
14922
14923     // if digged element was about to explode, prevent the explosion
14924     ExplodeField[x][y] = EX_TYPE_NONE;
14925
14926     PlayLevelSoundAction(x, y, action);
14927   }
14928
14929   Store[x][y] = EL_EMPTY;
14930
14931   // this makes it possible to leave the removed element again
14932   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14933     Store[x][y] = element;
14934
14935   return TRUE;
14936 }
14937
14938 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14939 {
14940   int jx = player->jx, jy = player->jy;
14941   int x = jx + dx, y = jy + dy;
14942   int snap_direction = (dx == -1 ? MV_LEFT  :
14943                         dx == +1 ? MV_RIGHT :
14944                         dy == -1 ? MV_UP    :
14945                         dy == +1 ? MV_DOWN  : MV_NONE);
14946   boolean can_continue_snapping = (level.continuous_snapping &&
14947                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14948
14949   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14950     return FALSE;
14951
14952   if (!player->active || !IN_LEV_FIELD(x, y))
14953     return FALSE;
14954
14955   if (dx && dy)
14956     return FALSE;
14957
14958   if (!dx && !dy)
14959   {
14960     if (player->MovPos == 0)
14961       player->is_pushing = FALSE;
14962
14963     player->is_snapping = FALSE;
14964
14965     if (player->MovPos == 0)
14966     {
14967       player->is_moving = FALSE;
14968       player->is_digging = FALSE;
14969       player->is_collecting = FALSE;
14970     }
14971
14972     return FALSE;
14973   }
14974
14975   // prevent snapping with already pressed snap key when not allowed
14976   if (player->is_snapping && !can_continue_snapping)
14977     return FALSE;
14978
14979   player->MovDir = snap_direction;
14980
14981   if (player->MovPos == 0)
14982   {
14983     player->is_moving = FALSE;
14984     player->is_digging = FALSE;
14985     player->is_collecting = FALSE;
14986   }
14987
14988   player->is_dropping = FALSE;
14989   player->is_dropping_pressed = FALSE;
14990   player->drop_pressed_delay = 0;
14991
14992   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14993     return FALSE;
14994
14995   player->is_snapping = TRUE;
14996   player->is_active = TRUE;
14997
14998   if (player->MovPos == 0)
14999   {
15000     player->is_moving = FALSE;
15001     player->is_digging = FALSE;
15002     player->is_collecting = FALSE;
15003   }
15004
15005   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15006     TEST_DrawLevelField(player->last_jx, player->last_jy);
15007
15008   TEST_DrawLevelField(x, y);
15009
15010   return TRUE;
15011 }
15012
15013 static boolean DropElement(struct PlayerInfo *player)
15014 {
15015   int old_element, new_element;
15016   int dropx = player->jx, dropy = player->jy;
15017   int drop_direction = player->MovDir;
15018   int drop_side = drop_direction;
15019   int drop_element = get_next_dropped_element(player);
15020
15021   /* do not drop an element on top of another element; when holding drop key
15022      pressed without moving, dropped element must move away before the next
15023      element can be dropped (this is especially important if the next element
15024      is dynamite, which can be placed on background for historical reasons) */
15025   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15026     return MP_ACTION;
15027
15028   if (IS_THROWABLE(drop_element))
15029   {
15030     dropx += GET_DX_FROM_DIR(drop_direction);
15031     dropy += GET_DY_FROM_DIR(drop_direction);
15032
15033     if (!IN_LEV_FIELD(dropx, dropy))
15034       return FALSE;
15035   }
15036
15037   old_element = Tile[dropx][dropy];     // old element at dropping position
15038   new_element = drop_element;           // default: no change when dropping
15039
15040   // check if player is active, not moving and ready to drop
15041   if (!player->active || player->MovPos || player->drop_delay > 0)
15042     return FALSE;
15043
15044   // check if player has anything that can be dropped
15045   if (new_element == EL_UNDEFINED)
15046     return FALSE;
15047
15048   // only set if player has anything that can be dropped
15049   player->is_dropping_pressed = TRUE;
15050
15051   // check if drop key was pressed long enough for EM style dynamite
15052   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15053     return FALSE;
15054
15055   // check if anything can be dropped at the current position
15056   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15057     return FALSE;
15058
15059   // collected custom elements can only be dropped on empty fields
15060   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15061     return FALSE;
15062
15063   if (old_element != EL_EMPTY)
15064     Back[dropx][dropy] = old_element;   // store old element on this field
15065
15066   ResetGfxAnimation(dropx, dropy);
15067   ResetRandomAnimationValue(dropx, dropy);
15068
15069   if (player->inventory_size > 0 ||
15070       player->inventory_infinite_element != EL_UNDEFINED)
15071   {
15072     if (player->inventory_size > 0)
15073     {
15074       player->inventory_size--;
15075
15076       DrawGameDoorValues();
15077
15078       if (new_element == EL_DYNAMITE)
15079         new_element = EL_DYNAMITE_ACTIVE;
15080       else if (new_element == EL_EM_DYNAMITE)
15081         new_element = EL_EM_DYNAMITE_ACTIVE;
15082       else if (new_element == EL_SP_DISK_RED)
15083         new_element = EL_SP_DISK_RED_ACTIVE;
15084     }
15085
15086     Tile[dropx][dropy] = new_element;
15087
15088     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15089       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15090                           el2img(Tile[dropx][dropy]), 0);
15091
15092     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15093
15094     // needed if previous element just changed to "empty" in the last frame
15095     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15096
15097     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15098                                player->index_bit, drop_side);
15099     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15100                                         CE_PLAYER_DROPS_X,
15101                                         player->index_bit, drop_side);
15102
15103     TestIfElementTouchesCustomElement(dropx, dropy);
15104   }
15105   else          // player is dropping a dyna bomb
15106   {
15107     player->dynabombs_left--;
15108
15109     Tile[dropx][dropy] = new_element;
15110
15111     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15112       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15113                           el2img(Tile[dropx][dropy]), 0);
15114
15115     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15116   }
15117
15118   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15119     InitField_WithBug1(dropx, dropy, FALSE);
15120
15121   new_element = Tile[dropx][dropy];     // element might have changed
15122
15123   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15124       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15125   {
15126     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15127       MovDir[dropx][dropy] = drop_direction;
15128
15129     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15130
15131     // do not cause impact style collision by dropping elements that can fall
15132     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15133   }
15134
15135   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15136   player->is_dropping = TRUE;
15137
15138   player->drop_pressed_delay = 0;
15139   player->is_dropping_pressed = FALSE;
15140
15141   player->drop_x = dropx;
15142   player->drop_y = dropy;
15143
15144   return TRUE;
15145 }
15146
15147 // ----------------------------------------------------------------------------
15148 // game sound playing functions
15149 // ----------------------------------------------------------------------------
15150
15151 static int *loop_sound_frame = NULL;
15152 static int *loop_sound_volume = NULL;
15153
15154 void InitPlayLevelSound(void)
15155 {
15156   int num_sounds = getSoundListSize();
15157
15158   checked_free(loop_sound_frame);
15159   checked_free(loop_sound_volume);
15160
15161   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15162   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15163 }
15164
15165 static void PlayLevelSound(int x, int y, int nr)
15166 {
15167   int sx = SCREENX(x), sy = SCREENY(y);
15168   int volume, stereo_position;
15169   int max_distance = 8;
15170   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15171
15172   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15173       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15174     return;
15175
15176   if (!IN_LEV_FIELD(x, y) ||
15177       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15178       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15179     return;
15180
15181   volume = SOUND_MAX_VOLUME;
15182
15183   if (!IN_SCR_FIELD(sx, sy))
15184   {
15185     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15186     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15187
15188     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15189   }
15190
15191   stereo_position = (SOUND_MAX_LEFT +
15192                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15193                      (SCR_FIELDX + 2 * max_distance));
15194
15195   if (IS_LOOP_SOUND(nr))
15196   {
15197     /* This assures that quieter loop sounds do not overwrite louder ones,
15198        while restarting sound volume comparison with each new game frame. */
15199
15200     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15201       return;
15202
15203     loop_sound_volume[nr] = volume;
15204     loop_sound_frame[nr] = FrameCounter;
15205   }
15206
15207   PlaySoundExt(nr, volume, stereo_position, type);
15208 }
15209
15210 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15211 {
15212   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15213                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15214                  y < LEVELY(BY1) ? LEVELY(BY1) :
15215                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15216                  sound_action);
15217 }
15218
15219 static void PlayLevelSoundAction(int x, int y, int action)
15220 {
15221   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15222 }
15223
15224 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15225 {
15226   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15227
15228   if (sound_effect != SND_UNDEFINED)
15229     PlayLevelSound(x, y, sound_effect);
15230 }
15231
15232 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15233                                               int action)
15234 {
15235   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15236
15237   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15238     PlayLevelSound(x, y, sound_effect);
15239 }
15240
15241 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15242 {
15243   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15244
15245   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15246     PlayLevelSound(x, y, sound_effect);
15247 }
15248
15249 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15250 {
15251   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15252
15253   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15254     StopSound(sound_effect);
15255 }
15256
15257 static int getLevelMusicNr(void)
15258 {
15259   int level_pos = level_nr - leveldir_current->first_level;
15260
15261   if (levelset.music[level_nr] != MUS_UNDEFINED)
15262     return levelset.music[level_nr];            // from config file
15263   else
15264     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15265 }
15266
15267 static void FadeLevelSounds(void)
15268 {
15269   FadeSounds();
15270 }
15271
15272 static void FadeLevelMusic(void)
15273 {
15274   int music_nr = getLevelMusicNr();
15275   char *curr_music = getCurrentlyPlayingMusicFilename();
15276   char *next_music = getMusicInfoEntryFilename(music_nr);
15277
15278   if (!strEqual(curr_music, next_music))
15279     FadeMusic();
15280 }
15281
15282 void FadeLevelSoundsAndMusic(void)
15283 {
15284   FadeLevelSounds();
15285   FadeLevelMusic();
15286 }
15287
15288 static void PlayLevelMusic(void)
15289 {
15290   int music_nr = getLevelMusicNr();
15291   char *curr_music = getCurrentlyPlayingMusicFilename();
15292   char *next_music = getMusicInfoEntryFilename(music_nr);
15293
15294   if (!strEqual(curr_music, next_music))
15295     PlayMusicLoop(music_nr);
15296 }
15297
15298 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15299 {
15300   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15301   int offset = 0;
15302   int x = xx - offset;
15303   int y = yy - offset;
15304
15305   switch (sample)
15306   {
15307     case SOUND_blank:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15309       break;
15310
15311     case SOUND_roll:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15313       break;
15314
15315     case SOUND_stone:
15316       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15317       break;
15318
15319     case SOUND_nut:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15321       break;
15322
15323     case SOUND_crack:
15324       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15325       break;
15326
15327     case SOUND_bug:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15329       break;
15330
15331     case SOUND_tank:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15333       break;
15334
15335     case SOUND_android_clone:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15337       break;
15338
15339     case SOUND_android_move:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15341       break;
15342
15343     case SOUND_spring:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15345       break;
15346
15347     case SOUND_slurp:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15349       break;
15350
15351     case SOUND_eater:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15353       break;
15354
15355     case SOUND_eater_eat:
15356       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15357       break;
15358
15359     case SOUND_alien:
15360       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15361       break;
15362
15363     case SOUND_collect:
15364       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15365       break;
15366
15367     case SOUND_diamond:
15368       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15369       break;
15370
15371     case SOUND_squash:
15372       // !!! CHECK THIS !!!
15373 #if 1
15374       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15375 #else
15376       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15377 #endif
15378       break;
15379
15380     case SOUND_wonderfall:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15382       break;
15383
15384     case SOUND_drip:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15386       break;
15387
15388     case SOUND_push:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15390       break;
15391
15392     case SOUND_dirt:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15394       break;
15395
15396     case SOUND_acid:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15398       break;
15399
15400     case SOUND_ball:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15402       break;
15403
15404     case SOUND_slide:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15406       break;
15407
15408     case SOUND_wonder:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15410       break;
15411
15412     case SOUND_door:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15414       break;
15415
15416     case SOUND_exit_open:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15418       break;
15419
15420     case SOUND_exit_leave:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15422       break;
15423
15424     case SOUND_dynamite:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15426       break;
15427
15428     case SOUND_tick:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15430       break;
15431
15432     case SOUND_press:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15434       break;
15435
15436     case SOUND_wheel:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15438       break;
15439
15440     case SOUND_boom:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15442       break;
15443
15444     case SOUND_die:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15446       break;
15447
15448     case SOUND_time:
15449       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15450       break;
15451
15452     default:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15454       break;
15455   }
15456 }
15457
15458 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15459 {
15460   int element = map_element_SP_to_RND(element_sp);
15461   int action = map_action_SP_to_RND(action_sp);
15462   int offset = (setup.sp_show_border_elements ? 0 : 1);
15463   int x = xx - offset;
15464   int y = yy - offset;
15465
15466   PlayLevelSoundElementAction(x, y, element, action);
15467 }
15468
15469 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15470 {
15471   int element = map_element_MM_to_RND(element_mm);
15472   int action = map_action_MM_to_RND(action_mm);
15473   int offset = 0;
15474   int x = xx - offset;
15475   int y = yy - offset;
15476
15477   if (!IS_MM_ELEMENT(element))
15478     element = EL_MM_DEFAULT;
15479
15480   PlayLevelSoundElementAction(x, y, element, action);
15481 }
15482
15483 void PlaySound_MM(int sound_mm)
15484 {
15485   int sound = map_sound_MM_to_RND(sound_mm);
15486
15487   if (sound == SND_UNDEFINED)
15488     return;
15489
15490   PlaySound(sound);
15491 }
15492
15493 void PlaySoundLoop_MM(int sound_mm)
15494 {
15495   int sound = map_sound_MM_to_RND(sound_mm);
15496
15497   if (sound == SND_UNDEFINED)
15498     return;
15499
15500   PlaySoundLoop(sound);
15501 }
15502
15503 void StopSound_MM(int sound_mm)
15504 {
15505   int sound = map_sound_MM_to_RND(sound_mm);
15506
15507   if (sound == SND_UNDEFINED)
15508     return;
15509
15510   StopSound(sound);
15511 }
15512
15513 void RaiseScore(int value)
15514 {
15515   game.score += value;
15516
15517   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15518
15519   DisplayGameControlValues();
15520 }
15521
15522 void RaiseScoreElement(int element)
15523 {
15524   switch (element)
15525   {
15526     case EL_EMERALD:
15527     case EL_BD_DIAMOND:
15528     case EL_EMERALD_YELLOW:
15529     case EL_EMERALD_RED:
15530     case EL_EMERALD_PURPLE:
15531     case EL_SP_INFOTRON:
15532       RaiseScore(level.score[SC_EMERALD]);
15533       break;
15534     case EL_DIAMOND:
15535       RaiseScore(level.score[SC_DIAMOND]);
15536       break;
15537     case EL_CRYSTAL:
15538       RaiseScore(level.score[SC_CRYSTAL]);
15539       break;
15540     case EL_PEARL:
15541       RaiseScore(level.score[SC_PEARL]);
15542       break;
15543     case EL_BUG:
15544     case EL_BD_BUTTERFLY:
15545     case EL_SP_ELECTRON:
15546       RaiseScore(level.score[SC_BUG]);
15547       break;
15548     case EL_SPACESHIP:
15549     case EL_BD_FIREFLY:
15550     case EL_SP_SNIKSNAK:
15551       RaiseScore(level.score[SC_SPACESHIP]);
15552       break;
15553     case EL_YAMYAM:
15554     case EL_DARK_YAMYAM:
15555       RaiseScore(level.score[SC_YAMYAM]);
15556       break;
15557     case EL_ROBOT:
15558       RaiseScore(level.score[SC_ROBOT]);
15559       break;
15560     case EL_PACMAN:
15561       RaiseScore(level.score[SC_PACMAN]);
15562       break;
15563     case EL_NUT:
15564       RaiseScore(level.score[SC_NUT]);
15565       break;
15566     case EL_DYNAMITE:
15567     case EL_EM_DYNAMITE:
15568     case EL_SP_DISK_RED:
15569     case EL_DYNABOMB_INCREASE_NUMBER:
15570     case EL_DYNABOMB_INCREASE_SIZE:
15571     case EL_DYNABOMB_INCREASE_POWER:
15572       RaiseScore(level.score[SC_DYNAMITE]);
15573       break;
15574     case EL_SHIELD_NORMAL:
15575     case EL_SHIELD_DEADLY:
15576       RaiseScore(level.score[SC_SHIELD]);
15577       break;
15578     case EL_EXTRA_TIME:
15579       RaiseScore(level.extra_time_score);
15580       break;
15581     case EL_KEY_1:
15582     case EL_KEY_2:
15583     case EL_KEY_3:
15584     case EL_KEY_4:
15585     case EL_EM_KEY_1:
15586     case EL_EM_KEY_2:
15587     case EL_EM_KEY_3:
15588     case EL_EM_KEY_4:
15589     case EL_EMC_KEY_5:
15590     case EL_EMC_KEY_6:
15591     case EL_EMC_KEY_7:
15592     case EL_EMC_KEY_8:
15593     case EL_DC_KEY_WHITE:
15594       RaiseScore(level.score[SC_KEY]);
15595       break;
15596     default:
15597       RaiseScore(element_info[element].collect_score);
15598       break;
15599   }
15600 }
15601
15602 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15603 {
15604   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15605   {
15606     if (!quick_quit)
15607     {
15608       // prevent short reactivation of overlay buttons while closing door
15609       SetOverlayActive(FALSE);
15610       UnmapGameButtons();
15611
15612       // door may still be open due to skipped or envelope style request
15613       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15614     }
15615
15616     if (network.enabled)
15617       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15618     else
15619     {
15620       if (quick_quit)
15621         FadeSkipNextFadeIn();
15622
15623       SetGameStatus(GAME_MODE_MAIN);
15624
15625       DrawMainMenu();
15626     }
15627   }
15628   else          // continue playing the game
15629   {
15630     if (tape.playing && tape.deactivate_display)
15631       TapeDeactivateDisplayOff(TRUE);
15632
15633     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15634
15635     if (tape.playing && tape.deactivate_display)
15636       TapeDeactivateDisplayOn();
15637   }
15638 }
15639
15640 void RequestQuitGame(boolean escape_key_pressed)
15641 {
15642   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15643   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15644                         level_editor_test_game);
15645   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15646                           quick_quit || score_info_tape_play);
15647
15648   RequestQuitGameExt(skip_request, quick_quit,
15649                      "Do you really want to quit the game?");
15650 }
15651
15652 static char *getRestartGameMessage(void)
15653 {
15654   boolean play_again = hasStartedNetworkGame();
15655   static char message[MAX_OUTPUT_LINESIZE];
15656   char *game_over_text = "Game over!";
15657   char *play_again_text = " Play it again?";
15658
15659   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15660       game_mm.game_over_message != NULL)
15661     game_over_text = game_mm.game_over_message;
15662
15663   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15664            (play_again ? play_again_text : ""));
15665
15666   return message;
15667 }
15668
15669 static void RequestRestartGame(void)
15670 {
15671   char *message = getRestartGameMessage();
15672   boolean has_started_game = hasStartedNetworkGame();
15673   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15674   int door_state = DOOR_CLOSE_1;
15675
15676   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15677   {
15678     CloseDoor(door_state);
15679
15680     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15681   }
15682   else
15683   {
15684     // if game was invoked from level editor, also close tape recorder door
15685     if (level_editor_test_game)
15686       door_state = DOOR_CLOSE_ALL;
15687
15688     CloseDoor(door_state);
15689
15690     SetGameStatus(GAME_MODE_MAIN);
15691
15692     DrawMainMenu();
15693   }
15694 }
15695
15696 boolean CheckRestartGame(void)
15697 {
15698   static int game_over_delay = 0;
15699   int game_over_delay_value = 50;
15700   boolean game_over = checkGameFailed();
15701
15702   if (!game_over)
15703   {
15704     game_over_delay = game_over_delay_value;
15705
15706     return FALSE;
15707   }
15708
15709   if (game_over_delay > 0)
15710   {
15711     if (game_over_delay == game_over_delay_value / 2)
15712       PlaySound(SND_GAME_LOSING);
15713
15714     game_over_delay--;
15715
15716     return FALSE;
15717   }
15718
15719   // do not handle game over if request dialog is already active
15720   if (game.request_active)
15721     return FALSE;
15722
15723   // do not ask to play again if game was never actually played
15724   if (!game.GamePlayed)
15725     return FALSE;
15726
15727   // do not ask to play again if this was disabled in setup menu
15728   if (!setup.ask_on_game_over)
15729     return FALSE;
15730
15731   RequestRestartGame();
15732
15733   return TRUE;
15734 }
15735
15736 boolean checkGameSolved(void)
15737 {
15738   // set for all game engines if level was solved
15739   return game.LevelSolved_GameEnd;
15740 }
15741
15742 boolean checkGameFailed(void)
15743 {
15744   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15745     return (game_em.game_over && !game_em.level_solved);
15746   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15747     return (game_sp.game_over && !game_sp.level_solved);
15748   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15749     return (game_mm.game_over && !game_mm.level_solved);
15750   else                          // GAME_ENGINE_TYPE_RND
15751     return (game.GameOver && !game.LevelSolved);
15752 }
15753
15754 boolean checkGameEnded(void)
15755 {
15756   return (checkGameSolved() || checkGameFailed());
15757 }
15758
15759
15760 // ----------------------------------------------------------------------------
15761 // random generator functions
15762 // ----------------------------------------------------------------------------
15763
15764 unsigned int InitEngineRandom_RND(int seed)
15765 {
15766   game.num_random_calls = 0;
15767
15768   return InitEngineRandom(seed);
15769 }
15770
15771 unsigned int RND(int max)
15772 {
15773   if (max > 0)
15774   {
15775     game.num_random_calls++;
15776
15777     return GetEngineRandom(max);
15778   }
15779
15780   return 0;
15781 }
15782
15783
15784 // ----------------------------------------------------------------------------
15785 // game engine snapshot handling functions
15786 // ----------------------------------------------------------------------------
15787
15788 struct EngineSnapshotInfo
15789 {
15790   // runtime values for custom element collect score
15791   int collect_score[NUM_CUSTOM_ELEMENTS];
15792
15793   // runtime values for group element choice position
15794   int choice_pos[NUM_GROUP_ELEMENTS];
15795
15796   // runtime values for belt position animations
15797   int belt_graphic[4][NUM_BELT_PARTS];
15798   int belt_anim_mode[4][NUM_BELT_PARTS];
15799 };
15800
15801 static struct EngineSnapshotInfo engine_snapshot_rnd;
15802 static char *snapshot_level_identifier = NULL;
15803 static int snapshot_level_nr = -1;
15804
15805 static void SaveEngineSnapshotValues_RND(void)
15806 {
15807   static int belt_base_active_element[4] =
15808   {
15809     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15810     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15811     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15812     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15813   };
15814   int i, j;
15815
15816   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15817   {
15818     int element = EL_CUSTOM_START + i;
15819
15820     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15821   }
15822
15823   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15824   {
15825     int element = EL_GROUP_START + i;
15826
15827     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15828   }
15829
15830   for (i = 0; i < 4; i++)
15831   {
15832     for (j = 0; j < NUM_BELT_PARTS; j++)
15833     {
15834       int element = belt_base_active_element[i] + j;
15835       int graphic = el2img(element);
15836       int anim_mode = graphic_info[graphic].anim_mode;
15837
15838       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15839       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15840     }
15841   }
15842 }
15843
15844 static void LoadEngineSnapshotValues_RND(void)
15845 {
15846   unsigned int num_random_calls = game.num_random_calls;
15847   int i, j;
15848
15849   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15850   {
15851     int element = EL_CUSTOM_START + i;
15852
15853     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15854   }
15855
15856   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15857   {
15858     int element = EL_GROUP_START + i;
15859
15860     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15861   }
15862
15863   for (i = 0; i < 4; i++)
15864   {
15865     for (j = 0; j < NUM_BELT_PARTS; j++)
15866     {
15867       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15868       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15869
15870       graphic_info[graphic].anim_mode = anim_mode;
15871     }
15872   }
15873
15874   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15875   {
15876     InitRND(tape.random_seed);
15877     for (i = 0; i < num_random_calls; i++)
15878       RND(1);
15879   }
15880
15881   if (game.num_random_calls != num_random_calls)
15882   {
15883     Error("number of random calls out of sync");
15884     Error("number of random calls should be %d", num_random_calls);
15885     Error("number of random calls is %d", game.num_random_calls);
15886
15887     Fail("this should not happen -- please debug");
15888   }
15889 }
15890
15891 void FreeEngineSnapshotSingle(void)
15892 {
15893   FreeSnapshotSingle();
15894
15895   setString(&snapshot_level_identifier, NULL);
15896   snapshot_level_nr = -1;
15897 }
15898
15899 void FreeEngineSnapshotList(void)
15900 {
15901   FreeSnapshotList();
15902 }
15903
15904 static ListNode *SaveEngineSnapshotBuffers(void)
15905 {
15906   ListNode *buffers = NULL;
15907
15908   // copy some special values to a structure better suited for the snapshot
15909
15910   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15911     SaveEngineSnapshotValues_RND();
15912   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15913     SaveEngineSnapshotValues_EM();
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15915     SaveEngineSnapshotValues_SP(&buffers);
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15917     SaveEngineSnapshotValues_MM();
15918
15919   // save values stored in special snapshot structure
15920
15921   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15922     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15923   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15924     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15925   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15926     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15927   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15928     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15929
15930   // save further RND engine values
15931
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15935
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15941
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15947
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15950
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15969
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15972
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15989
15990 #if 0
15991   ListNode *node = engine_snapshot_list_rnd;
15992   int num_bytes = 0;
15993
15994   while (node != NULL)
15995   {
15996     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15997
15998     node = node->next;
15999   }
16000
16001   Debug("game:playing:SaveEngineSnapshotBuffers",
16002         "size of engine snapshot: %d bytes", num_bytes);
16003 #endif
16004
16005   return buffers;
16006 }
16007
16008 void SaveEngineSnapshotSingle(void)
16009 {
16010   ListNode *buffers = SaveEngineSnapshotBuffers();
16011
16012   // finally save all snapshot buffers to single snapshot
16013   SaveSnapshotSingle(buffers);
16014
16015   // save level identification information
16016   setString(&snapshot_level_identifier, leveldir_current->identifier);
16017   snapshot_level_nr = level_nr;
16018 }
16019
16020 boolean CheckSaveEngineSnapshotToList(void)
16021 {
16022   boolean save_snapshot =
16023     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16024      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16025       game.snapshot.changed_action) ||
16026      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16027       game.snapshot.collected_item));
16028
16029   game.snapshot.changed_action = FALSE;
16030   game.snapshot.collected_item = FALSE;
16031   game.snapshot.save_snapshot = save_snapshot;
16032
16033   return save_snapshot;
16034 }
16035
16036 void SaveEngineSnapshotToList(void)
16037 {
16038   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16039       tape.quick_resume)
16040     return;
16041
16042   ListNode *buffers = SaveEngineSnapshotBuffers();
16043
16044   // finally save all snapshot buffers to snapshot list
16045   SaveSnapshotToList(buffers);
16046 }
16047
16048 void SaveEngineSnapshotToListInitial(void)
16049 {
16050   FreeEngineSnapshotList();
16051
16052   SaveEngineSnapshotToList();
16053 }
16054
16055 static void LoadEngineSnapshotValues(void)
16056 {
16057   // restore special values from snapshot structure
16058
16059   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16060     LoadEngineSnapshotValues_RND();
16061   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16062     LoadEngineSnapshotValues_EM();
16063   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16064     LoadEngineSnapshotValues_SP();
16065   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16066     LoadEngineSnapshotValues_MM();
16067 }
16068
16069 void LoadEngineSnapshotSingle(void)
16070 {
16071   LoadSnapshotSingle();
16072
16073   LoadEngineSnapshotValues();
16074 }
16075
16076 static void LoadEngineSnapshot_Undo(int steps)
16077 {
16078   LoadSnapshotFromList_Older(steps);
16079
16080   LoadEngineSnapshotValues();
16081 }
16082
16083 static void LoadEngineSnapshot_Redo(int steps)
16084 {
16085   LoadSnapshotFromList_Newer(steps);
16086
16087   LoadEngineSnapshotValues();
16088 }
16089
16090 boolean CheckEngineSnapshotSingle(void)
16091 {
16092   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16093           snapshot_level_nr == level_nr);
16094 }
16095
16096 boolean CheckEngineSnapshotList(void)
16097 {
16098   return CheckSnapshotList();
16099 }
16100
16101
16102 // ---------- new game button stuff -------------------------------------------
16103
16104 static struct
16105 {
16106   int graphic;
16107   struct XY *pos;
16108   int gadget_id;
16109   boolean *setup_value;
16110   boolean allowed_on_tape;
16111   boolean is_touch_button;
16112   char *infotext;
16113 } gamebutton_info[NUM_GAME_BUTTONS] =
16114 {
16115   {
16116     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16117     GAME_CTRL_ID_STOP,                          NULL,
16118     TRUE, FALSE,                                "stop game"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16122     GAME_CTRL_ID_PAUSE,                         NULL,
16123     TRUE, FALSE,                                "pause game"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16127     GAME_CTRL_ID_PLAY,                          NULL,
16128     TRUE, FALSE,                                "play game"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16132     GAME_CTRL_ID_UNDO,                          NULL,
16133     TRUE, FALSE,                                "undo step"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16137     GAME_CTRL_ID_REDO,                          NULL,
16138     TRUE, FALSE,                                "redo step"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16142     GAME_CTRL_ID_SAVE,                          NULL,
16143     TRUE, FALSE,                                "save game"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16147     GAME_CTRL_ID_PAUSE2,                        NULL,
16148     TRUE, FALSE,                                "pause game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16152     GAME_CTRL_ID_LOAD,                          NULL,
16153     TRUE, FALSE,                                "load game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16157     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16158     FALSE, FALSE,                               "stop game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16162     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16163     FALSE, FALSE,                               "pause game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16167     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16168     FALSE, FALSE,                               "play game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16172     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16173     FALSE, TRUE,                                "stop game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16177     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16178     FALSE, TRUE,                                "pause game"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16182     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16183     TRUE, FALSE,                                "background music on/off"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16187     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16188     TRUE, FALSE,                                "sound loops on/off"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16192     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16193     TRUE, FALSE,                                "normal sounds on/off"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16197     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16198     FALSE, FALSE,                               "background music on/off"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16202     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16203     FALSE, FALSE,                               "sound loops on/off"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16207     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16208     FALSE, FALSE,                               "normal sounds on/off"
16209   }
16210 };
16211
16212 void CreateGameButtons(void)
16213 {
16214   int i;
16215
16216   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16217   {
16218     int graphic = gamebutton_info[i].graphic;
16219     struct GraphicInfo *gfx = &graphic_info[graphic];
16220     struct XY *pos = gamebutton_info[i].pos;
16221     struct GadgetInfo *gi;
16222     int button_type;
16223     boolean checked;
16224     unsigned int event_mask;
16225     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16226     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16227     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16228     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16229     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16230     int gd_x   = gfx->src_x;
16231     int gd_y   = gfx->src_y;
16232     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16233     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16234     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16235     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16236     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16237     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16238     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16239     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16240     int id = i;
16241
16242     // do not use touch buttons if overlay touch buttons are disabled
16243     if (is_touch_button && !setup.touch.overlay_buttons)
16244       continue;
16245
16246     if (gfx->bitmap == NULL)
16247     {
16248       game_gadget[id] = NULL;
16249
16250       continue;
16251     }
16252
16253     if (id == GAME_CTRL_ID_STOP ||
16254         id == GAME_CTRL_ID_PANEL_STOP ||
16255         id == GAME_CTRL_ID_TOUCH_STOP ||
16256         id == GAME_CTRL_ID_PLAY ||
16257         id == GAME_CTRL_ID_PANEL_PLAY ||
16258         id == GAME_CTRL_ID_SAVE ||
16259         id == GAME_CTRL_ID_LOAD)
16260     {
16261       button_type = GD_TYPE_NORMAL_BUTTON;
16262       checked = FALSE;
16263       event_mask = GD_EVENT_RELEASED;
16264     }
16265     else if (id == GAME_CTRL_ID_UNDO ||
16266              id == GAME_CTRL_ID_REDO)
16267     {
16268       button_type = GD_TYPE_NORMAL_BUTTON;
16269       checked = FALSE;
16270       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16271     }
16272     else
16273     {
16274       button_type = GD_TYPE_CHECK_BUTTON;
16275       checked = (gamebutton_info[i].setup_value != NULL ?
16276                  *gamebutton_info[i].setup_value : FALSE);
16277       event_mask = GD_EVENT_PRESSED;
16278     }
16279
16280     gi = CreateGadget(GDI_CUSTOM_ID, id,
16281                       GDI_IMAGE_ID, graphic,
16282                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16283                       GDI_X, base_x + x,
16284                       GDI_Y, base_y + y,
16285                       GDI_WIDTH, gfx->width,
16286                       GDI_HEIGHT, gfx->height,
16287                       GDI_TYPE, button_type,
16288                       GDI_STATE, GD_BUTTON_UNPRESSED,
16289                       GDI_CHECKED, checked,
16290                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16291                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16292                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16293                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16294                       GDI_DIRECT_DRAW, FALSE,
16295                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16296                       GDI_EVENT_MASK, event_mask,
16297                       GDI_CALLBACK_ACTION, HandleGameButtons,
16298                       GDI_END);
16299
16300     if (gi == NULL)
16301       Fail("cannot create gadget");
16302
16303     game_gadget[id] = gi;
16304   }
16305 }
16306
16307 void FreeGameButtons(void)
16308 {
16309   int i;
16310
16311   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16312     FreeGadget(game_gadget[i]);
16313 }
16314
16315 static void UnmapGameButtonsAtSamePosition(int id)
16316 {
16317   int i;
16318
16319   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16320     if (i != id &&
16321         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16322         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16323       UnmapGadget(game_gadget[i]);
16324 }
16325
16326 static void UnmapGameButtonsAtSamePosition_All(void)
16327 {
16328   if (setup.show_load_save_buttons)
16329   {
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16331     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16333   }
16334   else if (setup.show_undo_redo_buttons)
16335   {
16336     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16339   }
16340   else
16341   {
16342     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16343     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16344     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16345
16346     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16347     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16348     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16349   }
16350 }
16351
16352 void MapLoadSaveButtons(void)
16353 {
16354   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16355   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16356
16357   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16358   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16359 }
16360
16361 void MapUndoRedoButtons(void)
16362 {
16363   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16364   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16365
16366   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16367   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16368 }
16369
16370 void ModifyPauseButtons(void)
16371 {
16372   static int ids[] =
16373   {
16374     GAME_CTRL_ID_PAUSE,
16375     GAME_CTRL_ID_PAUSE2,
16376     GAME_CTRL_ID_PANEL_PAUSE,
16377     GAME_CTRL_ID_TOUCH_PAUSE,
16378     -1
16379   };
16380   int i;
16381
16382   for (i = 0; ids[i] > -1; i++)
16383     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16384 }
16385
16386 static void MapGameButtonsExt(boolean on_tape)
16387 {
16388   int i;
16389
16390   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16391   {
16392     if ((i == GAME_CTRL_ID_UNDO ||
16393          i == GAME_CTRL_ID_REDO) &&
16394         game_status != GAME_MODE_PLAYING)
16395       continue;
16396
16397     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16398       MapGadget(game_gadget[i]);
16399   }
16400
16401   UnmapGameButtonsAtSamePosition_All();
16402
16403   RedrawGameButtons();
16404 }
16405
16406 static void UnmapGameButtonsExt(boolean on_tape)
16407 {
16408   int i;
16409
16410   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16411     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16412       UnmapGadget(game_gadget[i]);
16413 }
16414
16415 static void RedrawGameButtonsExt(boolean on_tape)
16416 {
16417   int i;
16418
16419   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16420     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16421       RedrawGadget(game_gadget[i]);
16422 }
16423
16424 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16425 {
16426   if (gi == NULL)
16427     return;
16428
16429   gi->checked = state;
16430 }
16431
16432 static void RedrawSoundButtonGadget(int id)
16433 {
16434   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16435              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16436              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16437              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16438              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16439              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16440              id);
16441
16442   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16443   RedrawGadget(game_gadget[id2]);
16444 }
16445
16446 void MapGameButtons(void)
16447 {
16448   MapGameButtonsExt(FALSE);
16449 }
16450
16451 void UnmapGameButtons(void)
16452 {
16453   UnmapGameButtonsExt(FALSE);
16454 }
16455
16456 void RedrawGameButtons(void)
16457 {
16458   RedrawGameButtonsExt(FALSE);
16459 }
16460
16461 void MapGameButtonsOnTape(void)
16462 {
16463   MapGameButtonsExt(TRUE);
16464 }
16465
16466 void UnmapGameButtonsOnTape(void)
16467 {
16468   UnmapGameButtonsExt(TRUE);
16469 }
16470
16471 void RedrawGameButtonsOnTape(void)
16472 {
16473   RedrawGameButtonsExt(TRUE);
16474 }
16475
16476 static void GameUndoRedoExt(void)
16477 {
16478   ClearPlayerAction();
16479
16480   tape.pausing = TRUE;
16481
16482   RedrawPlayfield();
16483   UpdateAndDisplayGameControlValues();
16484
16485   DrawCompleteVideoDisplay();
16486   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16487   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16488   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16489
16490   ModifyPauseButtons();
16491
16492   BackToFront();
16493 }
16494
16495 static void GameUndo(int steps)
16496 {
16497   if (!CheckEngineSnapshotList())
16498     return;
16499
16500   int tape_property_bits = tape.property_bits;
16501
16502   LoadEngineSnapshot_Undo(steps);
16503
16504   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16505
16506   GameUndoRedoExt();
16507 }
16508
16509 static void GameRedo(int steps)
16510 {
16511   if (!CheckEngineSnapshotList())
16512     return;
16513
16514   int tape_property_bits = tape.property_bits;
16515
16516   LoadEngineSnapshot_Redo(steps);
16517
16518   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16519
16520   GameUndoRedoExt();
16521 }
16522
16523 static void HandleGameButtonsExt(int id, int button)
16524 {
16525   static boolean game_undo_executed = FALSE;
16526   int steps = BUTTON_STEPSIZE(button);
16527   boolean handle_game_buttons =
16528     (game_status == GAME_MODE_PLAYING ||
16529      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16530
16531   if (!handle_game_buttons)
16532     return;
16533
16534   switch (id)
16535   {
16536     case GAME_CTRL_ID_STOP:
16537     case GAME_CTRL_ID_PANEL_STOP:
16538     case GAME_CTRL_ID_TOUCH_STOP:
16539       TapeStopGame();
16540
16541       break;
16542
16543     case GAME_CTRL_ID_PAUSE:
16544     case GAME_CTRL_ID_PAUSE2:
16545     case GAME_CTRL_ID_PANEL_PAUSE:
16546     case GAME_CTRL_ID_TOUCH_PAUSE:
16547       if (network.enabled && game_status == GAME_MODE_PLAYING)
16548       {
16549         if (tape.pausing)
16550           SendToServer_ContinuePlaying();
16551         else
16552           SendToServer_PausePlaying();
16553       }
16554       else
16555         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16556
16557       game_undo_executed = FALSE;
16558
16559       break;
16560
16561     case GAME_CTRL_ID_PLAY:
16562     case GAME_CTRL_ID_PANEL_PLAY:
16563       if (game_status == GAME_MODE_MAIN)
16564       {
16565         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16566       }
16567       else if (tape.pausing)
16568       {
16569         if (network.enabled)
16570           SendToServer_ContinuePlaying();
16571         else
16572           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16573       }
16574       break;
16575
16576     case GAME_CTRL_ID_UNDO:
16577       // Important: When using "save snapshot when collecting an item" mode,
16578       // load last (current) snapshot for first "undo" after pressing "pause"
16579       // (else the last-but-one snapshot would be loaded, because the snapshot
16580       // pointer already points to the last snapshot when pressing "pause",
16581       // which is fine for "every step/move" mode, but not for "every collect")
16582       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16583           !game_undo_executed)
16584         steps--;
16585
16586       game_undo_executed = TRUE;
16587
16588       GameUndo(steps);
16589       break;
16590
16591     case GAME_CTRL_ID_REDO:
16592       GameRedo(steps);
16593       break;
16594
16595     case GAME_CTRL_ID_SAVE:
16596       TapeQuickSave();
16597       break;
16598
16599     case GAME_CTRL_ID_LOAD:
16600       TapeQuickLoad();
16601       break;
16602
16603     case SOUND_CTRL_ID_MUSIC:
16604     case SOUND_CTRL_ID_PANEL_MUSIC:
16605       if (setup.sound_music)
16606       { 
16607         setup.sound_music = FALSE;
16608
16609         FadeMusic();
16610       }
16611       else if (audio.music_available)
16612       { 
16613         setup.sound = setup.sound_music = TRUE;
16614
16615         SetAudioMode(setup.sound);
16616
16617         if (game_status == GAME_MODE_PLAYING)
16618           PlayLevelMusic();
16619       }
16620
16621       RedrawSoundButtonGadget(id);
16622
16623       break;
16624
16625     case SOUND_CTRL_ID_LOOPS:
16626     case SOUND_CTRL_ID_PANEL_LOOPS:
16627       if (setup.sound_loops)
16628         setup.sound_loops = FALSE;
16629       else if (audio.loops_available)
16630       {
16631         setup.sound = setup.sound_loops = TRUE;
16632
16633         SetAudioMode(setup.sound);
16634       }
16635
16636       RedrawSoundButtonGadget(id);
16637
16638       break;
16639
16640     case SOUND_CTRL_ID_SIMPLE:
16641     case SOUND_CTRL_ID_PANEL_SIMPLE:
16642       if (setup.sound_simple)
16643         setup.sound_simple = FALSE;
16644       else if (audio.sound_available)
16645       {
16646         setup.sound = setup.sound_simple = TRUE;
16647
16648         SetAudioMode(setup.sound);
16649       }
16650
16651       RedrawSoundButtonGadget(id);
16652
16653       break;
16654
16655     default:
16656       break;
16657   }
16658 }
16659
16660 static void HandleGameButtons(struct GadgetInfo *gi)
16661 {
16662   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16663 }
16664
16665 void HandleSoundButtonKeys(Key key)
16666 {
16667   if (key == setup.shortcut.sound_simple)
16668     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16669   else if (key == setup.shortcut.sound_loops)
16670     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16671   else if (key == setup.shortcut.sound_music)
16672     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16673 }