d197e6b32f96a2e0bac70b6a366c86a362a53aa3
[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 boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_TIME)
2685       {
2686         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2687
2688         if (use_dynamic_size)           // use dynamic number of digits
2689         {
2690           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2691           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2692           int size2 = size1 + 1;
2693           int font1 = pos->font;
2694           int font2 = pos->font_alt;
2695
2696           size = (value < value_change ? size1 : size2);
2697           font = (value < value_change ? font1 : font2);
2698         }
2699       }
2700
2701       // correct text size if "digits" is zero or less
2702       if (size <= 0)
2703         size = strlen(int2str(value, size));
2704
2705       // dynamically correct text alignment
2706       pos->width = size * getFontWidth(font);
2707
2708       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                   int2str(value, size), font, mask_mode);
2710     }
2711     else if (type == TYPE_ELEMENT)
2712     {
2713       int element, graphic;
2714       Bitmap *src_bitmap;
2715       int src_x, src_y;
2716       int width, height;
2717       int dst_x = PANEL_XPOS(pos);
2718       int dst_y = PANEL_YPOS(pos);
2719
2720       if (value != EL_UNDEFINED && value != EL_EMPTY)
2721       {
2722         element = value;
2723         graphic = el2panelimg(value);
2724
2725 #if 0
2726         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2727               element, EL_NAME(element), size);
2728 #endif
2729
2730         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2731           size = TILESIZE;
2732
2733         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2734                               &src_x, &src_y);
2735
2736         width  = graphic_info[graphic].width  * size / TILESIZE;
2737         height = graphic_info[graphic].height * size / TILESIZE;
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_GRAPHIC)
2748     {
2749       int graphic        = gpc->graphic;
2750       int graphic_active = gpc->graphic_active;
2751       Bitmap *src_bitmap;
2752       int src_x, src_y;
2753       int width, height;
2754       int dst_x = PANEL_XPOS(pos);
2755       int dst_y = PANEL_YPOS(pos);
2756       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2757                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2758
2759       if (graphic != IMG_UNDEFINED && !skip)
2760       {
2761         if (pos->style == STYLE_REVERSE)
2762           value = 100 - value;
2763
2764         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2765
2766         if (pos->direction & MV_HORIZONTAL)
2767         {
2768           width  = graphic_info[graphic_active].width * value / 100;
2769           height = graphic_info[graphic_active].height;
2770
2771           if (pos->direction == MV_LEFT)
2772           {
2773             src_x += graphic_info[graphic_active].width - width;
2774             dst_x += graphic_info[graphic_active].width - width;
2775           }
2776         }
2777         else
2778         {
2779           width  = graphic_info[graphic_active].width;
2780           height = graphic_info[graphic_active].height * value / 100;
2781
2782           if (pos->direction == MV_UP)
2783           {
2784             src_y += graphic_info[graphic_active].height - height;
2785             dst_y += graphic_info[graphic_active].height - height;
2786           }
2787         }
2788
2789         if (draw_masked)
2790           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2791                            dst_x, dst_y);
2792         else
2793           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2794                      dst_x, dst_y);
2795
2796         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2797
2798         if (pos->direction & MV_HORIZONTAL)
2799         {
2800           if (pos->direction == MV_RIGHT)
2801           {
2802             src_x += width;
2803             dst_x += width;
2804           }
2805           else
2806           {
2807             dst_x = PANEL_XPOS(pos);
2808           }
2809
2810           width = graphic_info[graphic].width - width;
2811         }
2812         else
2813         {
2814           if (pos->direction == MV_DOWN)
2815           {
2816             src_y += height;
2817             dst_y += height;
2818           }
2819           else
2820           {
2821             dst_y = PANEL_YPOS(pos);
2822           }
2823
2824           height = graphic_info[graphic].height - height;
2825         }
2826
2827         if (draw_masked)
2828           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2829                            dst_x, dst_y);
2830         else
2831           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2832                      dst_x, dst_y);
2833       }
2834     }
2835     else if (type == TYPE_STRING)
2836     {
2837       boolean active = (value != 0);
2838       char *state_normal = "off";
2839       char *state_active = "on";
2840       char *state = (active ? state_active : state_normal);
2841       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2842                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2843                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2844                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2845
2846       if (nr == GAME_PANEL_GRAVITY_STATE)
2847       {
2848         int font1 = pos->font;          // (used for normal state)
2849         int font2 = pos->font_alt;      // (used for active state)
2850
2851         font = (active ? font2 : font1);
2852       }
2853
2854       if (s != NULL)
2855       {
2856         char *s_cut;
2857
2858         if (size <= 0)
2859         {
2860           // don't truncate output if "chars" is zero or less
2861           size = strlen(s);
2862
2863           // dynamically correct text alignment
2864           pos->width = size * getFontWidth(font);
2865         }
2866
2867         s_cut = getStringCopyN(s, size);
2868
2869         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2870                     s_cut, font, mask_mode);
2871
2872         free(s_cut);
2873       }
2874     }
2875
2876     redraw_mask |= REDRAW_DOOR_1;
2877   }
2878
2879   SetGameStatus(GAME_MODE_PLAYING);
2880 }
2881
2882 void UpdateAndDisplayGameControlValues(void)
2883 {
2884   if (tape.deactivate_display)
2885     return;
2886
2887   UpdateGameControlValues();
2888   DisplayGameControlValues();
2889 }
2890
2891 void UpdateGameDoorValues(void)
2892 {
2893   UpdateGameControlValues();
2894 }
2895
2896 void DrawGameDoorValues(void)
2897 {
2898   DisplayGameControlValues();
2899 }
2900
2901
2902 // ============================================================================
2903 // InitGameEngine()
2904 // ----------------------------------------------------------------------------
2905 // initialize game engine due to level / tape version number
2906 // ============================================================================
2907
2908 static void InitGameEngine(void)
2909 {
2910   int i, j, k, l, x, y;
2911
2912   // set game engine from tape file when re-playing, else from level file
2913   game.engine_version = (tape.playing ? tape.engine_version :
2914                          level.game_version);
2915
2916   // set single or multi-player game mode (needed for re-playing tapes)
2917   game.team_mode = setup.team_mode;
2918
2919   if (tape.playing)
2920   {
2921     int num_players = 0;
2922
2923     for (i = 0; i < MAX_PLAYERS; i++)
2924       if (tape.player_participates[i])
2925         num_players++;
2926
2927     // multi-player tapes contain input data for more than one player
2928     game.team_mode = (num_players > 1);
2929   }
2930
2931 #if 0
2932   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2933         level.game_version);
2934   Debug("game:init:level", "          tape.file_version   == %06d",
2935         tape.file_version);
2936   Debug("game:init:level", "          tape.game_version   == %06d",
2937         tape.game_version);
2938   Debug("game:init:level", "          tape.engine_version == %06d",
2939         tape.engine_version);
2940   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2941         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2942 #endif
2943
2944   // --------------------------------------------------------------------------
2945   // set flags for bugs and changes according to active game engine version
2946   // --------------------------------------------------------------------------
2947
2948   /*
2949     Summary of bugfix:
2950     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2951
2952     Bug was introduced in version:
2953     2.0.1
2954
2955     Bug was fixed in version:
2956     4.2.0.0
2957
2958     Description:
2959     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2960     but the property "can fall" was missing, which caused some levels to be
2961     unsolvable. This was fixed in version 4.2.0.0.
2962
2963     Affected levels/tapes:
2964     An example for a tape that was fixed by this bugfix is tape 029 from the
2965     level set "rnd_sam_bateman".
2966     The wrong behaviour will still be used for all levels or tapes that were
2967     created/recorded with it. An example for this is tape 023 from the level
2968     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2969   */
2970
2971   boolean use_amoeba_dropping_cannot_fall_bug =
2972     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2973       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2974      (tape.playing &&
2975       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2976       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2977
2978   /*
2979     Summary of bugfix/change:
2980     Fixed move speed of elements entering or leaving magic wall.
2981
2982     Fixed/changed in version:
2983     2.0.1
2984
2985     Description:
2986     Before 2.0.1, move speed of elements entering or leaving magic wall was
2987     twice as fast as it is now.
2988     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2989
2990     Affected levels/tapes:
2991     The first condition is generally needed for all levels/tapes before version
2992     2.0.1, which might use the old behaviour before it was changed; known tapes
2993     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2994     The second condition is an exception from the above case and is needed for
2995     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2996     above, but before it was known that this change would break tapes like the
2997     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2998     although the engine version while recording maybe was before 2.0.1. There
2999     are a lot of tapes that are affected by this exception, like tape 006 from
3000     the level set "rnd_conor_mancone".
3001   */
3002
3003   boolean use_old_move_stepsize_for_magic_wall =
3004     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3005      !(tape.playing &&
3006        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3007        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3008
3009   /*
3010     Summary of bugfix/change:
3011     Fixed handling for custom elements that change when pushed by the player.
3012
3013     Fixed/changed in version:
3014     3.1.0
3015
3016     Description:
3017     Before 3.1.0, custom elements that "change when pushing" changed directly
3018     after the player started pushing them (until then handled in "DigField()").
3019     Since 3.1.0, these custom elements are not changed until the "pushing"
3020     move of the element is finished (now handled in "ContinueMoving()").
3021
3022     Affected levels/tapes:
3023     The first condition is generally needed for all levels/tapes before version
3024     3.1.0, which might use the old behaviour before it was changed; known tapes
3025     that are affected are some tapes from the level set "Walpurgis Gardens" by
3026     Jamie Cullen.
3027     The second condition is an exception from the above case and is needed for
3028     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3029     above (including some development versions of 3.1.0), but before it was
3030     known that this change would break tapes like the above and was fixed in
3031     3.1.1, so that the changed behaviour was active although the engine version
3032     while recording maybe was before 3.1.0. There is at least one tape that is
3033     affected by this exception, which is the tape for the one-level set "Bug
3034     Machine" by Juergen Bonhagen.
3035   */
3036
3037   game.use_change_when_pushing_bug =
3038     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3039      !(tape.playing &&
3040        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3041        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3042
3043   /*
3044     Summary of bugfix/change:
3045     Fixed handling for blocking the field the player leaves when moving.
3046
3047     Fixed/changed in version:
3048     3.1.1
3049
3050     Description:
3051     Before 3.1.1, when "block last field when moving" was enabled, the field
3052     the player is leaving when moving was blocked for the time of the move,
3053     and was directly unblocked afterwards. This resulted in the last field
3054     being blocked for exactly one less than the number of frames of one player
3055     move. Additionally, even when blocking was disabled, the last field was
3056     blocked for exactly one frame.
3057     Since 3.1.1, due to changes in player movement handling, the last field
3058     is not blocked at all when blocking is disabled. When blocking is enabled,
3059     the last field is blocked for exactly the number of frames of one player
3060     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3061     last field is blocked for exactly one more than the number of frames of
3062     one player move.
3063
3064     Affected levels/tapes:
3065     (!!! yet to be determined -- probably many !!!)
3066   */
3067
3068   game.use_block_last_field_bug =
3069     (game.engine_version < VERSION_IDENT(3,1,1,0));
3070
3071   /* various special flags and settings for native Emerald Mine game engine */
3072
3073   game_em.use_single_button =
3074     (game.engine_version > VERSION_IDENT(4,0,0,2));
3075
3076   game_em.use_snap_key_bug =
3077     (game.engine_version < VERSION_IDENT(4,0,1,0));
3078
3079   game_em.use_random_bug =
3080     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3081
3082   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3083
3084   game_em.use_old_explosions            = use_old_em_engine;
3085   game_em.use_old_android               = use_old_em_engine;
3086   game_em.use_old_push_elements         = use_old_em_engine;
3087   game_em.use_old_push_into_acid        = use_old_em_engine;
3088
3089   game_em.use_wrap_around               = !use_old_em_engine;
3090
3091   // --------------------------------------------------------------------------
3092
3093   // set maximal allowed number of custom element changes per game frame
3094   game.max_num_changes_per_frame = 1;
3095
3096   // default scan direction: scan playfield from top/left to bottom/right
3097   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3098
3099   // dynamically adjust element properties according to game engine version
3100   InitElementPropertiesEngine(game.engine_version);
3101
3102   // ---------- initialize special element properties -------------------------
3103
3104   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3105   if (use_amoeba_dropping_cannot_fall_bug)
3106     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3107
3108   // ---------- initialize player's initial move delay ------------------------
3109
3110   // dynamically adjust player properties according to level information
3111   for (i = 0; i < MAX_PLAYERS; i++)
3112     game.initial_move_delay_value[i] =
3113       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3114
3115   // dynamically adjust player properties according to game engine version
3116   for (i = 0; i < MAX_PLAYERS; i++)
3117     game.initial_move_delay[i] =
3118       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3119        game.initial_move_delay_value[i] : 0);
3120
3121   // ---------- initialize player's initial push delay ------------------------
3122
3123   // dynamically adjust player properties according to game engine version
3124   game.initial_push_delay_value =
3125     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3126
3127   // ---------- initialize changing elements ----------------------------------
3128
3129   // initialize changing elements information
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131   {
3132     struct ElementInfo *ei = &element_info[i];
3133
3134     // this pointer might have been changed in the level editor
3135     ei->change = &ei->change_page[0];
3136
3137     if (!IS_CUSTOM_ELEMENT(i))
3138     {
3139       ei->change->target_element = EL_EMPTY_SPACE;
3140       ei->change->delay_fixed = 0;
3141       ei->change->delay_random = 0;
3142       ei->change->delay_frames = 1;
3143     }
3144
3145     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3146     {
3147       ei->has_change_event[j] = FALSE;
3148
3149       ei->event_page_nr[j] = 0;
3150       ei->event_page[j] = &ei->change_page[0];
3151     }
3152   }
3153
3154   // add changing elements from pre-defined list
3155   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3156   {
3157     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3158     struct ElementInfo *ei = &element_info[ch_delay->element];
3159
3160     ei->change->target_element       = ch_delay->target_element;
3161     ei->change->delay_fixed          = ch_delay->change_delay;
3162
3163     ei->change->pre_change_function  = ch_delay->pre_change_function;
3164     ei->change->change_function      = ch_delay->change_function;
3165     ei->change->post_change_function = ch_delay->post_change_function;
3166
3167     ei->change->can_change = TRUE;
3168     ei->change->can_change_or_has_action = TRUE;
3169
3170     ei->has_change_event[CE_DELAY] = TRUE;
3171
3172     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3174   }
3175
3176   // ---------- initialize internal run-time variables ------------------------
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       ei->change_page[j].can_change_or_has_action =
3185         (ei->change_page[j].can_change |
3186          ei->change_page[j].has_action);
3187     }
3188   }
3189
3190   // add change events from custom element configuration
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       if (!ei->change_page[j].can_change_or_has_action)
3198         continue;
3199
3200       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3201       {
3202         // only add event page for the first page found with this event
3203         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3204         {
3205           ei->has_change_event[k] = TRUE;
3206
3207           ei->event_page_nr[k] = j;
3208           ei->event_page[k] = &ei->change_page[j];
3209         }
3210       }
3211     }
3212   }
3213
3214   // ---------- initialize reference elements in change conditions ------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     int element = EL_CUSTOM_START + i;
3219     struct ElementInfo *ei = &element_info[element];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       int trigger_element = ei->change_page[j].initial_trigger_element;
3224
3225       if (trigger_element >= EL_PREV_CE_8 &&
3226           trigger_element <= EL_NEXT_CE_8)
3227         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3228
3229       ei->change_page[j].trigger_element = trigger_element;
3230     }
3231   }
3232
3233   // ---------- initialize run-time trigger player and element ----------------
3234
3235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3236   {
3237     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3242       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3244       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3245       ei->change_page[j].actual_trigger_ce_value = 0;
3246       ei->change_page[j].actual_trigger_ce_score = 0;
3247     }
3248   }
3249
3250   // ---------- initialize trigger events -------------------------------------
3251
3252   // initialize trigger events information
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3255       trigger_events[i][j] = FALSE;
3256
3257   // add trigger events from element change event properties
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     struct ElementInfo *ei = &element_info[i];
3261
3262     for (j = 0; j < ei->num_change_pages; j++)
3263     {
3264       if (!ei->change_page[j].can_change_or_has_action)
3265         continue;
3266
3267       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3268       {
3269         int trigger_element = ei->change_page[j].trigger_element;
3270
3271         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3272         {
3273           if (ei->change_page[j].has_event[k])
3274           {
3275             if (IS_GROUP_ELEMENT(trigger_element))
3276             {
3277               struct ElementGroupInfo *group =
3278                 element_info[trigger_element].group;
3279
3280               for (l = 0; l < group->num_elements_resolved; l++)
3281                 trigger_events[group->element_resolved[l]][k] = TRUE;
3282             }
3283             else if (trigger_element == EL_ANY_ELEMENT)
3284               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3285                 trigger_events[l][k] = TRUE;
3286             else
3287               trigger_events[trigger_element][k] = TRUE;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   // ---------- initialize push delay -----------------------------------------
3295
3296   // initialize push delay values to default
3297   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3298   {
3299     if (!IS_CUSTOM_ELEMENT(i))
3300     {
3301       // set default push delay values (corrected since version 3.0.7-1)
3302       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3303       {
3304         element_info[i].push_delay_fixed = 2;
3305         element_info[i].push_delay_random = 8;
3306       }
3307       else
3308       {
3309         element_info[i].push_delay_fixed = 8;
3310         element_info[i].push_delay_random = 8;
3311       }
3312     }
3313   }
3314
3315   // set push delay value for certain elements from pre-defined list
3316   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3317   {
3318     int e = push_delay_list[i].element;
3319
3320     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3321     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3322   }
3323
3324   // set push delay value for Supaplex elements for newer engine versions
3325   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3326   {
3327     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328     {
3329       if (IS_SP_ELEMENT(i))
3330       {
3331         // set SP push delay to just enough to push under a falling zonk
3332         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3333
3334         element_info[i].push_delay_fixed  = delay;
3335         element_info[i].push_delay_random = 0;
3336       }
3337     }
3338   }
3339
3340   // ---------- initialize move stepsize --------------------------------------
3341
3342   // initialize move stepsize values to default
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3346
3347   // set move stepsize value for certain elements from pre-defined list
3348   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     int e = move_stepsize_list[i].element;
3351
3352     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3353
3354     // set move stepsize value for certain elements for older engine versions
3355     if (use_old_move_stepsize_for_magic_wall)
3356     {
3357       if (e == EL_MAGIC_WALL_FILLING ||
3358           e == EL_MAGIC_WALL_EMPTYING ||
3359           e == EL_BD_MAGIC_WALL_FILLING ||
3360           e == EL_BD_MAGIC_WALL_EMPTYING)
3361         element_info[e].move_stepsize *= 2;
3362     }
3363   }
3364
3365   // ---------- initialize collect score --------------------------------------
3366
3367   // initialize collect score values for custom elements from initial value
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (IS_CUSTOM_ELEMENT(i))
3370       element_info[i].collect_score = element_info[i].collect_score_initial;
3371
3372   // ---------- initialize collect count --------------------------------------
3373
3374   // initialize collect count values for non-custom elements
3375   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3376     if (!IS_CUSTOM_ELEMENT(i))
3377       element_info[i].collect_count_initial = 0;
3378
3379   // add collect count values for all elements from pre-defined list
3380   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3381     element_info[collect_count_list[i].element].collect_count_initial =
3382       collect_count_list[i].count;
3383
3384   // ---------- initialize access direction -----------------------------------
3385
3386   // initialize access direction values to default (access from every side)
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388     if (!IS_CUSTOM_ELEMENT(i))
3389       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3390
3391   // set access direction value for certain elements from pre-defined list
3392   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3393     element_info[access_direction_list[i].element].access_direction =
3394       access_direction_list[i].direction;
3395
3396   // ---------- initialize explosion content ----------------------------------
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398   {
3399     if (IS_CUSTOM_ELEMENT(i))
3400       continue;
3401
3402     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3403     {
3404       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3405
3406       element_info[i].content.e[x][y] =
3407         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3408          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3409          i == EL_PLAYER_3 ? EL_EMERALD :
3410          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3411          i == EL_MOLE ? EL_EMERALD_RED :
3412          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3413          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3414          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3415          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3416          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3417          i == EL_WALL_EMERALD ? EL_EMERALD :
3418          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3419          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3420          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3421          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3422          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3423          i == EL_WALL_PEARL ? EL_PEARL :
3424          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3425          EL_EMPTY);
3426     }
3427   }
3428
3429   // ---------- initialize recursion detection --------------------------------
3430   recursion_loop_depth = 0;
3431   recursion_loop_detected = FALSE;
3432   recursion_loop_element = EL_UNDEFINED;
3433
3434   // ---------- initialize graphics engine ------------------------------------
3435   game.scroll_delay_value =
3436     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3437      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3438      !setup.forced_scroll_delay           ? 0 :
3439      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3440   game.scroll_delay_value =
3441     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3442
3443   // ---------- initialize game engine snapshots ------------------------------
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445     game.snapshot.last_action[i] = 0;
3446   game.snapshot.changed_action = FALSE;
3447   game.snapshot.collected_item = FALSE;
3448   game.snapshot.mode =
3449     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3450      SNAPSHOT_MODE_EVERY_STEP :
3451      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3452      SNAPSHOT_MODE_EVERY_MOVE :
3453      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3454      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3455   game.snapshot.save_snapshot = FALSE;
3456
3457   // ---------- initialize level time for Supaplex engine ---------------------
3458   // Supaplex levels with time limit currently unsupported -- should be added
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3460     level.time = 0;
3461
3462   // ---------- initialize flags for handling game actions --------------------
3463
3464   // set flags for game actions to default values
3465   game.use_key_actions = TRUE;
3466   game.use_mouse_actions = FALSE;
3467
3468   // when using Mirror Magic game engine, handle mouse events only
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3470   {
3471     game.use_key_actions = FALSE;
3472     game.use_mouse_actions = TRUE;
3473   }
3474
3475   // check for custom elements with mouse click events
3476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3477   {
3478     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479     {
3480       int element = EL_CUSTOM_START + i;
3481
3482       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3483           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3486         game.use_mouse_actions = TRUE;
3487     }
3488   }
3489 }
3490
3491 static int get_num_special_action(int element, int action_first,
3492                                   int action_last)
3493 {
3494   int num_special_action = 0;
3495   int i, j;
3496
3497   for (i = action_first; i <= action_last; i++)
3498   {
3499     boolean found = FALSE;
3500
3501     for (j = 0; j < NUM_DIRECTIONS; j++)
3502       if (el_act_dir2img(element, i, j) !=
3503           el_act_dir2img(element, ACTION_DEFAULT, j))
3504         found = TRUE;
3505
3506     if (found)
3507       num_special_action++;
3508     else
3509       break;
3510   }
3511
3512   return num_special_action;
3513 }
3514
3515
3516 // ============================================================================
3517 // InitGame()
3518 // ----------------------------------------------------------------------------
3519 // initialize and start new game
3520 // ============================================================================
3521
3522 #if DEBUG_INIT_PLAYER
3523 static void DebugPrintPlayerStatus(char *message)
3524 {
3525   int i;
3526
3527   if (!options.debug)
3528     return;
3529
3530   Debug("game:init:player", "%s:", message);
3531
3532   for (i = 0; i < MAX_PLAYERS; i++)
3533   {
3534     struct PlayerInfo *player = &stored_player[i];
3535
3536     Debug("game:init:player",
3537           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3538           i + 1,
3539           player->present,
3540           player->connected,
3541           player->connected_locally,
3542           player->connected_network,
3543           player->active,
3544           (local_player == player ? " (local player)" : ""));
3545   }
3546 }
3547 #endif
3548
3549 void InitGame(void)
3550 {
3551   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3552   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int fade_mask = REDRAW_FIELD;
3554
3555   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3556   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3557   int initial_move_dir = MV_DOWN;
3558   int i, j, x, y;
3559
3560   // required here to update video display before fading (FIX THIS)
3561   DrawMaskedBorder(REDRAW_DOOR_2);
3562
3563   if (!game.restart_level)
3564     CloseDoor(DOOR_CLOSE_1);
3565
3566   SetGameStatus(GAME_MODE_PLAYING);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeOut();
3570   else
3571     FadeSetEnterScreen();
3572
3573   if (CheckFadeAll())
3574     fade_mask = REDRAW_ALL;
3575
3576   FadeLevelSoundsAndMusic();
3577
3578   ExpireSoundLoops(TRUE);
3579
3580   FadeOut(fade_mask);
3581
3582   if (level_editor_test_game)
3583     FadeSkipNextFadeIn();
3584
3585   // needed if different viewport properties defined for playing
3586   ChangeViewportPropertiesIfNeeded();
3587
3588   ClearField();
3589
3590   DrawCompleteVideoDisplay();
3591
3592   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3593
3594   InitGameEngine();
3595   InitGameControlValues();
3596
3597   if (tape.recording)
3598   {
3599     // initialize tape actions from game when recording tape
3600     tape.use_key_actions   = game.use_key_actions;
3601     tape.use_mouse_actions = game.use_mouse_actions;
3602
3603     // initialize visible playfield size when recording tape (for team mode)
3604     tape.scr_fieldx = SCR_FIELDX;
3605     tape.scr_fieldy = SCR_FIELDY;
3606   }
3607
3608   // don't play tapes over network
3609   network_playing = (network.enabled && !tape.playing);
3610
3611   for (i = 0; i < MAX_PLAYERS; i++)
3612   {
3613     struct PlayerInfo *player = &stored_player[i];
3614
3615     player->index_nr = i;
3616     player->index_bit = (1 << i);
3617     player->element_nr = EL_PLAYER_1 + i;
3618
3619     player->present = FALSE;
3620     player->active = FALSE;
3621     player->mapped = FALSE;
3622
3623     player->killed = FALSE;
3624     player->reanimated = FALSE;
3625     player->buried = FALSE;
3626
3627     player->action = 0;
3628     player->effective_action = 0;
3629     player->programmed_action = 0;
3630     player->snap_action = 0;
3631
3632     player->mouse_action.lx = 0;
3633     player->mouse_action.ly = 0;
3634     player->mouse_action.button = 0;
3635     player->mouse_action.button_hint = 0;
3636
3637     player->effective_mouse_action.lx = 0;
3638     player->effective_mouse_action.ly = 0;
3639     player->effective_mouse_action.button = 0;
3640     player->effective_mouse_action.button_hint = 0;
3641
3642     for (j = 0; j < MAX_NUM_KEYS; j++)
3643       player->key[j] = FALSE;
3644
3645     player->num_white_keys = 0;
3646
3647     player->dynabomb_count = 0;
3648     player->dynabomb_size = 1;
3649     player->dynabombs_left = 0;
3650     player->dynabomb_xl = FALSE;
3651
3652     player->MovDir = initial_move_dir;
3653     player->MovPos = 0;
3654     player->GfxPos = 0;
3655     player->GfxDir = initial_move_dir;
3656     player->GfxAction = ACTION_DEFAULT;
3657     player->Frame = 0;
3658     player->StepFrame = 0;
3659
3660     player->initial_element = player->element_nr;
3661     player->artwork_element =
3662       (level.use_artwork_element[i] ? level.artwork_element[i] :
3663        player->element_nr);
3664     player->use_murphy = FALSE;
3665
3666     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3667     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3668
3669     player->gravity = level.initial_player_gravity[i];
3670
3671     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3672
3673     player->actual_frame_counter = 0;
3674
3675     player->step_counter = 0;
3676
3677     player->last_move_dir = initial_move_dir;
3678
3679     player->is_active = FALSE;
3680
3681     player->is_waiting = FALSE;
3682     player->is_moving = FALSE;
3683     player->is_auto_moving = FALSE;
3684     player->is_digging = FALSE;
3685     player->is_snapping = FALSE;
3686     player->is_collecting = FALSE;
3687     player->is_pushing = FALSE;
3688     player->is_switching = FALSE;
3689     player->is_dropping = FALSE;
3690     player->is_dropping_pressed = FALSE;
3691
3692     player->is_bored = FALSE;
3693     player->is_sleeping = FALSE;
3694
3695     player->was_waiting = TRUE;
3696     player->was_moving = FALSE;
3697     player->was_snapping = FALSE;
3698     player->was_dropping = FALSE;
3699
3700     player->force_dropping = FALSE;
3701
3702     player->frame_counter_bored = -1;
3703     player->frame_counter_sleeping = -1;
3704
3705     player->anim_delay_counter = 0;
3706     player->post_delay_counter = 0;
3707
3708     player->dir_waiting = initial_move_dir;
3709     player->action_waiting = ACTION_DEFAULT;
3710     player->last_action_waiting = ACTION_DEFAULT;
3711     player->special_action_bored = ACTION_DEFAULT;
3712     player->special_action_sleeping = ACTION_DEFAULT;
3713
3714     player->switch_x = -1;
3715     player->switch_y = -1;
3716
3717     player->drop_x = -1;
3718     player->drop_y = -1;
3719
3720     player->show_envelope = 0;
3721
3722     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3723
3724     player->push_delay       = -1;      // initialized when pushing starts
3725     player->push_delay_value = game.initial_push_delay_value;
3726
3727     player->drop_delay = 0;
3728     player->drop_pressed_delay = 0;
3729
3730     player->last_jx = -1;
3731     player->last_jy = -1;
3732     player->jx = -1;
3733     player->jy = -1;
3734
3735     player->shield_normal_time_left = 0;
3736     player->shield_deadly_time_left = 0;
3737
3738     player->last_removed_element = EL_UNDEFINED;
3739
3740     player->inventory_infinite_element = EL_UNDEFINED;
3741     player->inventory_size = 0;
3742
3743     if (level.use_initial_inventory[i])
3744     {
3745       for (j = 0; j < level.initial_inventory_size[i]; j++)
3746       {
3747         int element = level.initial_inventory_content[i][j];
3748         int collect_count = element_info[element].collect_count_initial;
3749         int k;
3750
3751         if (!IS_CUSTOM_ELEMENT(element))
3752           collect_count = 1;
3753
3754         if (collect_count == 0)
3755           player->inventory_infinite_element = element;
3756         else
3757           for (k = 0; k < collect_count; k++)
3758             if (player->inventory_size < MAX_INVENTORY_SIZE)
3759               player->inventory_element[player->inventory_size++] = element;
3760       }
3761     }
3762
3763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3764     SnapField(player, 0, 0);
3765
3766     map_player_action[i] = i;
3767   }
3768
3769   network_player_action_received = FALSE;
3770
3771   // initial null action
3772   if (network_playing)
3773     SendToServer_MovePlayer(MV_NONE);
3774
3775   FrameCounter = 0;
3776   TimeFrames = 0;
3777   TimePlayed = 0;
3778   TimeLeft = level.time;
3779   TapeTime = 0;
3780
3781   ScreenMovDir = MV_NONE;
3782   ScreenMovPos = 0;
3783   ScreenGfxPos = 0;
3784
3785   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3786
3787   game.robot_wheel_x = -1;
3788   game.robot_wheel_y = -1;
3789
3790   game.exit_x = -1;
3791   game.exit_y = -1;
3792
3793   game.all_players_gone = FALSE;
3794
3795   game.LevelSolved = FALSE;
3796   game.GameOver = FALSE;
3797
3798   game.GamePlayed = !tape.playing;
3799
3800   game.LevelSolved_GameWon = FALSE;
3801   game.LevelSolved_GameEnd = FALSE;
3802   game.LevelSolved_SaveTape = FALSE;
3803   game.LevelSolved_SaveScore = FALSE;
3804
3805   game.LevelSolved_CountingTime = 0;
3806   game.LevelSolved_CountingScore = 0;
3807   game.LevelSolved_CountingHealth = 0;
3808
3809   game.panel.active = TRUE;
3810
3811   game.no_time_limit = (level.time == 0);
3812
3813   game.yamyam_content_nr = 0;
3814   game.robot_wheel_active = FALSE;
3815   game.magic_wall_active = FALSE;
3816   game.magic_wall_time_left = 0;
3817   game.light_time_left = 0;
3818   game.timegate_time_left = 0;
3819   game.switchgate_pos = 0;
3820   game.wind_direction = level.wind_direction_initial;
3821
3822   game.time_final = 0;
3823   game.score_time_final = 0;
3824
3825   game.score = 0;
3826   game.score_final = 0;
3827
3828   game.health = MAX_HEALTH;
3829   game.health_final = MAX_HEALTH;
3830
3831   game.gems_still_needed = level.gems_needed;
3832   game.sokoban_fields_still_needed = 0;
3833   game.sokoban_objects_still_needed = 0;
3834   game.lights_still_needed = 0;
3835   game.players_still_needed = 0;
3836   game.friends_still_needed = 0;
3837
3838   game.lenses_time_left = 0;
3839   game.magnify_time_left = 0;
3840
3841   game.ball_active = level.ball_active_initial;
3842   game.ball_content_nr = 0;
3843
3844   game.explosions_delayed = TRUE;
3845
3846   game.envelope_active = FALSE;
3847
3848   // special case: set custom artwork setting to initial value
3849   game.use_masked_elements = game.use_masked_elements_initial;
3850
3851   for (i = 0; i < NUM_BELTS; i++)
3852   {
3853     game.belt_dir[i] = MV_NONE;
3854     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3855   }
3856
3857   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3858     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3859
3860 #if DEBUG_INIT_PLAYER
3861   DebugPrintPlayerStatus("Player status at level initialization");
3862 #endif
3863
3864   SCAN_PLAYFIELD(x, y)
3865   {
3866     Tile[x][y] = Last[x][y] = level.field[x][y];
3867     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3868     ChangeDelay[x][y] = 0;
3869     ChangePage[x][y] = -1;
3870     CustomValue[x][y] = 0;              // initialized in InitField()
3871     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3872     AmoebaNr[x][y] = 0;
3873     WasJustMoving[x][y] = 0;
3874     WasJustFalling[x][y] = 0;
3875     CheckCollision[x][y] = 0;
3876     CheckImpact[x][y] = 0;
3877     Stop[x][y] = FALSE;
3878     Pushed[x][y] = FALSE;
3879
3880     ChangeCount[x][y] = 0;
3881     ChangeEvent[x][y] = -1;
3882
3883     ExplodePhase[x][y] = 0;
3884     ExplodeDelay[x][y] = 0;
3885     ExplodeField[x][y] = EX_TYPE_NONE;
3886
3887     RunnerVisit[x][y] = 0;
3888     PlayerVisit[x][y] = 0;
3889
3890     GfxFrame[x][y] = 0;
3891     GfxRandom[x][y] = INIT_GFX_RANDOM();
3892     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3893     GfxElement[x][y] = EL_UNDEFINED;
3894     GfxElementEmpty[x][y] = EL_EMPTY;
3895     GfxAction[x][y] = ACTION_DEFAULT;
3896     GfxDir[x][y] = MV_NONE;
3897     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3898   }
3899
3900   SCAN_PLAYFIELD(x, y)
3901   {
3902     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3903       emulate_bd = FALSE;
3904     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3905       emulate_sp = FALSE;
3906
3907     InitField(x, y, TRUE);
3908
3909     ResetGfxAnimation(x, y);
3910   }
3911
3912   InitBeltMovement();
3913
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915   {
3916     struct PlayerInfo *player = &stored_player[i];
3917
3918     // set number of special actions for bored and sleeping animation
3919     player->num_special_action_bored =
3920       get_num_special_action(player->artwork_element,
3921                              ACTION_BORING_1, ACTION_BORING_LAST);
3922     player->num_special_action_sleeping =
3923       get_num_special_action(player->artwork_element,
3924                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3925   }
3926
3927   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3928                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3929
3930   // initialize type of slippery elements
3931   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3932   {
3933     if (!IS_CUSTOM_ELEMENT(i))
3934     {
3935       // default: elements slip down either to the left or right randomly
3936       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3937
3938       // SP style elements prefer to slip down on the left side
3939       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3940         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3941
3942       // BD style elements prefer to slip down on the left side
3943       if (game.emulation == EMU_BOULDERDASH)
3944         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3945     }
3946   }
3947
3948   // initialize explosion and ignition delay
3949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3950   {
3951     if (!IS_CUSTOM_ELEMENT(i))
3952     {
3953       int num_phase = 8;
3954       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3955                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3956                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3957       int last_phase = (num_phase + 1) * delay;
3958       int half_phase = (num_phase / 2) * delay;
3959
3960       element_info[i].explosion_delay = last_phase - 1;
3961       element_info[i].ignition_delay = half_phase;
3962
3963       if (i == EL_BLACK_ORB)
3964         element_info[i].ignition_delay = 1;
3965     }
3966   }
3967
3968   // correct non-moving belts to start moving left
3969   for (i = 0; i < NUM_BELTS; i++)
3970     if (game.belt_dir[i] == MV_NONE)
3971       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3972
3973 #if USE_NEW_PLAYER_ASSIGNMENTS
3974   // use preferred player also in local single-player mode
3975   if (!network.enabled && !game.team_mode)
3976   {
3977     int new_index_nr = setup.network_player_nr;
3978
3979     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3980     {
3981       for (i = 0; i < MAX_PLAYERS; i++)
3982         stored_player[i].connected_locally = FALSE;
3983
3984       stored_player[new_index_nr].connected_locally = TRUE;
3985     }
3986   }
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989   {
3990     stored_player[i].connected = FALSE;
3991
3992     // in network game mode, the local player might not be the first player
3993     if (stored_player[i].connected_locally)
3994       local_player = &stored_player[i];
3995   }
3996
3997   if (!network.enabled)
3998     local_player->connected = TRUE;
3999
4000   if (tape.playing)
4001   {
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003       stored_player[i].connected = tape.player_participates[i];
4004   }
4005   else if (network.enabled)
4006   {
4007     // add team mode players connected over the network (needed for correct
4008     // assignment of player figures from level to locally playing players)
4009
4010     for (i = 0; i < MAX_PLAYERS; i++)
4011       if (stored_player[i].connected_network)
4012         stored_player[i].connected = TRUE;
4013   }
4014   else if (game.team_mode)
4015   {
4016     // try to guess locally connected team mode players (needed for correct
4017     // assignment of player figures from level to locally playing players)
4018
4019     for (i = 0; i < MAX_PLAYERS; i++)
4020       if (setup.input[i].use_joystick ||
4021           setup.input[i].key.left != KSYM_UNDEFINED)
4022         stored_player[i].connected = TRUE;
4023   }
4024
4025 #if DEBUG_INIT_PLAYER
4026   DebugPrintPlayerStatus("Player status after level initialization");
4027 #endif
4028
4029 #if DEBUG_INIT_PLAYER
4030   Debug("game:init:player", "Reassigning players ...");
4031 #endif
4032
4033   // check if any connected player was not found in playfield
4034   for (i = 0; i < MAX_PLAYERS; i++)
4035   {
4036     struct PlayerInfo *player = &stored_player[i];
4037
4038     if (player->connected && !player->present)
4039     {
4040       struct PlayerInfo *field_player = NULL;
4041
4042 #if DEBUG_INIT_PLAYER
4043       Debug("game:init:player",
4044             "- looking for field player for player %d ...", i + 1);
4045 #endif
4046
4047       // assign first free player found that is present in the playfield
4048
4049       // first try: look for unmapped playfield player that is not connected
4050       for (j = 0; j < MAX_PLAYERS; j++)
4051         if (field_player == NULL &&
4052             stored_player[j].present &&
4053             !stored_player[j].mapped &&
4054             !stored_player[j].connected)
4055           field_player = &stored_player[j];
4056
4057       // second try: look for *any* unmapped playfield player
4058       for (j = 0; j < MAX_PLAYERS; j++)
4059         if (field_player == NULL &&
4060             stored_player[j].present &&
4061             !stored_player[j].mapped)
4062           field_player = &stored_player[j];
4063
4064       if (field_player != NULL)
4065       {
4066         int jx = field_player->jx, jy = field_player->jy;
4067
4068 #if DEBUG_INIT_PLAYER
4069         Debug("game:init:player", "- found player %d",
4070               field_player->index_nr + 1);
4071 #endif
4072
4073         player->present = FALSE;
4074         player->active = FALSE;
4075
4076         field_player->present = TRUE;
4077         field_player->active = TRUE;
4078
4079         /*
4080         player->initial_element = field_player->initial_element;
4081         player->artwork_element = field_player->artwork_element;
4082
4083         player->block_last_field       = field_player->block_last_field;
4084         player->block_delay_adjustment = field_player->block_delay_adjustment;
4085         */
4086
4087         StorePlayer[jx][jy] = field_player->element_nr;
4088
4089         field_player->jx = field_player->last_jx = jx;
4090         field_player->jy = field_player->last_jy = jy;
4091
4092         if (local_player == player)
4093           local_player = field_player;
4094
4095         map_player_action[field_player->index_nr] = i;
4096
4097         field_player->mapped = TRUE;
4098
4099 #if DEBUG_INIT_PLAYER
4100         Debug("game:init:player", "- map_player_action[%d] == %d",
4101               field_player->index_nr + 1, i + 1);
4102 #endif
4103       }
4104     }
4105
4106     if (player->connected && player->present)
4107       player->mapped = TRUE;
4108   }
4109
4110 #if DEBUG_INIT_PLAYER
4111   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4112 #endif
4113
4114 #else
4115
4116   // check if any connected player was not found in playfield
4117   for (i = 0; i < MAX_PLAYERS; i++)
4118   {
4119     struct PlayerInfo *player = &stored_player[i];
4120
4121     if (player->connected && !player->present)
4122     {
4123       for (j = 0; j < MAX_PLAYERS; j++)
4124       {
4125         struct PlayerInfo *field_player = &stored_player[j];
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128         // assign first free player found that is present in the playfield
4129         if (field_player->present && !field_player->connected)
4130         {
4131           player->present = TRUE;
4132           player->active = TRUE;
4133
4134           field_player->present = FALSE;
4135           field_player->active = FALSE;
4136
4137           player->initial_element = field_player->initial_element;
4138           player->artwork_element = field_player->artwork_element;
4139
4140           player->block_last_field       = field_player->block_last_field;
4141           player->block_delay_adjustment = field_player->block_delay_adjustment;
4142
4143           StorePlayer[jx][jy] = player->element_nr;
4144
4145           player->jx = player->last_jx = jx;
4146           player->jy = player->last_jy = jy;
4147
4148           break;
4149         }
4150       }
4151     }
4152   }
4153 #endif
4154
4155 #if 0
4156   Debug("game:init:player", "local_player->present == %d",
4157         local_player->present);
4158 #endif
4159
4160   // set focus to local player for network games, else to all players
4161   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4162   game.centered_player_nr_next = game.centered_player_nr;
4163   game.set_centered_player = FALSE;
4164   game.set_centered_player_wrap = FALSE;
4165
4166   if (network_playing && tape.recording)
4167   {
4168     // store client dependent player focus when recording network games
4169     tape.centered_player_nr_next = game.centered_player_nr_next;
4170     tape.set_centered_player = TRUE;
4171   }
4172
4173   if (tape.playing)
4174   {
4175     // when playing a tape, eliminate all players who do not participate
4176
4177 #if USE_NEW_PLAYER_ASSIGNMENTS
4178
4179     if (!game.team_mode)
4180     {
4181       for (i = 0; i < MAX_PLAYERS; i++)
4182       {
4183         if (stored_player[i].active &&
4184             !tape.player_participates[map_player_action[i]])
4185         {
4186           struct PlayerInfo *player = &stored_player[i];
4187           int jx = player->jx, jy = player->jy;
4188
4189 #if DEBUG_INIT_PLAYER
4190           Debug("game:init:player", "Removing player %d at (%d, %d)",
4191                 i + 1, jx, jy);
4192 #endif
4193
4194           player->active = FALSE;
4195           StorePlayer[jx][jy] = 0;
4196           Tile[jx][jy] = EL_EMPTY;
4197         }
4198       }
4199     }
4200
4201 #else
4202
4203     for (i = 0; i < MAX_PLAYERS; i++)
4204     {
4205       if (stored_player[i].active &&
4206           !tape.player_participates[i])
4207       {
4208         struct PlayerInfo *player = &stored_player[i];
4209         int jx = player->jx, jy = player->jy;
4210
4211         player->active = FALSE;
4212         StorePlayer[jx][jy] = 0;
4213         Tile[jx][jy] = EL_EMPTY;
4214       }
4215     }
4216 #endif
4217   }
4218   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4219   {
4220     // when in single player mode, eliminate all but the local player
4221
4222     for (i = 0; i < MAX_PLAYERS; i++)
4223     {
4224       struct PlayerInfo *player = &stored_player[i];
4225
4226       if (player->active && player != local_player)
4227       {
4228         int jx = player->jx, jy = player->jy;
4229
4230         player->active = FALSE;
4231         player->present = FALSE;
4232
4233         StorePlayer[jx][jy] = 0;
4234         Tile[jx][jy] = EL_EMPTY;
4235       }
4236     }
4237   }
4238
4239   for (i = 0; i < MAX_PLAYERS; i++)
4240     if (stored_player[i].active)
4241       game.players_still_needed++;
4242
4243   if (level.solved_by_one_player)
4244     game.players_still_needed = 1;
4245
4246   // when recording the game, store which players take part in the game
4247   if (tape.recording)
4248   {
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251       if (stored_player[i].connected)
4252         tape.player_participates[i] = TRUE;
4253 #else
4254     for (i = 0; i < MAX_PLAYERS; i++)
4255       if (stored_player[i].active)
4256         tape.player_participates[i] = TRUE;
4257 #endif
4258   }
4259
4260 #if DEBUG_INIT_PLAYER
4261   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4262 #endif
4263
4264   if (BorderElement == EL_EMPTY)
4265   {
4266     SBX_Left = 0;
4267     SBX_Right = lev_fieldx - SCR_FIELDX;
4268     SBY_Upper = 0;
4269     SBY_Lower = lev_fieldy - SCR_FIELDY;
4270   }
4271   else
4272   {
4273     SBX_Left = -1;
4274     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4275     SBY_Upper = -1;
4276     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4277   }
4278
4279   if (full_lev_fieldx <= SCR_FIELDX)
4280     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4281   if (full_lev_fieldy <= SCR_FIELDY)
4282     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4283
4284   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4285     SBX_Left--;
4286   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4287     SBY_Upper--;
4288
4289   // if local player not found, look for custom element that might create
4290   // the player (make some assumptions about the right custom element)
4291   if (!local_player->present)
4292   {
4293     int start_x = 0, start_y = 0;
4294     int found_rating = 0;
4295     int found_element = EL_UNDEFINED;
4296     int player_nr = local_player->index_nr;
4297
4298     SCAN_PLAYFIELD(x, y)
4299     {
4300       int element = Tile[x][y];
4301       int content;
4302       int xx, yy;
4303       boolean is_player;
4304
4305       if (level.use_start_element[player_nr] &&
4306           level.start_element[player_nr] == element &&
4307           found_rating < 4)
4308       {
4309         start_x = x;
4310         start_y = y;
4311
4312         found_rating = 4;
4313         found_element = element;
4314       }
4315
4316       if (!IS_CUSTOM_ELEMENT(element))
4317         continue;
4318
4319       if (CAN_CHANGE(element))
4320       {
4321         for (i = 0; i < element_info[element].num_change_pages; i++)
4322         {
4323           // check for player created from custom element as single target
4324           content = element_info[element].change_page[i].target_element;
4325           is_player = IS_PLAYER_ELEMENT(content);
4326
4327           if (is_player && (found_rating < 3 ||
4328                             (found_rating == 3 && element < found_element)))
4329           {
4330             start_x = x;
4331             start_y = y;
4332
4333             found_rating = 3;
4334             found_element = element;
4335           }
4336         }
4337       }
4338
4339       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4340       {
4341         // check for player created from custom element as explosion content
4342         content = element_info[element].content.e[xx][yy];
4343         is_player = IS_PLAYER_ELEMENT(content);
4344
4345         if (is_player && (found_rating < 2 ||
4346                           (found_rating == 2 && element < found_element)))
4347         {
4348           start_x = x + xx - 1;
4349           start_y = y + yy - 1;
4350
4351           found_rating = 2;
4352           found_element = element;
4353         }
4354
4355         if (!CAN_CHANGE(element))
4356           continue;
4357
4358         for (i = 0; i < element_info[element].num_change_pages; i++)
4359         {
4360           // check for player created from custom element as extended target
4361           content =
4362             element_info[element].change_page[i].target_content.e[xx][yy];
4363
4364           is_player = IS_PLAYER_ELEMENT(content);
4365
4366           if (is_player && (found_rating < 1 ||
4367                             (found_rating == 1 && element < found_element)))
4368           {
4369             start_x = x + xx - 1;
4370             start_y = y + yy - 1;
4371
4372             found_rating = 1;
4373             found_element = element;
4374           }
4375         }
4376       }
4377     }
4378
4379     scroll_x = SCROLL_POSITION_X(start_x);
4380     scroll_y = SCROLL_POSITION_Y(start_y);
4381   }
4382   else
4383   {
4384     scroll_x = SCROLL_POSITION_X(local_player->jx);
4385     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4386   }
4387
4388   // !!! FIX THIS (START) !!!
4389   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4390   {
4391     InitGameEngine_EM();
4392   }
4393   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4394   {
4395     InitGameEngine_SP();
4396   }
4397   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4398   {
4399     InitGameEngine_MM();
4400   }
4401   else
4402   {
4403     DrawLevel(REDRAW_FIELD);
4404     DrawAllPlayers();
4405
4406     // after drawing the level, correct some elements
4407     if (game.timegate_time_left == 0)
4408       CloseAllOpenTimegates();
4409   }
4410
4411   // blit playfield from scroll buffer to normal back buffer for fading in
4412   BlitScreenToBitmap(backbuffer);
4413   // !!! FIX THIS (END) !!!
4414
4415   DrawMaskedBorder(fade_mask);
4416
4417   FadeIn(fade_mask);
4418
4419 #if 1
4420   // full screen redraw is required at this point in the following cases:
4421   // - special editor door undrawn when game was started from level editor
4422   // - drawing area (playfield) was changed and has to be removed completely
4423   redraw_mask = REDRAW_ALL;
4424   BackToFront();
4425 #endif
4426
4427   if (!game.restart_level)
4428   {
4429     // copy default game door content to main double buffer
4430
4431     // !!! CHECK AGAIN !!!
4432     SetPanelBackground();
4433     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4434     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4435   }
4436
4437   SetPanelBackground();
4438   SetDrawBackgroundMask(REDRAW_DOOR_1);
4439
4440   UpdateAndDisplayGameControlValues();
4441
4442   if (!game.restart_level)
4443   {
4444     UnmapGameButtons();
4445     UnmapTapeButtons();
4446
4447     FreeGameButtons();
4448     CreateGameButtons();
4449
4450     MapGameButtons();
4451     MapTapeButtons();
4452
4453     // copy actual game door content to door double buffer for OpenDoor()
4454     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4455
4456     OpenDoor(DOOR_OPEN_ALL);
4457
4458     KeyboardAutoRepeatOffUnlessAutoplay();
4459
4460 #if DEBUG_INIT_PLAYER
4461     DebugPrintPlayerStatus("Player status (final)");
4462 #endif
4463   }
4464
4465   UnmapAllGadgets();
4466
4467   MapGameButtons();
4468   MapTapeButtons();
4469
4470   if (!game.restart_level && !tape.playing)
4471   {
4472     LevelStats_incPlayed(level_nr);
4473
4474     SaveLevelSetup_SeriesInfo();
4475   }
4476
4477   game.restart_level = FALSE;
4478   game.restart_game_message = NULL;
4479
4480   game.request_active = FALSE;
4481   game.request_active_or_moving = FALSE;
4482
4483   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4484     InitGameActions_MM();
4485
4486   SaveEngineSnapshotToListInitial();
4487
4488   if (!game.restart_level)
4489   {
4490     PlaySound(SND_GAME_STARTING);
4491
4492     if (setup.sound_music)
4493       PlayLevelMusic();
4494   }
4495
4496   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4497 }
4498
4499 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4500                         int actual_player_x, int actual_player_y)
4501 {
4502   // this is used for non-R'n'D game engines to update certain engine values
4503
4504   // needed to determine if sounds are played within the visible screen area
4505   scroll_x = actual_scroll_x;
4506   scroll_y = actual_scroll_y;
4507
4508   // needed to get player position for "follow finger" playing input method
4509   local_player->jx = actual_player_x;
4510   local_player->jy = actual_player_y;
4511 }
4512
4513 void InitMovDir(int x, int y)
4514 {
4515   int i, element = Tile[x][y];
4516   static int xy[4][2] =
4517   {
4518     {  0, +1 },
4519     { +1,  0 },
4520     {  0, -1 },
4521     { -1,  0 }
4522   };
4523   static int direction[3][4] =
4524   {
4525     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4526     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4527     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4528   };
4529
4530   switch (element)
4531   {
4532     case EL_BUG_RIGHT:
4533     case EL_BUG_UP:
4534     case EL_BUG_LEFT:
4535     case EL_BUG_DOWN:
4536       Tile[x][y] = EL_BUG;
4537       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4538       break;
4539
4540     case EL_SPACESHIP_RIGHT:
4541     case EL_SPACESHIP_UP:
4542     case EL_SPACESHIP_LEFT:
4543     case EL_SPACESHIP_DOWN:
4544       Tile[x][y] = EL_SPACESHIP;
4545       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4546       break;
4547
4548     case EL_BD_BUTTERFLY_RIGHT:
4549     case EL_BD_BUTTERFLY_UP:
4550     case EL_BD_BUTTERFLY_LEFT:
4551     case EL_BD_BUTTERFLY_DOWN:
4552       Tile[x][y] = EL_BD_BUTTERFLY;
4553       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4554       break;
4555
4556     case EL_BD_FIREFLY_RIGHT:
4557     case EL_BD_FIREFLY_UP:
4558     case EL_BD_FIREFLY_LEFT:
4559     case EL_BD_FIREFLY_DOWN:
4560       Tile[x][y] = EL_BD_FIREFLY;
4561       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4562       break;
4563
4564     case EL_PACMAN_RIGHT:
4565     case EL_PACMAN_UP:
4566     case EL_PACMAN_LEFT:
4567     case EL_PACMAN_DOWN:
4568       Tile[x][y] = EL_PACMAN;
4569       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4570       break;
4571
4572     case EL_YAMYAM_LEFT:
4573     case EL_YAMYAM_RIGHT:
4574     case EL_YAMYAM_UP:
4575     case EL_YAMYAM_DOWN:
4576       Tile[x][y] = EL_YAMYAM;
4577       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4578       break;
4579
4580     case EL_SP_SNIKSNAK:
4581       MovDir[x][y] = MV_UP;
4582       break;
4583
4584     case EL_SP_ELECTRON:
4585       MovDir[x][y] = MV_LEFT;
4586       break;
4587
4588     case EL_MOLE_LEFT:
4589     case EL_MOLE_RIGHT:
4590     case EL_MOLE_UP:
4591     case EL_MOLE_DOWN:
4592       Tile[x][y] = EL_MOLE;
4593       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4594       break;
4595
4596     case EL_SPRING_LEFT:
4597     case EL_SPRING_RIGHT:
4598       Tile[x][y] = EL_SPRING;
4599       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4600       break;
4601
4602     default:
4603       if (IS_CUSTOM_ELEMENT(element))
4604       {
4605         struct ElementInfo *ei = &element_info[element];
4606         int move_direction_initial = ei->move_direction_initial;
4607         int move_pattern = ei->move_pattern;
4608
4609         if (move_direction_initial == MV_START_PREVIOUS)
4610         {
4611           if (MovDir[x][y] != MV_NONE)
4612             return;
4613
4614           move_direction_initial = MV_START_AUTOMATIC;
4615         }
4616
4617         if (move_direction_initial == MV_START_RANDOM)
4618           MovDir[x][y] = 1 << RND(4);
4619         else if (move_direction_initial & MV_ANY_DIRECTION)
4620           MovDir[x][y] = move_direction_initial;
4621         else if (move_pattern == MV_ALL_DIRECTIONS ||
4622                  move_pattern == MV_TURNING_LEFT ||
4623                  move_pattern == MV_TURNING_RIGHT ||
4624                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4625                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4626                  move_pattern == MV_TURNING_RANDOM)
4627           MovDir[x][y] = 1 << RND(4);
4628         else if (move_pattern == MV_HORIZONTAL)
4629           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4630         else if (move_pattern == MV_VERTICAL)
4631           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4632         else if (move_pattern & MV_ANY_DIRECTION)
4633           MovDir[x][y] = element_info[element].move_pattern;
4634         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4635                  move_pattern == MV_ALONG_RIGHT_SIDE)
4636         {
4637           // use random direction as default start direction
4638           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4639             MovDir[x][y] = 1 << RND(4);
4640
4641           for (i = 0; i < NUM_DIRECTIONS; i++)
4642           {
4643             int x1 = x + xy[i][0];
4644             int y1 = y + xy[i][1];
4645
4646             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4647             {
4648               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4649                 MovDir[x][y] = direction[0][i];
4650               else
4651                 MovDir[x][y] = direction[1][i];
4652
4653               break;
4654             }
4655           }
4656         }                
4657       }
4658       else
4659       {
4660         MovDir[x][y] = 1 << RND(4);
4661
4662         if (element != EL_BUG &&
4663             element != EL_SPACESHIP &&
4664             element != EL_BD_BUTTERFLY &&
4665             element != EL_BD_FIREFLY)
4666           break;
4667
4668         for (i = 0; i < NUM_DIRECTIONS; i++)
4669         {
4670           int x1 = x + xy[i][0];
4671           int y1 = y + xy[i][1];
4672
4673           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4674           {
4675             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4676             {
4677               MovDir[x][y] = direction[0][i];
4678               break;
4679             }
4680             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4681                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4682             {
4683               MovDir[x][y] = direction[1][i];
4684               break;
4685             }
4686           }
4687         }
4688       }
4689       break;
4690   }
4691
4692   GfxDir[x][y] = MovDir[x][y];
4693 }
4694
4695 void InitAmoebaNr(int x, int y)
4696 {
4697   int i;
4698   int group_nr = AmoebaNeighbourNr(x, y);
4699
4700   if (group_nr == 0)
4701   {
4702     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4703     {
4704       if (AmoebaCnt[i] == 0)
4705       {
4706         group_nr = i;
4707         break;
4708       }
4709     }
4710   }
4711
4712   AmoebaNr[x][y] = group_nr;
4713   AmoebaCnt[group_nr]++;
4714   AmoebaCnt2[group_nr]++;
4715 }
4716
4717 static void LevelSolved_SetFinalGameValues(void)
4718 {
4719   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4720   game.score_time_final = (level.use_step_counter ? TimePlayed :
4721                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4722
4723   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4724                       game_em.lev->score :
4725                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4726                       game_mm.score :
4727                       game.score);
4728
4729   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4730                        MM_HEALTH(game_mm.laser_overload_value) :
4731                        game.health);
4732
4733   game.LevelSolved_CountingTime = game.time_final;
4734   game.LevelSolved_CountingScore = game.score_final;
4735   game.LevelSolved_CountingHealth = game.health_final;
4736 }
4737
4738 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4739 {
4740   game.LevelSolved_CountingTime = time;
4741   game.LevelSolved_CountingScore = score;
4742   game.LevelSolved_CountingHealth = health;
4743
4744   game_panel_controls[GAME_PANEL_TIME].value = time;
4745   game_panel_controls[GAME_PANEL_SCORE].value = score;
4746   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4747
4748   DisplayGameControlValues();
4749 }
4750
4751 static void LevelSolved(void)
4752 {
4753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4754       game.players_still_needed > 0)
4755     return;
4756
4757   game.LevelSolved = TRUE;
4758   game.GameOver = TRUE;
4759
4760   // needed here to display correct panel values while player walks into exit
4761   LevelSolved_SetFinalGameValues();
4762 }
4763
4764 void GameWon(void)
4765 {
4766   static int time_count_steps;
4767   static int time, time_final;
4768   static float score, score_final; // needed for time score < 10 for 10 seconds
4769   static int health, health_final;
4770   static int game_over_delay_1 = 0;
4771   static int game_over_delay_2 = 0;
4772   static int game_over_delay_3 = 0;
4773   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4774   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4775
4776   if (!game.LevelSolved_GameWon)
4777   {
4778     int i;
4779
4780     // do not start end game actions before the player stops moving (to exit)
4781     if (local_player->active && local_player->MovPos)
4782       return;
4783
4784     // calculate final game values after player finished walking into exit
4785     LevelSolved_SetFinalGameValues();
4786
4787     game.LevelSolved_GameWon = TRUE;
4788     game.LevelSolved_SaveTape = tape.recording;
4789     game.LevelSolved_SaveScore = !tape.playing;
4790
4791     if (!tape.playing)
4792     {
4793       LevelStats_incSolved(level_nr);
4794
4795       SaveLevelSetup_SeriesInfo();
4796     }
4797
4798     if (tape.auto_play)         // tape might already be stopped here
4799       tape.auto_play_level_solved = TRUE;
4800
4801     TapeStop();
4802
4803     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4804     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4805     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4806
4807     time = time_final = game.time_final;
4808     score = score_final = game.score_final;
4809     health = health_final = game.health_final;
4810
4811     // update game panel values before (delayed) counting of score (if any)
4812     LevelSolved_DisplayFinalGameValues(time, score, health);
4813
4814     // if level has time score defined, calculate new final game values
4815     if (time_score > 0)
4816     {
4817       int time_final_max = 999;
4818       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4819       int time_frames = 0;
4820       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4821       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4822
4823       if (TimeLeft > 0)
4824       {
4825         time_final = 0;
4826         time_frames = time_frames_left;
4827       }
4828       else if (game.no_time_limit && TimePlayed < time_final_max)
4829       {
4830         time_final = time_final_max;
4831         time_frames = time_frames_final_max - time_frames_played;
4832       }
4833
4834       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4835
4836       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4837
4838       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4839       {
4840         health_final = 0;
4841         score_final += health * time_score;
4842       }
4843
4844       game.score_final = score_final;
4845       game.health_final = health_final;
4846     }
4847
4848     // if not counting score after game, immediately update game panel values
4849     if (level_editor_test_game || !setup.count_score_after_game)
4850     {
4851       time = time_final;
4852       score = score_final;
4853
4854       LevelSolved_DisplayFinalGameValues(time, score, health);
4855     }
4856
4857     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4858     {
4859       // check if last player has left the level
4860       if (game.exit_x >= 0 &&
4861           game.exit_y >= 0)
4862       {
4863         int x = game.exit_x;
4864         int y = game.exit_y;
4865         int element = Tile[x][y];
4866
4867         // close exit door after last player
4868         if ((game.all_players_gone &&
4869              (element == EL_EXIT_OPEN ||
4870               element == EL_SP_EXIT_OPEN ||
4871               element == EL_STEEL_EXIT_OPEN)) ||
4872             element == EL_EM_EXIT_OPEN ||
4873             element == EL_EM_STEEL_EXIT_OPEN)
4874         {
4875
4876           Tile[x][y] =
4877             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4878              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4879              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4880              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4881              EL_EM_STEEL_EXIT_CLOSING);
4882
4883           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4884         }
4885
4886         // player disappears
4887         DrawLevelField(x, y);
4888       }
4889
4890       for (i = 0; i < MAX_PLAYERS; i++)
4891       {
4892         struct PlayerInfo *player = &stored_player[i];
4893
4894         if (player->present)
4895         {
4896           RemovePlayer(player);
4897
4898           // player disappears
4899           DrawLevelField(player->jx, player->jy);
4900         }
4901       }
4902     }
4903
4904     PlaySound(SND_GAME_WINNING);
4905   }
4906
4907   if (setup.count_score_after_game)
4908   {
4909     if (time != time_final)
4910     {
4911       if (game_over_delay_1 > 0)
4912       {
4913         game_over_delay_1--;
4914
4915         return;
4916       }
4917
4918       int time_to_go = ABS(time_final - time);
4919       int time_count_dir = (time < time_final ? +1 : -1);
4920
4921       if (time_to_go < time_count_steps)
4922         time_count_steps = 1;
4923
4924       time  += time_count_steps * time_count_dir;
4925       score += time_count_steps * time_score;
4926
4927       // set final score to correct rounding differences after counting score
4928       if (time == time_final)
4929         score = score_final;
4930
4931       LevelSolved_DisplayFinalGameValues(time, score, health);
4932
4933       if (time == time_final)
4934         StopSound(SND_GAME_LEVELTIME_BONUS);
4935       else if (setup.sound_loops)
4936         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4937       else
4938         PlaySound(SND_GAME_LEVELTIME_BONUS);
4939
4940       return;
4941     }
4942
4943     if (health != health_final)
4944     {
4945       if (game_over_delay_2 > 0)
4946       {
4947         game_over_delay_2--;
4948
4949         return;
4950       }
4951
4952       int health_count_dir = (health < health_final ? +1 : -1);
4953
4954       health += health_count_dir;
4955       score  += time_score;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (health == health_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968   }
4969
4970   game.panel.active = FALSE;
4971
4972   if (game_over_delay_3 > 0)
4973   {
4974     game_over_delay_3--;
4975
4976     return;
4977   }
4978
4979   GameEnd();
4980 }
4981
4982 void GameEnd(void)
4983 {
4984   // used instead of "level_nr" (needed for network games)
4985   int last_level_nr = levelset.level_nr;
4986   boolean tape_saved = FALSE;
4987
4988   game.LevelSolved_GameEnd = TRUE;
4989
4990   if (game.LevelSolved_SaveTape)
4991   {
4992     // make sure that request dialog to save tape does not open door again
4993     if (!global.use_envelope_request)
4994       CloseDoor(DOOR_CLOSE_1);
4995
4996     // ask to save tape
4997     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4998
4999     // set unique basename for score tape (also saved in high score table)
5000     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5001   }
5002
5003   // if no tape is to be saved, close both doors simultaneously
5004   CloseDoor(DOOR_CLOSE_ALL);
5005
5006   if (level_editor_test_game)
5007   {
5008     SetGameStatus(GAME_MODE_MAIN);
5009
5010     DrawMainMenu();
5011
5012     return;
5013   }
5014
5015   if (!game.LevelSolved_SaveScore)
5016   {
5017     SetGameStatus(GAME_MODE_MAIN);
5018
5019     DrawMainMenu();
5020
5021     return;
5022   }
5023
5024   if (level_nr == leveldir_current->handicap_level)
5025   {
5026     leveldir_current->handicap_level++;
5027
5028     SaveLevelSetup_SeriesInfo();
5029   }
5030
5031   // save score and score tape before potentially erasing tape below
5032   NewHighScore(last_level_nr, tape_saved);
5033
5034   if (setup.increment_levels &&
5035       level_nr < leveldir_current->last_level &&
5036       !network_playing)
5037   {
5038     level_nr++;         // advance to next level
5039     TapeErase();        // start with empty tape
5040
5041     if (setup.auto_play_next_level)
5042     {
5043       LoadLevel(level_nr);
5044
5045       SaveLevelSetup_SeriesInfo();
5046     }
5047   }
5048
5049   if (scores.last_added >= 0 && setup.show_scores_after_game)
5050   {
5051     SetGameStatus(GAME_MODE_SCORES);
5052
5053     DrawHallOfFame(last_level_nr);
5054   }
5055   else if (setup.auto_play_next_level && setup.increment_levels &&
5056            last_level_nr < leveldir_current->last_level &&
5057            !network_playing)
5058   {
5059     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5060   }
5061   else
5062   {
5063     SetGameStatus(GAME_MODE_MAIN);
5064
5065     DrawMainMenu();
5066   }
5067 }
5068
5069 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5070                          boolean one_score_entry_per_name)
5071 {
5072   int i;
5073
5074   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5075     return -1;
5076
5077   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5078   {
5079     struct ScoreEntry *entry = &list->entry[i];
5080     boolean score_is_better = (new_entry->score >  entry->score);
5081     boolean score_is_equal  = (new_entry->score == entry->score);
5082     boolean time_is_better  = (new_entry->time  <  entry->time);
5083     boolean time_is_equal   = (new_entry->time  == entry->time);
5084     boolean better_by_score = (score_is_better ||
5085                                (score_is_equal && time_is_better));
5086     boolean better_by_time  = (time_is_better ||
5087                                (time_is_equal && score_is_better));
5088     boolean is_better = (level.rate_time_over_score ? better_by_time :
5089                          better_by_score);
5090     boolean entry_is_empty = (entry->score == 0 &&
5091                               entry->time == 0);
5092
5093     // prevent adding server score entries if also existing in local score file
5094     // (special case: historic score entries have an empty tape basename entry)
5095     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5096         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5097       return -1;
5098
5099     if (is_better || entry_is_empty)
5100     {
5101       // player has made it to the hall of fame
5102
5103       if (i < MAX_SCORE_ENTRIES - 1)
5104       {
5105         int m = MAX_SCORE_ENTRIES - 1;
5106         int l;
5107
5108         if (one_score_entry_per_name)
5109         {
5110           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5111             if (strEqual(list->entry[l].name, new_entry->name))
5112               m = l;
5113
5114           if (m == i)   // player's new highscore overwrites his old one
5115             goto put_into_list;
5116         }
5117
5118         for (l = m; l > i; l--)
5119           list->entry[l] = list->entry[l - 1];
5120       }
5121
5122       put_into_list:
5123
5124       *entry = *new_entry;
5125
5126       return i;
5127     }
5128     else if (one_score_entry_per_name &&
5129              strEqual(entry->name, new_entry->name))
5130     {
5131       // player already in high score list with better score or time
5132
5133       return -1;
5134     }
5135   }
5136
5137   return -1;
5138 }
5139
5140 void NewHighScore(int level_nr, boolean tape_saved)
5141 {
5142   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5143   boolean one_per_name = FALSE;
5144
5145   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5146   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5147
5148   new_entry.score = game.score_final;
5149   new_entry.time = game.score_time_final;
5150
5151   LoadScore(level_nr);
5152
5153   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5154
5155   if (scores.last_added < 0)
5156     return;
5157
5158   SaveScore(level_nr);
5159
5160   // store last added local score entry (before merging server scores)
5161   scores.last_added_local = scores.last_added;
5162
5163   if (!game.LevelSolved_SaveTape)
5164     return;
5165
5166   SaveScoreTape(level_nr);
5167
5168   if (setup.ask_for_using_api_server)
5169   {
5170     setup.use_api_server =
5171       Request("Upload your score and tape to the high score server?", REQ_ASK);
5172
5173     if (!setup.use_api_server)
5174       Request("Not using high score server! Use setup menu to enable again!",
5175               REQ_CONFIRM);
5176
5177     runtime.use_api_server = setup.use_api_server;
5178
5179     // after asking for using API server once, do not ask again
5180     setup.ask_for_using_api_server = FALSE;
5181
5182     SaveSetup_ServerSetup();
5183   }
5184
5185   SaveServerScore(level_nr, tape_saved);
5186 }
5187
5188 void MergeServerScore(void)
5189 {
5190   struct ScoreEntry last_added_entry;
5191   boolean one_per_name = FALSE;
5192   int i;
5193
5194   if (scores.last_added >= 0)
5195     last_added_entry = scores.entry[scores.last_added];
5196
5197   for (i = 0; i < server_scores.num_entries; i++)
5198   {
5199     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5200
5201     if (pos >= 0 && pos <= scores.last_added)
5202       scores.last_added++;
5203   }
5204
5205   if (scores.last_added >= MAX_SCORE_ENTRIES)
5206   {
5207     scores.last_added = MAX_SCORE_ENTRIES - 1;
5208     scores.force_last_added = TRUE;
5209
5210     scores.entry[scores.last_added] = last_added_entry;
5211   }
5212 }
5213
5214 static int getElementMoveStepsizeExt(int x, int y, int direction)
5215 {
5216   int element = Tile[x][y];
5217   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5218   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5219   int horiz_move = (dx != 0);
5220   int sign = (horiz_move ? dx : dy);
5221   int step = sign * element_info[element].move_stepsize;
5222
5223   // special values for move stepsize for spring and things on conveyor belt
5224   if (horiz_move)
5225   {
5226     if (CAN_FALL(element) &&
5227         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5228       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5229     else if (element == EL_SPRING)
5230       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5231   }
5232
5233   return step;
5234 }
5235
5236 static int getElementMoveStepsize(int x, int y)
5237 {
5238   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5239 }
5240
5241 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5242 {
5243   if (player->GfxAction != action || player->GfxDir != dir)
5244   {
5245     player->GfxAction = action;
5246     player->GfxDir = dir;
5247     player->Frame = 0;
5248     player->StepFrame = 0;
5249   }
5250 }
5251
5252 static void ResetGfxFrame(int x, int y)
5253 {
5254   // profiling showed that "autotest" spends 10~20% of its time in this function
5255   if (DrawingDeactivatedField())
5256     return;
5257
5258   int element = Tile[x][y];
5259   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5260
5261   if (graphic_info[graphic].anim_global_sync)
5262     GfxFrame[x][y] = FrameCounter;
5263   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5264     GfxFrame[x][y] = CustomValue[x][y];
5265   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5266     GfxFrame[x][y] = element_info[element].collect_score;
5267   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5268     GfxFrame[x][y] = ChangeDelay[x][y];
5269 }
5270
5271 static void ResetGfxAnimation(int x, int y)
5272 {
5273   GfxAction[x][y] = ACTION_DEFAULT;
5274   GfxDir[x][y] = MovDir[x][y];
5275   GfxFrame[x][y] = 0;
5276
5277   ResetGfxFrame(x, y);
5278 }
5279
5280 static void ResetRandomAnimationValue(int x, int y)
5281 {
5282   GfxRandom[x][y] = INIT_GFX_RANDOM();
5283 }
5284
5285 static void InitMovingField(int x, int y, int direction)
5286 {
5287   int element = Tile[x][y];
5288   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5289   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5290   int newx = x + dx;
5291   int newy = y + dy;
5292   boolean is_moving_before, is_moving_after;
5293
5294   // check if element was/is moving or being moved before/after mode change
5295   is_moving_before = (WasJustMoving[x][y] != 0);
5296   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5297
5298   // reset animation only for moving elements which change direction of moving
5299   // or which just started or stopped moving
5300   // (else CEs with property "can move" / "not moving" are reset each frame)
5301   if (is_moving_before != is_moving_after ||
5302       direction != MovDir[x][y])
5303     ResetGfxAnimation(x, y);
5304
5305   MovDir[x][y] = direction;
5306   GfxDir[x][y] = direction;
5307
5308   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5309                      direction == MV_DOWN && CAN_FALL(element) ?
5310                      ACTION_FALLING : ACTION_MOVING);
5311
5312   // this is needed for CEs with property "can move" / "not moving"
5313
5314   if (is_moving_after)
5315   {
5316     if (Tile[newx][newy] == EL_EMPTY)
5317       Tile[newx][newy] = EL_BLOCKED;
5318
5319     MovDir[newx][newy] = MovDir[x][y];
5320
5321     CustomValue[newx][newy] = CustomValue[x][y];
5322
5323     GfxFrame[newx][newy] = GfxFrame[x][y];
5324     GfxRandom[newx][newy] = GfxRandom[x][y];
5325     GfxAction[newx][newy] = GfxAction[x][y];
5326     GfxDir[newx][newy] = GfxDir[x][y];
5327   }
5328 }
5329
5330 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5331 {
5332   int direction = MovDir[x][y];
5333   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5334   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5335
5336   *goes_to_x = newx;
5337   *goes_to_y = newy;
5338 }
5339
5340 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5341 {
5342   int oldx = x, oldy = y;
5343   int direction = MovDir[x][y];
5344
5345   if (direction == MV_LEFT)
5346     oldx++;
5347   else if (direction == MV_RIGHT)
5348     oldx--;
5349   else if (direction == MV_UP)
5350     oldy++;
5351   else if (direction == MV_DOWN)
5352     oldy--;
5353
5354   *comes_from_x = oldx;
5355   *comes_from_y = oldy;
5356 }
5357
5358 static int MovingOrBlocked2Element(int x, int y)
5359 {
5360   int element = Tile[x][y];
5361
5362   if (element == EL_BLOCKED)
5363   {
5364     int oldx, oldy;
5365
5366     Blocked2Moving(x, y, &oldx, &oldy);
5367     return Tile[oldx][oldy];
5368   }
5369   else
5370     return element;
5371 }
5372
5373 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5374 {
5375   // like MovingOrBlocked2Element(), but if element is moving
5376   // and (x,y) is the field the moving element is just leaving,
5377   // return EL_BLOCKED instead of the element value
5378   int element = Tile[x][y];
5379
5380   if (IS_MOVING(x, y))
5381   {
5382     if (element == EL_BLOCKED)
5383     {
5384       int oldx, oldy;
5385
5386       Blocked2Moving(x, y, &oldx, &oldy);
5387       return Tile[oldx][oldy];
5388     }
5389     else
5390       return EL_BLOCKED;
5391   }
5392   else
5393     return element;
5394 }
5395
5396 static void RemoveField(int x, int y)
5397 {
5398   Tile[x][y] = EL_EMPTY;
5399
5400   MovPos[x][y] = 0;
5401   MovDir[x][y] = 0;
5402   MovDelay[x][y] = 0;
5403
5404   CustomValue[x][y] = 0;
5405
5406   AmoebaNr[x][y] = 0;
5407   ChangeDelay[x][y] = 0;
5408   ChangePage[x][y] = -1;
5409   Pushed[x][y] = FALSE;
5410
5411   GfxElement[x][y] = EL_UNDEFINED;
5412   GfxAction[x][y] = ACTION_DEFAULT;
5413   GfxDir[x][y] = MV_NONE;
5414 }
5415
5416 static void RemoveMovingField(int x, int y)
5417 {
5418   int oldx = x, oldy = y, newx = x, newy = y;
5419   int element = Tile[x][y];
5420   int next_element = EL_UNDEFINED;
5421
5422   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5423     return;
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     Moving2Blocked(x, y, &newx, &newy);
5428
5429     if (Tile[newx][newy] != EL_BLOCKED)
5430     {
5431       // element is moving, but target field is not free (blocked), but
5432       // already occupied by something different (example: acid pool);
5433       // in this case, only remove the moving field, but not the target
5434
5435       RemoveField(oldx, oldy);
5436
5437       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5438
5439       TEST_DrawLevelField(oldx, oldy);
5440
5441       return;
5442     }
5443   }
5444   else if (element == EL_BLOCKED)
5445   {
5446     Blocked2Moving(x, y, &oldx, &oldy);
5447     if (!IS_MOVING(oldx, oldy))
5448       return;
5449   }
5450
5451   if (element == EL_BLOCKED &&
5452       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5453        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5454        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5455        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5456        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5457        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5458     next_element = get_next_element(Tile[oldx][oldy]);
5459
5460   RemoveField(oldx, oldy);
5461   RemoveField(newx, newy);
5462
5463   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5464
5465   if (next_element != EL_UNDEFINED)
5466     Tile[oldx][oldy] = next_element;
5467
5468   TEST_DrawLevelField(oldx, oldy);
5469   TEST_DrawLevelField(newx, newy);
5470 }
5471
5472 void DrawDynamite(int x, int y)
5473 {
5474   int sx = SCREENX(x), sy = SCREENY(y);
5475   int graphic = el2img(Tile[x][y]);
5476   int frame;
5477
5478   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5479     return;
5480
5481   if (IS_WALKABLE_INSIDE(Back[x][y]))
5482     return;
5483
5484   if (Back[x][y])
5485     DrawLevelElement(x, y, Back[x][y]);
5486   else if (Store[x][y])
5487     DrawLevelElement(x, y, Store[x][y]);
5488   else if (game.use_masked_elements)
5489     DrawLevelElement(x, y, EL_EMPTY);
5490
5491   frame = getGraphicAnimationFrameXY(graphic, x, y);
5492
5493   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5494     DrawGraphicThruMask(sx, sy, graphic, frame);
5495   else
5496     DrawGraphic(sx, sy, graphic, frame);
5497 }
5498
5499 static void CheckDynamite(int x, int y)
5500 {
5501   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5502   {
5503     MovDelay[x][y]--;
5504
5505     if (MovDelay[x][y] != 0)
5506     {
5507       DrawDynamite(x, y);
5508       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5509
5510       return;
5511     }
5512   }
5513
5514   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5515
5516   Bang(x, y);
5517 }
5518
5519 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5520 {
5521   boolean num_checked_players = 0;
5522   int i;
5523
5524   for (i = 0; i < MAX_PLAYERS; i++)
5525   {
5526     if (stored_player[i].active)
5527     {
5528       int sx = stored_player[i].jx;
5529       int sy = stored_player[i].jy;
5530
5531       if (num_checked_players == 0)
5532       {
5533         *sx1 = *sx2 = sx;
5534         *sy1 = *sy2 = sy;
5535       }
5536       else
5537       {
5538         *sx1 = MIN(*sx1, sx);
5539         *sy1 = MIN(*sy1, sy);
5540         *sx2 = MAX(*sx2, sx);
5541         *sy2 = MAX(*sy2, sy);
5542       }
5543
5544       num_checked_players++;
5545     }
5546   }
5547 }
5548
5549 static boolean checkIfAllPlayersFitToScreen_RND(void)
5550 {
5551   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5552
5553   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5554
5555   return (sx2 - sx1 < SCR_FIELDX &&
5556           sy2 - sy1 < SCR_FIELDY);
5557 }
5558
5559 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5560 {
5561   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5562
5563   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5564
5565   *sx = (sx1 + sx2) / 2;
5566   *sy = (sy1 + sy2) / 2;
5567 }
5568
5569 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5570                                boolean center_screen, boolean quick_relocation)
5571 {
5572   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5573   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5574   boolean no_delay = (tape.warp_forward);
5575   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5576   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5577   int new_scroll_x, new_scroll_y;
5578
5579   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5580   {
5581     // case 1: quick relocation inside visible screen (without scrolling)
5582
5583     RedrawPlayfield();
5584
5585     return;
5586   }
5587
5588   if (!level.shifted_relocation || center_screen)
5589   {
5590     // relocation _with_ centering of screen
5591
5592     new_scroll_x = SCROLL_POSITION_X(x);
5593     new_scroll_y = SCROLL_POSITION_Y(y);
5594   }
5595   else
5596   {
5597     // relocation _without_ centering of screen
5598
5599     int center_scroll_x = SCROLL_POSITION_X(old_x);
5600     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5601     int offset_x = x + (scroll_x - center_scroll_x);
5602     int offset_y = y + (scroll_y - center_scroll_y);
5603
5604     // for new screen position, apply previous offset to center position
5605     new_scroll_x = SCROLL_POSITION_X(offset_x);
5606     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5607   }
5608
5609   if (quick_relocation)
5610   {
5611     // case 2: quick relocation (redraw without visible scrolling)
5612
5613     scroll_x = new_scroll_x;
5614     scroll_y = new_scroll_y;
5615
5616     RedrawPlayfield();
5617
5618     return;
5619   }
5620
5621   // case 3: visible relocation (with scrolling to new position)
5622
5623   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5624
5625   SetVideoFrameDelay(wait_delay_value);
5626
5627   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5628   {
5629     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5630     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5631
5632     if (dx == 0 && dy == 0)             // no scrolling needed at all
5633       break;
5634
5635     scroll_x -= dx;
5636     scroll_y -= dy;
5637
5638     // set values for horizontal/vertical screen scrolling (half tile size)
5639     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5640     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5641     int pos_x = dx * TILEX / 2;
5642     int pos_y = dy * TILEY / 2;
5643     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5644     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5645
5646     ScrollLevel(dx, dy);
5647     DrawAllPlayers();
5648
5649     // scroll in two steps of half tile size to make things smoother
5650     BlitScreenToBitmapExt_RND(window, fx, fy);
5651
5652     // scroll second step to align at full tile size
5653     BlitScreenToBitmap(window);
5654   }
5655
5656   DrawAllPlayers();
5657   BackToFront();
5658
5659   SetVideoFrameDelay(frame_delay_value_old);
5660 }
5661
5662 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5663 {
5664   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5665   int player_nr = GET_PLAYER_NR(el_player);
5666   struct PlayerInfo *player = &stored_player[player_nr];
5667   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5668   boolean no_delay = (tape.warp_forward);
5669   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5670   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5671   int old_jx = player->jx;
5672   int old_jy = player->jy;
5673   int old_element = Tile[old_jx][old_jy];
5674   int element = Tile[jx][jy];
5675   boolean player_relocated = (old_jx != jx || old_jy != jy);
5676
5677   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5678   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5679   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5680   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5681   int leave_side_horiz = move_dir_horiz;
5682   int leave_side_vert  = move_dir_vert;
5683   int enter_side = enter_side_horiz | enter_side_vert;
5684   int leave_side = leave_side_horiz | leave_side_vert;
5685
5686   if (player->buried)           // do not reanimate dead player
5687     return;
5688
5689   if (!player_relocated)        // no need to relocate the player
5690     return;
5691
5692   if (IS_PLAYER(jx, jy))        // player already placed at new position
5693   {
5694     RemoveField(jx, jy);        // temporarily remove newly placed player
5695     DrawLevelField(jx, jy);
5696   }
5697
5698   if (player->present)
5699   {
5700     while (player->MovPos)
5701     {
5702       ScrollPlayer(player, SCROLL_GO_ON);
5703       ScrollScreen(NULL, SCROLL_GO_ON);
5704
5705       AdvanceFrameAndPlayerCounters(player->index_nr);
5706
5707       DrawPlayer(player);
5708
5709       BackToFront_WithFrameDelay(wait_delay_value);
5710     }
5711
5712     DrawPlayer(player);         // needed here only to cleanup last field
5713     DrawLevelField(player->jx, player->jy);     // remove player graphic
5714
5715     player->is_moving = FALSE;
5716   }
5717
5718   if (IS_CUSTOM_ELEMENT(old_element))
5719     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5720                                CE_LEFT_BY_PLAYER,
5721                                player->index_bit, leave_side);
5722
5723   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5724                                       CE_PLAYER_LEAVES_X,
5725                                       player->index_bit, leave_side);
5726
5727   Tile[jx][jy] = el_player;
5728   InitPlayerField(jx, jy, el_player, TRUE);
5729
5730   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5731      possible that the relocation target field did not contain a player element,
5732      but a walkable element, to which the new player was relocated -- in this
5733      case, restore that (already initialized!) element on the player field */
5734   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5735   {
5736     Tile[jx][jy] = element;     // restore previously existing element
5737   }
5738
5739   // only visually relocate centered player
5740   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5741                      FALSE, level.instant_relocation);
5742
5743   TestIfPlayerTouchesBadThing(jx, jy);
5744   TestIfPlayerTouchesCustomElement(jx, jy);
5745
5746   if (IS_CUSTOM_ELEMENT(element))
5747     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5748                                player->index_bit, enter_side);
5749
5750   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5751                                       player->index_bit, enter_side);
5752
5753   if (player->is_switching)
5754   {
5755     /* ensure that relocation while still switching an element does not cause
5756        a new element to be treated as also switched directly after relocation
5757        (this is important for teleporter switches that teleport the player to
5758        a place where another teleporter switch is in the same direction, which
5759        would then incorrectly be treated as immediately switched before the
5760        direction key that caused the switch was released) */
5761
5762     player->switch_x += jx - old_jx;
5763     player->switch_y += jy - old_jy;
5764   }
5765 }
5766
5767 static void Explode(int ex, int ey, int phase, int mode)
5768 {
5769   int x, y;
5770   int last_phase;
5771   int border_element;
5772
5773   // !!! eliminate this variable !!!
5774   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5775
5776   if (game.explosions_delayed)
5777   {
5778     ExplodeField[ex][ey] = mode;
5779     return;
5780   }
5781
5782   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5783   {
5784     int center_element = Tile[ex][ey];
5785     int artwork_element, explosion_element;     // set these values later
5786
5787     // remove things displayed in background while burning dynamite
5788     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5789       Back[ex][ey] = 0;
5790
5791     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5792     {
5793       // put moving element to center field (and let it explode there)
5794       center_element = MovingOrBlocked2Element(ex, ey);
5795       RemoveMovingField(ex, ey);
5796       Tile[ex][ey] = center_element;
5797     }
5798
5799     // now "center_element" is finally determined -- set related values now
5800     artwork_element = center_element;           // for custom player artwork
5801     explosion_element = center_element;         // for custom player artwork
5802
5803     if (IS_PLAYER(ex, ey))
5804     {
5805       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5806
5807       artwork_element = stored_player[player_nr].artwork_element;
5808
5809       if (level.use_explosion_element[player_nr])
5810       {
5811         explosion_element = level.explosion_element[player_nr];
5812         artwork_element = explosion_element;
5813       }
5814     }
5815
5816     if (mode == EX_TYPE_NORMAL ||
5817         mode == EX_TYPE_CENTER ||
5818         mode == EX_TYPE_CROSS)
5819       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5820
5821     last_phase = element_info[explosion_element].explosion_delay + 1;
5822
5823     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5824     {
5825       int xx = x - ex + 1;
5826       int yy = y - ey + 1;
5827       int element;
5828
5829       if (!IN_LEV_FIELD(x, y) ||
5830           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5831           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5832         continue;
5833
5834       element = Tile[x][y];
5835
5836       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5837       {
5838         element = MovingOrBlocked2Element(x, y);
5839
5840         if (!IS_EXPLOSION_PROOF(element))
5841           RemoveMovingField(x, y);
5842       }
5843
5844       // indestructible elements can only explode in center (but not flames)
5845       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5846                                            mode == EX_TYPE_BORDER)) ||
5847           element == EL_FLAMES)
5848         continue;
5849
5850       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5851          behaviour, for example when touching a yamyam that explodes to rocks
5852          with active deadly shield, a rock is created under the player !!! */
5853       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5854 #if 0
5855       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5856           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5857            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5858 #else
5859       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5860 #endif
5861       {
5862         if (IS_ACTIVE_BOMB(element))
5863         {
5864           // re-activate things under the bomb like gate or penguin
5865           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5866           Back[x][y] = 0;
5867         }
5868
5869         continue;
5870       }
5871
5872       // save walkable background elements while explosion on same tile
5873       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5874           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5875         Back[x][y] = element;
5876
5877       // ignite explodable elements reached by other explosion
5878       if (element == EL_EXPLOSION)
5879         element = Store2[x][y];
5880
5881       if (AmoebaNr[x][y] &&
5882           (element == EL_AMOEBA_FULL ||
5883            element == EL_BD_AMOEBA ||
5884            element == EL_AMOEBA_GROWING))
5885       {
5886         AmoebaCnt[AmoebaNr[x][y]]--;
5887         AmoebaCnt2[AmoebaNr[x][y]]--;
5888       }
5889
5890       RemoveField(x, y);
5891
5892       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5893       {
5894         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5895
5896         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5897
5898         if (PLAYERINFO(ex, ey)->use_murphy)
5899           Store[x][y] = EL_EMPTY;
5900       }
5901
5902       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5903       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5904       else if (IS_PLAYER_ELEMENT(center_element))
5905         Store[x][y] = EL_EMPTY;
5906       else if (center_element == EL_YAMYAM)
5907         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5908       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5909         Store[x][y] = element_info[center_element].content.e[xx][yy];
5910 #if 1
5911       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5912       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5913       // otherwise) -- FIX THIS !!!
5914       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5915         Store[x][y] = element_info[element].content.e[1][1];
5916 #else
5917       else if (!CAN_EXPLODE(element))
5918         Store[x][y] = element_info[element].content.e[1][1];
5919 #endif
5920       else
5921         Store[x][y] = EL_EMPTY;
5922
5923       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5924           center_element == EL_AMOEBA_TO_DIAMOND)
5925         Store2[x][y] = element;
5926
5927       Tile[x][y] = EL_EXPLOSION;
5928       GfxElement[x][y] = artwork_element;
5929
5930       ExplodePhase[x][y] = 1;
5931       ExplodeDelay[x][y] = last_phase;
5932
5933       Stop[x][y] = TRUE;
5934     }
5935
5936     if (center_element == EL_YAMYAM)
5937       game.yamyam_content_nr =
5938         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5939
5940     return;
5941   }
5942
5943   if (Stop[ex][ey])
5944     return;
5945
5946   x = ex;
5947   y = ey;
5948
5949   if (phase == 1)
5950     GfxFrame[x][y] = 0;         // restart explosion animation
5951
5952   last_phase = ExplodeDelay[x][y];
5953
5954   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5955
5956   // this can happen if the player leaves an explosion just in time
5957   if (GfxElement[x][y] == EL_UNDEFINED)
5958     GfxElement[x][y] = EL_EMPTY;
5959
5960   border_element = Store2[x][y];
5961   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5962     border_element = StorePlayer[x][y];
5963
5964   if (phase == element_info[border_element].ignition_delay ||
5965       phase == last_phase)
5966   {
5967     boolean border_explosion = FALSE;
5968
5969     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5970         !PLAYER_EXPLOSION_PROTECTED(x, y))
5971     {
5972       KillPlayerUnlessExplosionProtected(x, y);
5973       border_explosion = TRUE;
5974     }
5975     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5976     {
5977       Tile[x][y] = Store2[x][y];
5978       Store2[x][y] = 0;
5979       Bang(x, y);
5980       border_explosion = TRUE;
5981     }
5982     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5983     {
5984       AmoebaToDiamond(x, y);
5985       Store2[x][y] = 0;
5986       border_explosion = TRUE;
5987     }
5988
5989     // if an element just explodes due to another explosion (chain-reaction),
5990     // do not immediately end the new explosion when it was the last frame of
5991     // the explosion (as it would be done in the following "if"-statement!)
5992     if (border_explosion && phase == last_phase)
5993       return;
5994   }
5995
5996   if (phase == last_phase)
5997   {
5998     int element;
5999
6000     element = Tile[x][y] = Store[x][y];
6001     Store[x][y] = Store2[x][y] = 0;
6002     GfxElement[x][y] = EL_UNDEFINED;
6003
6004     // player can escape from explosions and might therefore be still alive
6005     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6006         element <= EL_PLAYER_IS_EXPLODING_4)
6007     {
6008       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6009       int explosion_element = EL_PLAYER_1 + player_nr;
6010       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6011       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6012
6013       if (level.use_explosion_element[player_nr])
6014         explosion_element = level.explosion_element[player_nr];
6015
6016       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6017                     element_info[explosion_element].content.e[xx][yy]);
6018     }
6019
6020     // restore probably existing indestructible background element
6021     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6022       element = Tile[x][y] = Back[x][y];
6023     Back[x][y] = 0;
6024
6025     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6026     GfxDir[x][y] = MV_NONE;
6027     ChangeDelay[x][y] = 0;
6028     ChangePage[x][y] = -1;
6029
6030     CustomValue[x][y] = 0;
6031
6032     InitField_WithBug2(x, y, FALSE);
6033
6034     TEST_DrawLevelField(x, y);
6035
6036     TestIfElementTouchesCustomElement(x, y);
6037
6038     if (GFX_CRUMBLED(element))
6039       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6040
6041     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6042       StorePlayer[x][y] = 0;
6043
6044     if (IS_PLAYER_ELEMENT(element))
6045       RelocatePlayer(x, y, element);
6046   }
6047   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6048   {
6049     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6050     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6051
6052     if (phase == delay)
6053       TEST_DrawLevelFieldCrumbled(x, y);
6054
6055     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6056     {
6057       DrawLevelElement(x, y, Back[x][y]);
6058       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6059     }
6060     else if (IS_WALKABLE_UNDER(Back[x][y]))
6061     {
6062       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6063       DrawLevelElementThruMask(x, y, Back[x][y]);
6064     }
6065     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6066       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6067   }
6068 }
6069
6070 static void DynaExplode(int ex, int ey)
6071 {
6072   int i, j;
6073   int dynabomb_element = Tile[ex][ey];
6074   int dynabomb_size = 1;
6075   boolean dynabomb_xl = FALSE;
6076   struct PlayerInfo *player;
6077   static int xy[4][2] =
6078   {
6079     { 0, -1 },
6080     { -1, 0 },
6081     { +1, 0 },
6082     { 0, +1 }
6083   };
6084
6085   if (IS_ACTIVE_BOMB(dynabomb_element))
6086   {
6087     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6088     dynabomb_size = player->dynabomb_size;
6089     dynabomb_xl = player->dynabomb_xl;
6090     player->dynabombs_left++;
6091   }
6092
6093   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6094
6095   for (i = 0; i < NUM_DIRECTIONS; i++)
6096   {
6097     for (j = 1; j <= dynabomb_size; j++)
6098     {
6099       int x = ex + j * xy[i][0];
6100       int y = ey + j * xy[i][1];
6101       int element;
6102
6103       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6104         break;
6105
6106       element = Tile[x][y];
6107
6108       // do not restart explosions of fields with active bombs
6109       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6110         continue;
6111
6112       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6113
6114       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6115           !IS_DIGGABLE(element) && !dynabomb_xl)
6116         break;
6117     }
6118   }
6119 }
6120
6121 void Bang(int x, int y)
6122 {
6123   int element = MovingOrBlocked2Element(x, y);
6124   int explosion_type = EX_TYPE_NORMAL;
6125
6126   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6127   {
6128     struct PlayerInfo *player = PLAYERINFO(x, y);
6129
6130     element = Tile[x][y] = player->initial_element;
6131
6132     if (level.use_explosion_element[player->index_nr])
6133     {
6134       int explosion_element = level.explosion_element[player->index_nr];
6135
6136       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6137         explosion_type = EX_TYPE_CROSS;
6138       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6139         explosion_type = EX_TYPE_CENTER;
6140     }
6141   }
6142
6143   switch (element)
6144   {
6145     case EL_BUG:
6146     case EL_SPACESHIP:
6147     case EL_BD_BUTTERFLY:
6148     case EL_BD_FIREFLY:
6149     case EL_YAMYAM:
6150     case EL_DARK_YAMYAM:
6151     case EL_ROBOT:
6152     case EL_PACMAN:
6153     case EL_MOLE:
6154       RaiseScoreElement(element);
6155       break;
6156
6157     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6158     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6159     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6160     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6161     case EL_DYNABOMB_INCREASE_NUMBER:
6162     case EL_DYNABOMB_INCREASE_SIZE:
6163     case EL_DYNABOMB_INCREASE_POWER:
6164       explosion_type = EX_TYPE_DYNA;
6165       break;
6166
6167     case EL_DC_LANDMINE:
6168       explosion_type = EX_TYPE_CENTER;
6169       break;
6170
6171     case EL_PENGUIN:
6172     case EL_LAMP:
6173     case EL_LAMP_ACTIVE:
6174     case EL_AMOEBA_TO_DIAMOND:
6175       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6176         explosion_type = EX_TYPE_CENTER;
6177       break;
6178
6179     default:
6180       if (element_info[element].explosion_type == EXPLODES_CROSS)
6181         explosion_type = EX_TYPE_CROSS;
6182       else if (element_info[element].explosion_type == EXPLODES_1X1)
6183         explosion_type = EX_TYPE_CENTER;
6184       break;
6185   }
6186
6187   if (explosion_type == EX_TYPE_DYNA)
6188     DynaExplode(x, y);
6189   else
6190     Explode(x, y, EX_PHASE_START, explosion_type);
6191
6192   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6193 }
6194
6195 static void SplashAcid(int x, int y)
6196 {
6197   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6198       (!IN_LEV_FIELD(x - 1, y - 2) ||
6199        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6200     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6201
6202   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6203       (!IN_LEV_FIELD(x + 1, y - 2) ||
6204        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6205     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6206
6207   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6208 }
6209
6210 static void InitBeltMovement(void)
6211 {
6212   static int belt_base_element[4] =
6213   {
6214     EL_CONVEYOR_BELT_1_LEFT,
6215     EL_CONVEYOR_BELT_2_LEFT,
6216     EL_CONVEYOR_BELT_3_LEFT,
6217     EL_CONVEYOR_BELT_4_LEFT
6218   };
6219   static int belt_base_active_element[4] =
6220   {
6221     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6222     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6223     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6224     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6225   };
6226
6227   int x, y, i, j;
6228
6229   // set frame order for belt animation graphic according to belt direction
6230   for (i = 0; i < NUM_BELTS; i++)
6231   {
6232     int belt_nr = i;
6233
6234     for (j = 0; j < NUM_BELT_PARTS; j++)
6235     {
6236       int element = belt_base_active_element[belt_nr] + j;
6237       int graphic_1 = el2img(element);
6238       int graphic_2 = el2panelimg(element);
6239
6240       if (game.belt_dir[i] == MV_LEFT)
6241       {
6242         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6243         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6244       }
6245       else
6246       {
6247         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6248         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6249       }
6250     }
6251   }
6252
6253   SCAN_PLAYFIELD(x, y)
6254   {
6255     int element = Tile[x][y];
6256
6257     for (i = 0; i < NUM_BELTS; i++)
6258     {
6259       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6260       {
6261         int e_belt_nr = getBeltNrFromBeltElement(element);
6262         int belt_nr = i;
6263
6264         if (e_belt_nr == belt_nr)
6265         {
6266           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6267
6268           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6269         }
6270       }
6271     }
6272   }
6273 }
6274
6275 static void ToggleBeltSwitch(int x, int y)
6276 {
6277   static int belt_base_element[4] =
6278   {
6279     EL_CONVEYOR_BELT_1_LEFT,
6280     EL_CONVEYOR_BELT_2_LEFT,
6281     EL_CONVEYOR_BELT_3_LEFT,
6282     EL_CONVEYOR_BELT_4_LEFT
6283   };
6284   static int belt_base_active_element[4] =
6285   {
6286     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6288     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6289     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6290   };
6291   static int belt_base_switch_element[4] =
6292   {
6293     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6294     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6295     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6296     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6297   };
6298   static int belt_move_dir[4] =
6299   {
6300     MV_LEFT,
6301     MV_NONE,
6302     MV_RIGHT,
6303     MV_NONE,
6304   };
6305
6306   int element = Tile[x][y];
6307   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6308   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6309   int belt_dir = belt_move_dir[belt_dir_nr];
6310   int xx, yy, i;
6311
6312   if (!IS_BELT_SWITCH(element))
6313     return;
6314
6315   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6316   game.belt_dir[belt_nr] = belt_dir;
6317
6318   if (belt_dir_nr == 3)
6319     belt_dir_nr = 1;
6320
6321   // set frame order for belt animation graphic according to belt direction
6322   for (i = 0; i < NUM_BELT_PARTS; i++)
6323   {
6324     int element = belt_base_active_element[belt_nr] + i;
6325     int graphic_1 = el2img(element);
6326     int graphic_2 = el2panelimg(element);
6327
6328     if (belt_dir == MV_LEFT)
6329     {
6330       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6331       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6332     }
6333     else
6334     {
6335       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6336       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6337     }
6338   }
6339
6340   SCAN_PLAYFIELD(xx, yy)
6341   {
6342     int element = Tile[xx][yy];
6343
6344     if (IS_BELT_SWITCH(element))
6345     {
6346       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6347
6348       if (e_belt_nr == belt_nr)
6349       {
6350         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6351         TEST_DrawLevelField(xx, yy);
6352       }
6353     }
6354     else if (IS_BELT(element) && belt_dir != MV_NONE)
6355     {
6356       int e_belt_nr = getBeltNrFromBeltElement(element);
6357
6358       if (e_belt_nr == belt_nr)
6359       {
6360         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6361
6362         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6363         TEST_DrawLevelField(xx, yy);
6364       }
6365     }
6366     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6367     {
6368       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6369
6370       if (e_belt_nr == belt_nr)
6371       {
6372         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6373
6374         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6375         TEST_DrawLevelField(xx, yy);
6376       }
6377     }
6378   }
6379 }
6380
6381 static void ToggleSwitchgateSwitch(int x, int y)
6382 {
6383   int xx, yy;
6384
6385   game.switchgate_pos = !game.switchgate_pos;
6386
6387   SCAN_PLAYFIELD(xx, yy)
6388   {
6389     int element = Tile[xx][yy];
6390
6391     if (element == EL_SWITCHGATE_SWITCH_UP)
6392     {
6393       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6397     {
6398       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6399       TEST_DrawLevelField(xx, yy);
6400     }
6401     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6402     {
6403       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6404       TEST_DrawLevelField(xx, yy);
6405     }
6406     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6407     {
6408       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6409       TEST_DrawLevelField(xx, yy);
6410     }
6411     else if (element == EL_SWITCHGATE_OPEN ||
6412              element == EL_SWITCHGATE_OPENING)
6413     {
6414       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6415
6416       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6417     }
6418     else if (element == EL_SWITCHGATE_CLOSED ||
6419              element == EL_SWITCHGATE_CLOSING)
6420     {
6421       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6422
6423       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6424     }
6425   }
6426 }
6427
6428 static int getInvisibleActiveFromInvisibleElement(int element)
6429 {
6430   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6431           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6432           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6433           element);
6434 }
6435
6436 static int getInvisibleFromInvisibleActiveElement(int element)
6437 {
6438   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6439           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6440           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6441           element);
6442 }
6443
6444 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6445 {
6446   int x, y;
6447
6448   SCAN_PLAYFIELD(x, y)
6449   {
6450     int element = Tile[x][y];
6451
6452     if (element == EL_LIGHT_SWITCH &&
6453         game.light_time_left > 0)
6454     {
6455       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6456       TEST_DrawLevelField(x, y);
6457     }
6458     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6459              game.light_time_left == 0)
6460     {
6461       Tile[x][y] = EL_LIGHT_SWITCH;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_EMC_DRIPPER &&
6465              game.light_time_left > 0)
6466     {
6467       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6471              game.light_time_left == 0)
6472     {
6473       Tile[x][y] = EL_EMC_DRIPPER;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (element == EL_INVISIBLE_STEELWALL ||
6477              element == EL_INVISIBLE_WALL ||
6478              element == EL_INVISIBLE_SAND)
6479     {
6480       if (game.light_time_left > 0)
6481         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6482
6483       TEST_DrawLevelField(x, y);
6484
6485       // uncrumble neighbour fields, if needed
6486       if (element == EL_INVISIBLE_SAND)
6487         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6488     }
6489     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6490              element == EL_INVISIBLE_WALL_ACTIVE ||
6491              element == EL_INVISIBLE_SAND_ACTIVE)
6492     {
6493       if (game.light_time_left == 0)
6494         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6495
6496       TEST_DrawLevelField(x, y);
6497
6498       // re-crumble neighbour fields, if needed
6499       if (element == EL_INVISIBLE_SAND)
6500         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501     }
6502   }
6503 }
6504
6505 static void RedrawAllInvisibleElementsForLenses(void)
6506 {
6507   int x, y;
6508
6509   SCAN_PLAYFIELD(x, y)
6510   {
6511     int element = Tile[x][y];
6512
6513     if (element == EL_EMC_DRIPPER &&
6514         game.lenses_time_left > 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6520              game.lenses_time_left == 0)
6521     {
6522       Tile[x][y] = EL_EMC_DRIPPER;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL ||
6526              element == EL_INVISIBLE_WALL ||
6527              element == EL_INVISIBLE_SAND)
6528     {
6529       if (game.lenses_time_left > 0)
6530         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // uncrumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6539              element == EL_INVISIBLE_WALL_ACTIVE ||
6540              element == EL_INVISIBLE_SAND_ACTIVE)
6541     {
6542       if (game.lenses_time_left == 0)
6543         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // re-crumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551   }
6552 }
6553
6554 static void RedrawAllInvisibleElementsForMagnifier(void)
6555 {
6556   int x, y;
6557
6558   SCAN_PLAYFIELD(x, y)
6559   {
6560     int element = Tile[x][y];
6561
6562     if (element == EL_EMC_FAKE_GRASS &&
6563         game.magnify_time_left > 0)
6564     {
6565       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6569              game.magnify_time_left == 0)
6570     {
6571       Tile[x][y] = EL_EMC_FAKE_GRASS;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (IS_GATE_GRAY(element) &&
6575              game.magnify_time_left > 0)
6576     {
6577       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6578                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6579                     IS_EM_GATE_GRAY(element) ?
6580                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6581                     IS_EMC_GATE_GRAY(element) ?
6582                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6583                     IS_DC_GATE_GRAY(element) ?
6584                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6585                     element);
6586       TEST_DrawLevelField(x, y);
6587     }
6588     else if (IS_GATE_GRAY_ACTIVE(element) &&
6589              game.magnify_time_left == 0)
6590     {
6591       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6592                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6593                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6594                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6595                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6596                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6597                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6598                     EL_DC_GATE_WHITE_GRAY :
6599                     element);
6600       TEST_DrawLevelField(x, y);
6601     }
6602   }
6603 }
6604
6605 static void ToggleLightSwitch(int x, int y)
6606 {
6607   int element = Tile[x][y];
6608
6609   game.light_time_left =
6610     (element == EL_LIGHT_SWITCH ?
6611      level.time_light * FRAMES_PER_SECOND : 0);
6612
6613   RedrawAllLightSwitchesAndInvisibleElements();
6614 }
6615
6616 static void ActivateTimegateSwitch(int x, int y)
6617 {
6618   int xx, yy;
6619
6620   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6621
6622   SCAN_PLAYFIELD(xx, yy)
6623   {
6624     int element = Tile[xx][yy];
6625
6626     if (element == EL_TIMEGATE_CLOSED ||
6627         element == EL_TIMEGATE_CLOSING)
6628     {
6629       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6630       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6631     }
6632
6633     /*
6634     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6635     {
6636       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6637       TEST_DrawLevelField(xx, yy);
6638     }
6639     */
6640
6641   }
6642
6643   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6644                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6645 }
6646
6647 static void Impact(int x, int y)
6648 {
6649   boolean last_line = (y == lev_fieldy - 1);
6650   boolean object_hit = FALSE;
6651   boolean impact = (last_line || object_hit);
6652   int element = Tile[x][y];
6653   int smashed = EL_STEELWALL;
6654
6655   if (!last_line)       // check if element below was hit
6656   {
6657     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6658       return;
6659
6660     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6661                                          MovDir[x][y + 1] != MV_DOWN ||
6662                                          MovPos[x][y + 1] <= TILEY / 2));
6663
6664     // do not smash moving elements that left the smashed field in time
6665     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6666         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6667       object_hit = FALSE;
6668
6669 #if USE_QUICKSAND_IMPACT_BUGFIX
6670     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6671     {
6672       RemoveMovingField(x, y + 1);
6673       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6674       Tile[x][y + 2] = EL_ROCK;
6675       TEST_DrawLevelField(x, y + 2);
6676
6677       object_hit = TRUE;
6678     }
6679
6680     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6681     {
6682       RemoveMovingField(x, y + 1);
6683       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6684       Tile[x][y + 2] = EL_ROCK;
6685       TEST_DrawLevelField(x, y + 2);
6686
6687       object_hit = TRUE;
6688     }
6689 #endif
6690
6691     if (object_hit)
6692       smashed = MovingOrBlocked2Element(x, y + 1);
6693
6694     impact = (last_line || object_hit);
6695   }
6696
6697   if (!last_line && smashed == EL_ACID) // element falls into acid
6698   {
6699     SplashAcid(x, y + 1);
6700     return;
6701   }
6702
6703   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6704   // only reset graphic animation if graphic really changes after impact
6705   if (impact &&
6706       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6707   {
6708     ResetGfxAnimation(x, y);
6709     TEST_DrawLevelField(x, y);
6710   }
6711
6712   if (impact && CAN_EXPLODE_IMPACT(element))
6713   {
6714     Bang(x, y);
6715     return;
6716   }
6717   else if (impact && element == EL_PEARL &&
6718            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6719   {
6720     ResetGfxAnimation(x, y);
6721
6722     Tile[x][y] = EL_PEARL_BREAKING;
6723     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6724     return;
6725   }
6726   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6727   {
6728     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6729
6730     return;
6731   }
6732
6733   if (impact && element == EL_AMOEBA_DROP)
6734   {
6735     if (object_hit && IS_PLAYER(x, y + 1))
6736       KillPlayerUnlessEnemyProtected(x, y + 1);
6737     else if (object_hit && smashed == EL_PENGUIN)
6738       Bang(x, y + 1);
6739     else
6740     {
6741       Tile[x][y] = EL_AMOEBA_GROWING;
6742       Store[x][y] = EL_AMOEBA_WET;
6743
6744       ResetRandomAnimationValue(x, y);
6745     }
6746     return;
6747   }
6748
6749   if (object_hit)               // check which object was hit
6750   {
6751     if ((CAN_PASS_MAGIC_WALL(element) && 
6752          (smashed == EL_MAGIC_WALL ||
6753           smashed == EL_BD_MAGIC_WALL)) ||
6754         (CAN_PASS_DC_MAGIC_WALL(element) &&
6755          smashed == EL_DC_MAGIC_WALL))
6756     {
6757       int xx, yy;
6758       int activated_magic_wall =
6759         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6760          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6761          EL_DC_MAGIC_WALL_ACTIVE);
6762
6763       // activate magic wall / mill
6764       SCAN_PLAYFIELD(xx, yy)
6765       {
6766         if (Tile[xx][yy] == smashed)
6767           Tile[xx][yy] = activated_magic_wall;
6768       }
6769
6770       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6771       game.magic_wall_active = TRUE;
6772
6773       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6774                             SND_MAGIC_WALL_ACTIVATING :
6775                             smashed == EL_BD_MAGIC_WALL ?
6776                             SND_BD_MAGIC_WALL_ACTIVATING :
6777                             SND_DC_MAGIC_WALL_ACTIVATING));
6778     }
6779
6780     if (IS_PLAYER(x, y + 1))
6781     {
6782       if (CAN_SMASH_PLAYER(element))
6783       {
6784         KillPlayerUnlessEnemyProtected(x, y + 1);
6785         return;
6786       }
6787     }
6788     else if (smashed == EL_PENGUIN)
6789     {
6790       if (CAN_SMASH_PLAYER(element))
6791       {
6792         Bang(x, y + 1);
6793         return;
6794       }
6795     }
6796     else if (element == EL_BD_DIAMOND)
6797     {
6798       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6799       {
6800         Bang(x, y + 1);
6801         return;
6802       }
6803     }
6804     else if (((element == EL_SP_INFOTRON ||
6805                element == EL_SP_ZONK) &&
6806               (smashed == EL_SP_SNIKSNAK ||
6807                smashed == EL_SP_ELECTRON ||
6808                smashed == EL_SP_DISK_ORANGE)) ||
6809              (element == EL_SP_INFOTRON &&
6810               smashed == EL_SP_DISK_YELLOW))
6811     {
6812       Bang(x, y + 1);
6813       return;
6814     }
6815     else if (CAN_SMASH_EVERYTHING(element))
6816     {
6817       if (IS_CLASSIC_ENEMY(smashed) ||
6818           CAN_EXPLODE_SMASHED(smashed))
6819       {
6820         Bang(x, y + 1);
6821         return;
6822       }
6823       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6824       {
6825         if (smashed == EL_LAMP ||
6826             smashed == EL_LAMP_ACTIVE)
6827         {
6828           Bang(x, y + 1);
6829           return;
6830         }
6831         else if (smashed == EL_NUT)
6832         {
6833           Tile[x][y + 1] = EL_NUT_BREAKING;
6834           PlayLevelSound(x, y, SND_NUT_BREAKING);
6835           RaiseScoreElement(EL_NUT);
6836           return;
6837         }
6838         else if (smashed == EL_PEARL)
6839         {
6840           ResetGfxAnimation(x, y);
6841
6842           Tile[x][y + 1] = EL_PEARL_BREAKING;
6843           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6844           return;
6845         }
6846         else if (smashed == EL_DIAMOND)
6847         {
6848           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6849           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6850           return;
6851         }
6852         else if (IS_BELT_SWITCH(smashed))
6853         {
6854           ToggleBeltSwitch(x, y + 1);
6855         }
6856         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6857                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6858                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6859                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6860         {
6861           ToggleSwitchgateSwitch(x, y + 1);
6862         }
6863         else if (smashed == EL_LIGHT_SWITCH ||
6864                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6865         {
6866           ToggleLightSwitch(x, y + 1);
6867         }
6868         else
6869         {
6870           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6871
6872           CheckElementChangeBySide(x, y + 1, smashed, element,
6873                                    CE_SWITCHED, CH_SIDE_TOP);
6874           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6875                                             CH_SIDE_TOP);
6876         }
6877       }
6878       else
6879       {
6880         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6881       }
6882     }
6883   }
6884
6885   // play sound of magic wall / mill
6886   if (!last_line &&
6887       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6888        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6889        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6890   {
6891     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6892       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6893     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6894       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6895     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6896       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6897
6898     return;
6899   }
6900
6901   // play sound of object that hits the ground
6902   if (last_line || object_hit)
6903     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6904 }
6905
6906 static void TurnRoundExt(int x, int y)
6907 {
6908   static struct
6909   {
6910     int dx, dy;
6911   } move_xy[] =
6912   {
6913     {  0,  0 },
6914     { -1,  0 },
6915     { +1,  0 },
6916     {  0,  0 },
6917     {  0, -1 },
6918     {  0,  0 }, { 0, 0 }, { 0, 0 },
6919     {  0, +1 }
6920   };
6921   static struct
6922   {
6923     int left, right, back;
6924   } turn[] =
6925   {
6926     { 0,        0,              0        },
6927     { MV_DOWN,  MV_UP,          MV_RIGHT },
6928     { MV_UP,    MV_DOWN,        MV_LEFT  },
6929     { 0,        0,              0        },
6930     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6931     { 0,        0,              0        },
6932     { 0,        0,              0        },
6933     { 0,        0,              0        },
6934     { MV_RIGHT, MV_LEFT,        MV_UP    }
6935   };
6936
6937   int element = Tile[x][y];
6938   int move_pattern = element_info[element].move_pattern;
6939
6940   int old_move_dir = MovDir[x][y];
6941   int left_dir  = turn[old_move_dir].left;
6942   int right_dir = turn[old_move_dir].right;
6943   int back_dir  = turn[old_move_dir].back;
6944
6945   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6946   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6947   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6948   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6949
6950   int left_x  = x + left_dx,  left_y  = y + left_dy;
6951   int right_x = x + right_dx, right_y = y + right_dy;
6952   int move_x  = x + move_dx,  move_y  = y + move_dy;
6953
6954   int xx, yy;
6955
6956   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6957   {
6958     TestIfBadThingTouchesOtherBadThing(x, y);
6959
6960     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6961       MovDir[x][y] = right_dir;
6962     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6963       MovDir[x][y] = left_dir;
6964
6965     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6966       MovDelay[x][y] = 9;
6967     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6968       MovDelay[x][y] = 1;
6969   }
6970   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6971   {
6972     TestIfBadThingTouchesOtherBadThing(x, y);
6973
6974     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6975       MovDir[x][y] = left_dir;
6976     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6977       MovDir[x][y] = right_dir;
6978
6979     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6980       MovDelay[x][y] = 9;
6981     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6982       MovDelay[x][y] = 1;
6983   }
6984   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6985   {
6986     TestIfBadThingTouchesOtherBadThing(x, y);
6987
6988     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6989       MovDir[x][y] = left_dir;
6990     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6991       MovDir[x][y] = right_dir;
6992
6993     if (MovDir[x][y] != old_move_dir)
6994       MovDelay[x][y] = 9;
6995   }
6996   else if (element == EL_YAMYAM)
6997   {
6998     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6999     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7000
7001     if (can_turn_left && can_turn_right)
7002       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7003     else if (can_turn_left)
7004       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7005     else if (can_turn_right)
7006       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7007     else
7008       MovDir[x][y] = back_dir;
7009
7010     MovDelay[x][y] = 16 + 16 * RND(3);
7011   }
7012   else if (element == EL_DARK_YAMYAM)
7013   {
7014     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7015                                                          left_x, left_y);
7016     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7017                                                          right_x, right_y);
7018
7019     if (can_turn_left && can_turn_right)
7020       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7021     else if (can_turn_left)
7022       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7023     else if (can_turn_right)
7024       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7025     else
7026       MovDir[x][y] = back_dir;
7027
7028     MovDelay[x][y] = 16 + 16 * RND(3);
7029   }
7030   else if (element == EL_PACMAN)
7031   {
7032     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7033     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7034
7035     if (can_turn_left && can_turn_right)
7036       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7037     else if (can_turn_left)
7038       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7039     else if (can_turn_right)
7040       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7041     else
7042       MovDir[x][y] = back_dir;
7043
7044     MovDelay[x][y] = 6 + RND(40);
7045   }
7046   else if (element == EL_PIG)
7047   {
7048     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7049     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7050     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7051     boolean should_turn_left, should_turn_right, should_move_on;
7052     int rnd_value = 24;
7053     int rnd = RND(rnd_value);
7054
7055     should_turn_left = (can_turn_left &&
7056                         (!can_move_on ||
7057                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7058                                                    y + back_dy + left_dy)));
7059     should_turn_right = (can_turn_right &&
7060                          (!can_move_on ||
7061                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7062                                                     y + back_dy + right_dy)));
7063     should_move_on = (can_move_on &&
7064                       (!can_turn_left ||
7065                        !can_turn_right ||
7066                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7067                                                  y + move_dy + left_dy) ||
7068                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7069                                                  y + move_dy + right_dy)));
7070
7071     if (should_turn_left || should_turn_right || should_move_on)
7072     {
7073       if (should_turn_left && should_turn_right && should_move_on)
7074         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7075                         rnd < 2 * rnd_value / 3 ? right_dir :
7076                         old_move_dir);
7077       else if (should_turn_left && should_turn_right)
7078         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7079       else if (should_turn_left && should_move_on)
7080         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7081       else if (should_turn_right && should_move_on)
7082         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7083       else if (should_turn_left)
7084         MovDir[x][y] = left_dir;
7085       else if (should_turn_right)
7086         MovDir[x][y] = right_dir;
7087       else if (should_move_on)
7088         MovDir[x][y] = old_move_dir;
7089     }
7090     else if (can_move_on && rnd > rnd_value / 8)
7091       MovDir[x][y] = old_move_dir;
7092     else if (can_turn_left && can_turn_right)
7093       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7094     else if (can_turn_left && rnd > rnd_value / 8)
7095       MovDir[x][y] = left_dir;
7096     else if (can_turn_right && rnd > rnd_value/8)
7097       MovDir[x][y] = right_dir;
7098     else
7099       MovDir[x][y] = back_dir;
7100
7101     xx = x + move_xy[MovDir[x][y]].dx;
7102     yy = y + move_xy[MovDir[x][y]].dy;
7103
7104     if (!IN_LEV_FIELD(xx, yy) ||
7105         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7106       MovDir[x][y] = old_move_dir;
7107
7108     MovDelay[x][y] = 0;
7109   }
7110   else if (element == EL_DRAGON)
7111   {
7112     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7113     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7114     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7115     int rnd_value = 24;
7116     int rnd = RND(rnd_value);
7117
7118     if (can_move_on && rnd > rnd_value / 8)
7119       MovDir[x][y] = old_move_dir;
7120     else if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7122     else if (can_turn_left && rnd > rnd_value / 8)
7123       MovDir[x][y] = left_dir;
7124     else if (can_turn_right && rnd > rnd_value / 8)
7125       MovDir[x][y] = right_dir;
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     xx = x + move_xy[MovDir[x][y]].dx;
7130     yy = y + move_xy[MovDir[x][y]].dy;
7131
7132     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7133       MovDir[x][y] = old_move_dir;
7134
7135     MovDelay[x][y] = 0;
7136   }
7137   else if (element == EL_MOLE)
7138   {
7139     boolean can_move_on =
7140       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7141                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7142                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7143     if (!can_move_on)
7144     {
7145       boolean can_turn_left =
7146         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7147                               IS_AMOEBOID(Tile[left_x][left_y])));
7148
7149       boolean can_turn_right =
7150         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7151                               IS_AMOEBOID(Tile[right_x][right_y])));
7152
7153       if (can_turn_left && can_turn_right)
7154         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7155       else if (can_turn_left)
7156         MovDir[x][y] = left_dir;
7157       else
7158         MovDir[x][y] = right_dir;
7159     }
7160
7161     if (MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = 9;
7163   }
7164   else if (element == EL_BALLOON)
7165   {
7166     MovDir[x][y] = game.wind_direction;
7167     MovDelay[x][y] = 0;
7168   }
7169   else if (element == EL_SPRING)
7170   {
7171     if (MovDir[x][y] & MV_HORIZONTAL)
7172     {
7173       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7174           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7175       {
7176         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7177         ResetGfxAnimation(move_x, move_y);
7178         TEST_DrawLevelField(move_x, move_y);
7179
7180         MovDir[x][y] = back_dir;
7181       }
7182       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7183                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7184         MovDir[x][y] = MV_NONE;
7185     }
7186
7187     MovDelay[x][y] = 0;
7188   }
7189   else if (element == EL_ROBOT ||
7190            element == EL_SATELLITE ||
7191            element == EL_PENGUIN ||
7192            element == EL_EMC_ANDROID)
7193   {
7194     int attr_x = -1, attr_y = -1;
7195
7196     if (game.all_players_gone)
7197     {
7198       attr_x = game.exit_x;
7199       attr_y = game.exit_y;
7200     }
7201     else
7202     {
7203       int i;
7204
7205       for (i = 0; i < MAX_PLAYERS; i++)
7206       {
7207         struct PlayerInfo *player = &stored_player[i];
7208         int jx = player->jx, jy = player->jy;
7209
7210         if (!player->active)
7211           continue;
7212
7213         if (attr_x == -1 ||
7214             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7215         {
7216           attr_x = jx;
7217           attr_y = jy;
7218         }
7219       }
7220     }
7221
7222     if (element == EL_ROBOT &&
7223         game.robot_wheel_x >= 0 &&
7224         game.robot_wheel_y >= 0 &&
7225         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7226          game.engine_version < VERSION_IDENT(3,1,0,0)))
7227     {
7228       attr_x = game.robot_wheel_x;
7229       attr_y = game.robot_wheel_y;
7230     }
7231
7232     if (element == EL_PENGUIN)
7233     {
7234       int i;
7235       static int xy[4][2] =
7236       {
7237         { 0, -1 },
7238         { -1, 0 },
7239         { +1, 0 },
7240         { 0, +1 }
7241       };
7242
7243       for (i = 0; i < NUM_DIRECTIONS; i++)
7244       {
7245         int ex = x + xy[i][0];
7246         int ey = y + xy[i][1];
7247
7248         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7249                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7250                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7251                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7252         {
7253           attr_x = ex;
7254           attr_y = ey;
7255           break;
7256         }
7257       }
7258     }
7259
7260     MovDir[x][y] = MV_NONE;
7261     if (attr_x < x)
7262       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7263     else if (attr_x > x)
7264       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7265     if (attr_y < y)
7266       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7267     else if (attr_y > y)
7268       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7269
7270     if (element == EL_ROBOT)
7271     {
7272       int newx, newy;
7273
7274       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7275         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7276       Moving2Blocked(x, y, &newx, &newy);
7277
7278       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7279         MovDelay[x][y] = 8 + 8 * !RND(3);
7280       else
7281         MovDelay[x][y] = 16;
7282     }
7283     else if (element == EL_PENGUIN)
7284     {
7285       int newx, newy;
7286
7287       MovDelay[x][y] = 1;
7288
7289       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7290       {
7291         boolean first_horiz = RND(2);
7292         int new_move_dir = MovDir[x][y];
7293
7294         MovDir[x][y] =
7295           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7296         Moving2Blocked(x, y, &newx, &newy);
7297
7298         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7299           return;
7300
7301         MovDir[x][y] =
7302           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7303         Moving2Blocked(x, y, &newx, &newy);
7304
7305         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7306           return;
7307
7308         MovDir[x][y] = old_move_dir;
7309         return;
7310       }
7311     }
7312     else if (element == EL_SATELLITE)
7313     {
7314       int newx, newy;
7315
7316       MovDelay[x][y] = 1;
7317
7318       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7319       {
7320         boolean first_horiz = RND(2);
7321         int new_move_dir = MovDir[x][y];
7322
7323         MovDir[x][y] =
7324           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7325         Moving2Blocked(x, y, &newx, &newy);
7326
7327         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7328           return;
7329
7330         MovDir[x][y] =
7331           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7332         Moving2Blocked(x, y, &newx, &newy);
7333
7334         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7335           return;
7336
7337         MovDir[x][y] = old_move_dir;
7338         return;
7339       }
7340     }
7341     else if (element == EL_EMC_ANDROID)
7342     {
7343       static int check_pos[16] =
7344       {
7345         -1,             //  0 => (invalid)
7346         7,              //  1 => MV_LEFT
7347         3,              //  2 => MV_RIGHT
7348         -1,             //  3 => (invalid)
7349         1,              //  4 =>            MV_UP
7350         0,              //  5 => MV_LEFT  | MV_UP
7351         2,              //  6 => MV_RIGHT | MV_UP
7352         -1,             //  7 => (invalid)
7353         5,              //  8 =>            MV_DOWN
7354         6,              //  9 => MV_LEFT  | MV_DOWN
7355         4,              // 10 => MV_RIGHT | MV_DOWN
7356         -1,             // 11 => (invalid)
7357         -1,             // 12 => (invalid)
7358         -1,             // 13 => (invalid)
7359         -1,             // 14 => (invalid)
7360         -1,             // 15 => (invalid)
7361       };
7362       static struct
7363       {
7364         int dx, dy;
7365         int dir;
7366       } check_xy[8] =
7367       {
7368         { -1, -1,       MV_LEFT  | MV_UP   },
7369         {  0, -1,                  MV_UP   },
7370         { +1, -1,       MV_RIGHT | MV_UP   },
7371         { +1,  0,       MV_RIGHT           },
7372         { +1, +1,       MV_RIGHT | MV_DOWN },
7373         {  0, +1,                  MV_DOWN },
7374         { -1, +1,       MV_LEFT  | MV_DOWN },
7375         { -1,  0,       MV_LEFT            },
7376       };
7377       int start_pos, check_order;
7378       boolean can_clone = FALSE;
7379       int i;
7380
7381       // check if there is any free field around current position
7382       for (i = 0; i < 8; i++)
7383       {
7384         int newx = x + check_xy[i].dx;
7385         int newy = y + check_xy[i].dy;
7386
7387         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7388         {
7389           can_clone = TRUE;
7390
7391           break;
7392         }
7393       }
7394
7395       if (can_clone)            // randomly find an element to clone
7396       {
7397         can_clone = FALSE;
7398
7399         start_pos = check_pos[RND(8)];
7400         check_order = (RND(2) ? -1 : +1);
7401
7402         for (i = 0; i < 8; i++)
7403         {
7404           int pos_raw = start_pos + i * check_order;
7405           int pos = (pos_raw + 8) % 8;
7406           int newx = x + check_xy[pos].dx;
7407           int newy = y + check_xy[pos].dy;
7408
7409           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7410           {
7411             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7412             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7413
7414             Store[x][y] = Tile[newx][newy];
7415
7416             can_clone = TRUE;
7417
7418             break;
7419           }
7420         }
7421       }
7422
7423       if (can_clone)            // randomly find a direction to move
7424       {
7425         can_clone = FALSE;
7426
7427         start_pos = check_pos[RND(8)];
7428         check_order = (RND(2) ? -1 : +1);
7429
7430         for (i = 0; i < 8; i++)
7431         {
7432           int pos_raw = start_pos + i * check_order;
7433           int pos = (pos_raw + 8) % 8;
7434           int newx = x + check_xy[pos].dx;
7435           int newy = y + check_xy[pos].dy;
7436           int new_move_dir = check_xy[pos].dir;
7437
7438           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7439           {
7440             MovDir[x][y] = new_move_dir;
7441             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7442
7443             can_clone = TRUE;
7444
7445             break;
7446           }
7447         }
7448       }
7449
7450       if (can_clone)            // cloning and moving successful
7451         return;
7452
7453       // cannot clone -- try to move towards player
7454
7455       start_pos = check_pos[MovDir[x][y] & 0x0f];
7456       check_order = (RND(2) ? -1 : +1);
7457
7458       for (i = 0; i < 3; i++)
7459       {
7460         // first check start_pos, then previous/next or (next/previous) pos
7461         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7462         int pos = (pos_raw + 8) % 8;
7463         int newx = x + check_xy[pos].dx;
7464         int newy = y + check_xy[pos].dy;
7465         int new_move_dir = check_xy[pos].dir;
7466
7467         if (IS_PLAYER(newx, newy))
7468           break;
7469
7470         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7471         {
7472           MovDir[x][y] = new_move_dir;
7473           MovDelay[x][y] = level.android_move_time * 8 + 1;
7474
7475           break;
7476         }
7477       }
7478     }
7479   }
7480   else if (move_pattern == MV_TURNING_LEFT ||
7481            move_pattern == MV_TURNING_RIGHT ||
7482            move_pattern == MV_TURNING_LEFT_RIGHT ||
7483            move_pattern == MV_TURNING_RIGHT_LEFT ||
7484            move_pattern == MV_TURNING_RANDOM ||
7485            move_pattern == MV_ALL_DIRECTIONS)
7486   {
7487     boolean can_turn_left =
7488       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7489     boolean can_turn_right =
7490       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7491
7492     if (element_info[element].move_stepsize == 0)       // "not moving"
7493       return;
7494
7495     if (move_pattern == MV_TURNING_LEFT)
7496       MovDir[x][y] = left_dir;
7497     else if (move_pattern == MV_TURNING_RIGHT)
7498       MovDir[x][y] = right_dir;
7499     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7500       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7501     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7502       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7503     else if (move_pattern == MV_TURNING_RANDOM)
7504       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7505                       can_turn_right && !can_turn_left ? right_dir :
7506                       RND(2) ? left_dir : right_dir);
7507     else if (can_turn_left && can_turn_right)
7508       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7509     else if (can_turn_left)
7510       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7511     else if (can_turn_right)
7512       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7513     else
7514       MovDir[x][y] = back_dir;
7515
7516     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7517   }
7518   else if (move_pattern == MV_HORIZONTAL ||
7519            move_pattern == MV_VERTICAL)
7520   {
7521     if (move_pattern & old_move_dir)
7522       MovDir[x][y] = back_dir;
7523     else if (move_pattern == MV_HORIZONTAL)
7524       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7525     else if (move_pattern == MV_VERTICAL)
7526       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7527
7528     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7529   }
7530   else if (move_pattern & MV_ANY_DIRECTION)
7531   {
7532     MovDir[x][y] = move_pattern;
7533     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7534   }
7535   else if (move_pattern & MV_WIND_DIRECTION)
7536   {
7537     MovDir[x][y] = game.wind_direction;
7538     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7539   }
7540   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7541   {
7542     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7543       MovDir[x][y] = left_dir;
7544     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7545       MovDir[x][y] = right_dir;
7546
7547     if (MovDir[x][y] != old_move_dir)
7548       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7549   }
7550   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7551   {
7552     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7553       MovDir[x][y] = right_dir;
7554     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7555       MovDir[x][y] = left_dir;
7556
7557     if (MovDir[x][y] != old_move_dir)
7558       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7559   }
7560   else if (move_pattern == MV_TOWARDS_PLAYER ||
7561            move_pattern == MV_AWAY_FROM_PLAYER)
7562   {
7563     int attr_x = -1, attr_y = -1;
7564     int newx, newy;
7565     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7566
7567     if (game.all_players_gone)
7568     {
7569       attr_x = game.exit_x;
7570       attr_y = game.exit_y;
7571     }
7572     else
7573     {
7574       int i;
7575
7576       for (i = 0; i < MAX_PLAYERS; i++)
7577       {
7578         struct PlayerInfo *player = &stored_player[i];
7579         int jx = player->jx, jy = player->jy;
7580
7581         if (!player->active)
7582           continue;
7583
7584         if (attr_x == -1 ||
7585             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7586         {
7587           attr_x = jx;
7588           attr_y = jy;
7589         }
7590       }
7591     }
7592
7593     MovDir[x][y] = MV_NONE;
7594     if (attr_x < x)
7595       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7596     else if (attr_x > x)
7597       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7598     if (attr_y < y)
7599       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7600     else if (attr_y > y)
7601       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7602
7603     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7604
7605     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7606     {
7607       boolean first_horiz = RND(2);
7608       int new_move_dir = MovDir[x][y];
7609
7610       if (element_info[element].move_stepsize == 0)     // "not moving"
7611       {
7612         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7613         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7614
7615         return;
7616       }
7617
7618       MovDir[x][y] =
7619         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7620       Moving2Blocked(x, y, &newx, &newy);
7621
7622       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7623         return;
7624
7625       MovDir[x][y] =
7626         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7627       Moving2Blocked(x, y, &newx, &newy);
7628
7629       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7630         return;
7631
7632       MovDir[x][y] = old_move_dir;
7633     }
7634   }
7635   else if (move_pattern == MV_WHEN_PUSHED ||
7636            move_pattern == MV_WHEN_DROPPED)
7637   {
7638     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7639       MovDir[x][y] = MV_NONE;
7640
7641     MovDelay[x][y] = 0;
7642   }
7643   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7644   {
7645     static int test_xy[7][2] =
7646     {
7647       { 0, -1 },
7648       { -1, 0 },
7649       { +1, 0 },
7650       { 0, +1 },
7651       { 0, -1 },
7652       { -1, 0 },
7653       { +1, 0 },
7654     };
7655     static int test_dir[7] =
7656     {
7657       MV_UP,
7658       MV_LEFT,
7659       MV_RIGHT,
7660       MV_DOWN,
7661       MV_UP,
7662       MV_LEFT,
7663       MV_RIGHT,
7664     };
7665     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7666     int move_preference = -1000000;     // start with very low preference
7667     int new_move_dir = MV_NONE;
7668     int start_test = RND(4);
7669     int i;
7670
7671     for (i = 0; i < NUM_DIRECTIONS; i++)
7672     {
7673       int move_dir = test_dir[start_test + i];
7674       int move_dir_preference;
7675
7676       xx = x + test_xy[start_test + i][0];
7677       yy = y + test_xy[start_test + i][1];
7678
7679       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7680           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7681       {
7682         new_move_dir = move_dir;
7683
7684         break;
7685       }
7686
7687       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7688         continue;
7689
7690       move_dir_preference = -1 * RunnerVisit[xx][yy];
7691       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7692         move_dir_preference = PlayerVisit[xx][yy];
7693
7694       if (move_dir_preference > move_preference)
7695       {
7696         // prefer field that has not been visited for the longest time
7697         move_preference = move_dir_preference;
7698         new_move_dir = move_dir;
7699       }
7700       else if (move_dir_preference == move_preference &&
7701                move_dir == old_move_dir)
7702       {
7703         // prefer last direction when all directions are preferred equally
7704         move_preference = move_dir_preference;
7705         new_move_dir = move_dir;
7706       }
7707     }
7708
7709     MovDir[x][y] = new_move_dir;
7710     if (old_move_dir != new_move_dir)
7711       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7712   }
7713 }
7714
7715 static void TurnRound(int x, int y)
7716 {
7717   int direction = MovDir[x][y];
7718
7719   TurnRoundExt(x, y);
7720
7721   GfxDir[x][y] = MovDir[x][y];
7722
7723   if (direction != MovDir[x][y])
7724     GfxFrame[x][y] = 0;
7725
7726   if (MovDelay[x][y])
7727     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7728
7729   ResetGfxFrame(x, y);
7730 }
7731
7732 static boolean JustBeingPushed(int x, int y)
7733 {
7734   int i;
7735
7736   for (i = 0; i < MAX_PLAYERS; i++)
7737   {
7738     struct PlayerInfo *player = &stored_player[i];
7739
7740     if (player->active && player->is_pushing && player->MovPos)
7741     {
7742       int next_jx = player->jx + (player->jx - player->last_jx);
7743       int next_jy = player->jy + (player->jy - player->last_jy);
7744
7745       if (x == next_jx && y == next_jy)
7746         return TRUE;
7747     }
7748   }
7749
7750   return FALSE;
7751 }
7752
7753 static void StartMoving(int x, int y)
7754 {
7755   boolean started_moving = FALSE;       // some elements can fall _and_ move
7756   int element = Tile[x][y];
7757
7758   if (Stop[x][y])
7759     return;
7760
7761   if (MovDelay[x][y] == 0)
7762     GfxAction[x][y] = ACTION_DEFAULT;
7763
7764   if (CAN_FALL(element) && y < lev_fieldy - 1)
7765   {
7766     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7767         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7768       if (JustBeingPushed(x, y))
7769         return;
7770
7771     if (element == EL_QUICKSAND_FULL)
7772     {
7773       if (IS_FREE(x, y + 1))
7774       {
7775         InitMovingField(x, y, MV_DOWN);
7776         started_moving = TRUE;
7777
7778         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7779 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7780         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7781           Store[x][y] = EL_ROCK;
7782 #else
7783         Store[x][y] = EL_ROCK;
7784 #endif
7785
7786         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7787       }
7788       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7789       {
7790         if (!MovDelay[x][y])
7791         {
7792           MovDelay[x][y] = TILEY + 1;
7793
7794           ResetGfxAnimation(x, y);
7795           ResetGfxAnimation(x, y + 1);
7796         }
7797
7798         if (MovDelay[x][y])
7799         {
7800           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7801           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7802
7803           MovDelay[x][y]--;
7804           if (MovDelay[x][y])
7805             return;
7806         }
7807
7808         Tile[x][y] = EL_QUICKSAND_EMPTY;
7809         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7810         Store[x][y + 1] = Store[x][y];
7811         Store[x][y] = 0;
7812
7813         PlayLevelSoundAction(x, y, ACTION_FILLING);
7814       }
7815       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7816       {
7817         if (!MovDelay[x][y])
7818         {
7819           MovDelay[x][y] = TILEY + 1;
7820
7821           ResetGfxAnimation(x, y);
7822           ResetGfxAnimation(x, y + 1);
7823         }
7824
7825         if (MovDelay[x][y])
7826         {
7827           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7828           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7829
7830           MovDelay[x][y]--;
7831           if (MovDelay[x][y])
7832             return;
7833         }
7834
7835         Tile[x][y] = EL_QUICKSAND_EMPTY;
7836         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7837         Store[x][y + 1] = Store[x][y];
7838         Store[x][y] = 0;
7839
7840         PlayLevelSoundAction(x, y, ACTION_FILLING);
7841       }
7842     }
7843     else if (element == EL_QUICKSAND_FAST_FULL)
7844     {
7845       if (IS_FREE(x, y + 1))
7846       {
7847         InitMovingField(x, y, MV_DOWN);
7848         started_moving = TRUE;
7849
7850         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7851 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7852         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7853           Store[x][y] = EL_ROCK;
7854 #else
7855         Store[x][y] = EL_ROCK;
7856 #endif
7857
7858         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7859       }
7860       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7861       {
7862         if (!MovDelay[x][y])
7863         {
7864           MovDelay[x][y] = TILEY + 1;
7865
7866           ResetGfxAnimation(x, y);
7867           ResetGfxAnimation(x, y + 1);
7868         }
7869
7870         if (MovDelay[x][y])
7871         {
7872           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7873           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7874
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7881         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7882         Store[x][y + 1] = Store[x][y];
7883         Store[x][y] = 0;
7884
7885         PlayLevelSoundAction(x, y, ACTION_FILLING);
7886       }
7887       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7888       {
7889         if (!MovDelay[x][y])
7890         {
7891           MovDelay[x][y] = TILEY + 1;
7892
7893           ResetGfxAnimation(x, y);
7894           ResetGfxAnimation(x, y + 1);
7895         }
7896
7897         if (MovDelay[x][y])
7898         {
7899           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7900           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7901
7902           MovDelay[x][y]--;
7903           if (MovDelay[x][y])
7904             return;
7905         }
7906
7907         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7908         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7909         Store[x][y + 1] = Store[x][y];
7910         Store[x][y] = 0;
7911
7912         PlayLevelSoundAction(x, y, ACTION_FILLING);
7913       }
7914     }
7915     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7916              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7917     {
7918       InitMovingField(x, y, MV_DOWN);
7919       started_moving = TRUE;
7920
7921       Tile[x][y] = EL_QUICKSAND_FILLING;
7922       Store[x][y] = element;
7923
7924       PlayLevelSoundAction(x, y, ACTION_FILLING);
7925     }
7926     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7927              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7928     {
7929       InitMovingField(x, y, MV_DOWN);
7930       started_moving = TRUE;
7931
7932       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7933       Store[x][y] = element;
7934
7935       PlayLevelSoundAction(x, y, ACTION_FILLING);
7936     }
7937     else if (element == EL_MAGIC_WALL_FULL)
7938     {
7939       if (IS_FREE(x, y + 1))
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943
7944         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7945         Store[x][y] = EL_CHANGED(Store[x][y]);
7946       }
7947       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7948       {
7949         if (!MovDelay[x][y])
7950           MovDelay[x][y] = TILEY / 4 + 1;
7951
7952         if (MovDelay[x][y])
7953         {
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7960         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7961         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7962         Store[x][y] = 0;
7963       }
7964     }
7965     else if (element == EL_BD_MAGIC_WALL_FULL)
7966     {
7967       if (IS_FREE(x, y + 1))
7968       {
7969         InitMovingField(x, y, MV_DOWN);
7970         started_moving = TRUE;
7971
7972         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7973         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7974       }
7975       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7976       {
7977         if (!MovDelay[x][y])
7978           MovDelay[x][y] = TILEY / 4 + 1;
7979
7980         if (MovDelay[x][y])
7981         {
7982           MovDelay[x][y]--;
7983           if (MovDelay[x][y])
7984             return;
7985         }
7986
7987         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7988         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7989         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7990         Store[x][y] = 0;
7991       }
7992     }
7993     else if (element == EL_DC_MAGIC_WALL_FULL)
7994     {
7995       if (IS_FREE(x, y + 1))
7996       {
7997         InitMovingField(x, y, MV_DOWN);
7998         started_moving = TRUE;
7999
8000         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8001         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8002       }
8003       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8004       {
8005         if (!MovDelay[x][y])
8006           MovDelay[x][y] = TILEY / 4 + 1;
8007
8008         if (MovDelay[x][y])
8009         {
8010           MovDelay[x][y]--;
8011           if (MovDelay[x][y])
8012             return;
8013         }
8014
8015         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8016         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8017         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8018         Store[x][y] = 0;
8019       }
8020     }
8021     else if ((CAN_PASS_MAGIC_WALL(element) &&
8022               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8023                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8024              (CAN_PASS_DC_MAGIC_WALL(element) &&
8025               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8026
8027     {
8028       InitMovingField(x, y, MV_DOWN);
8029       started_moving = TRUE;
8030
8031       Tile[x][y] =
8032         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8033          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8034          EL_DC_MAGIC_WALL_FILLING);
8035       Store[x][y] = element;
8036     }
8037     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8038     {
8039       SplashAcid(x, y + 1);
8040
8041       InitMovingField(x, y, MV_DOWN);
8042       started_moving = TRUE;
8043
8044       Store[x][y] = EL_ACID;
8045     }
8046     else if (
8047              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8048               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8049              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8050               CAN_FALL(element) && WasJustFalling[x][y] &&
8051               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8052
8053              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8054               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8055               (Tile[x][y + 1] == EL_BLOCKED)))
8056     {
8057       /* this is needed for a special case not covered by calling "Impact()"
8058          from "ContinueMoving()": if an element moves to a tile directly below
8059          another element which was just falling on that tile (which was empty
8060          in the previous frame), the falling element above would just stop
8061          instead of smashing the element below (in previous version, the above
8062          element was just checked for "moving" instead of "falling", resulting
8063          in incorrect smashes caused by horizontal movement of the above
8064          element; also, the case of the player being the element to smash was
8065          simply not covered here... :-/ ) */
8066
8067       CheckCollision[x][y] = 0;
8068       CheckImpact[x][y] = 0;
8069
8070       Impact(x, y);
8071     }
8072     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8073     {
8074       if (MovDir[x][y] == MV_NONE)
8075       {
8076         InitMovingField(x, y, MV_DOWN);
8077         started_moving = TRUE;
8078       }
8079     }
8080     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8081     {
8082       if (WasJustFalling[x][y]) // prevent animation from being restarted
8083         MovDir[x][y] = MV_DOWN;
8084
8085       InitMovingField(x, y, MV_DOWN);
8086       started_moving = TRUE;
8087     }
8088     else if (element == EL_AMOEBA_DROP)
8089     {
8090       Tile[x][y] = EL_AMOEBA_GROWING;
8091       Store[x][y] = EL_AMOEBA_WET;
8092     }
8093     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8094               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8095              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8096              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8097     {
8098       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8099                                 (IS_FREE(x - 1, y + 1) ||
8100                                  Tile[x - 1][y + 1] == EL_ACID));
8101       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8102                                 (IS_FREE(x + 1, y + 1) ||
8103                                  Tile[x + 1][y + 1] == EL_ACID));
8104       boolean can_fall_any  = (can_fall_left || can_fall_right);
8105       boolean can_fall_both = (can_fall_left && can_fall_right);
8106       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8107
8108       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8109       {
8110         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8111           can_fall_right = FALSE;
8112         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8113           can_fall_left = FALSE;
8114         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8115           can_fall_right = FALSE;
8116         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8117           can_fall_left = FALSE;
8118
8119         can_fall_any  = (can_fall_left || can_fall_right);
8120         can_fall_both = FALSE;
8121       }
8122
8123       if (can_fall_both)
8124       {
8125         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8126           can_fall_right = FALSE;       // slip down on left side
8127         else
8128           can_fall_left = !(can_fall_right = RND(2));
8129
8130         can_fall_both = FALSE;
8131       }
8132
8133       if (can_fall_any)
8134       {
8135         // if not determined otherwise, prefer left side for slipping down
8136         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8137         started_moving = TRUE;
8138       }
8139     }
8140     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8141     {
8142       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8143       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8144       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8145       int belt_dir = game.belt_dir[belt_nr];
8146
8147       if ((belt_dir == MV_LEFT  && left_is_free) ||
8148           (belt_dir == MV_RIGHT && right_is_free))
8149       {
8150         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8151
8152         InitMovingField(x, y, belt_dir);
8153         started_moving = TRUE;
8154
8155         Pushed[x][y] = TRUE;
8156         Pushed[nextx][y] = TRUE;
8157
8158         GfxAction[x][y] = ACTION_DEFAULT;
8159       }
8160       else
8161       {
8162         MovDir[x][y] = 0;       // if element was moving, stop it
8163       }
8164     }
8165   }
8166
8167   // not "else if" because of elements that can fall and move (EL_SPRING)
8168   if (CAN_MOVE(element) && !started_moving)
8169   {
8170     int move_pattern = element_info[element].move_pattern;
8171     int newx, newy;
8172
8173     Moving2Blocked(x, y, &newx, &newy);
8174
8175     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8176       return;
8177
8178     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8179         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8180     {
8181       WasJustMoving[x][y] = 0;
8182       CheckCollision[x][y] = 0;
8183
8184       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8185
8186       if (Tile[x][y] != element)        // element has changed
8187         return;
8188     }
8189
8190     if (!MovDelay[x][y])        // start new movement phase
8191     {
8192       // all objects that can change their move direction after each step
8193       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8194
8195       if (element != EL_YAMYAM &&
8196           element != EL_DARK_YAMYAM &&
8197           element != EL_PACMAN &&
8198           !(move_pattern & MV_ANY_DIRECTION) &&
8199           move_pattern != MV_TURNING_LEFT &&
8200           move_pattern != MV_TURNING_RIGHT &&
8201           move_pattern != MV_TURNING_LEFT_RIGHT &&
8202           move_pattern != MV_TURNING_RIGHT_LEFT &&
8203           move_pattern != MV_TURNING_RANDOM)
8204       {
8205         TurnRound(x, y);
8206
8207         if (MovDelay[x][y] && (element == EL_BUG ||
8208                                element == EL_SPACESHIP ||
8209                                element == EL_SP_SNIKSNAK ||
8210                                element == EL_SP_ELECTRON ||
8211                                element == EL_MOLE))
8212           TEST_DrawLevelField(x, y);
8213       }
8214     }
8215
8216     if (MovDelay[x][y])         // wait some time before next movement
8217     {
8218       MovDelay[x][y]--;
8219
8220       if (element == EL_ROBOT ||
8221           element == EL_YAMYAM ||
8222           element == EL_DARK_YAMYAM)
8223       {
8224         DrawLevelElementAnimationIfNeeded(x, y, element);
8225         PlayLevelSoundAction(x, y, ACTION_WAITING);
8226       }
8227       else if (element == EL_SP_ELECTRON)
8228         DrawLevelElementAnimationIfNeeded(x, y, element);
8229       else if (element == EL_DRAGON)
8230       {
8231         int i;
8232         int dir = MovDir[x][y];
8233         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8234         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8235         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8236                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8237                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8238                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8239         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8240
8241         GfxAction[x][y] = ACTION_ATTACKING;
8242
8243         if (IS_PLAYER(x, y))
8244           DrawPlayerField(x, y);
8245         else
8246           TEST_DrawLevelField(x, y);
8247
8248         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8249
8250         for (i = 1; i <= 3; i++)
8251         {
8252           int xx = x + i * dx;
8253           int yy = y + i * dy;
8254           int sx = SCREENX(xx);
8255           int sy = SCREENY(yy);
8256           int flame_graphic = graphic + (i - 1);
8257
8258           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8259             break;
8260
8261           if (MovDelay[x][y])
8262           {
8263             int flamed = MovingOrBlocked2Element(xx, yy);
8264
8265             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8266               Bang(xx, yy);
8267             else
8268               RemoveMovingField(xx, yy);
8269
8270             ChangeDelay[xx][yy] = 0;
8271
8272             Tile[xx][yy] = EL_FLAMES;
8273
8274             if (IN_SCR_FIELD(sx, sy))
8275             {
8276               TEST_DrawLevelFieldCrumbled(xx, yy);
8277               DrawGraphic(sx, sy, flame_graphic, frame);
8278             }
8279           }
8280           else
8281           {
8282             if (Tile[xx][yy] == EL_FLAMES)
8283               Tile[xx][yy] = EL_EMPTY;
8284             TEST_DrawLevelField(xx, yy);
8285           }
8286         }
8287       }
8288
8289       if (MovDelay[x][y])       // element still has to wait some time
8290       {
8291         PlayLevelSoundAction(x, y, ACTION_WAITING);
8292
8293         return;
8294       }
8295     }
8296
8297     // now make next step
8298
8299     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8300
8301     if (DONT_COLLIDE_WITH(element) &&
8302         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8303         !PLAYER_ENEMY_PROTECTED(newx, newy))
8304     {
8305       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8306
8307       return;
8308     }
8309
8310     else if (CAN_MOVE_INTO_ACID(element) &&
8311              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8312              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8313              (MovDir[x][y] == MV_DOWN ||
8314               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8315     {
8316       SplashAcid(newx, newy);
8317       Store[x][y] = EL_ACID;
8318     }
8319     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8320     {
8321       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8322           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8323           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8324           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8325       {
8326         RemoveField(x, y);
8327         TEST_DrawLevelField(x, y);
8328
8329         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8330         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8331           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8332
8333         game.friends_still_needed--;
8334         if (!game.friends_still_needed &&
8335             !game.GameOver &&
8336             game.all_players_gone)
8337           LevelSolved();
8338
8339         return;
8340       }
8341       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8342       {
8343         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8344           TEST_DrawLevelField(newx, newy);
8345         else
8346           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8347       }
8348       else if (!IS_FREE(newx, newy))
8349       {
8350         GfxAction[x][y] = ACTION_WAITING;
8351
8352         if (IS_PLAYER(x, y))
8353           DrawPlayerField(x, y);
8354         else
8355           TEST_DrawLevelField(x, y);
8356
8357         return;
8358       }
8359     }
8360     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8361     {
8362       if (IS_FOOD_PIG(Tile[newx][newy]))
8363       {
8364         if (IS_MOVING(newx, newy))
8365           RemoveMovingField(newx, newy);
8366         else
8367         {
8368           Tile[newx][newy] = EL_EMPTY;
8369           TEST_DrawLevelField(newx, newy);
8370         }
8371
8372         PlayLevelSound(x, y, SND_PIG_DIGGING);
8373       }
8374       else if (!IS_FREE(newx, newy))
8375       {
8376         if (IS_PLAYER(x, y))
8377           DrawPlayerField(x, y);
8378         else
8379           TEST_DrawLevelField(x, y);
8380
8381         return;
8382       }
8383     }
8384     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8385     {
8386       if (Store[x][y] != EL_EMPTY)
8387       {
8388         boolean can_clone = FALSE;
8389         int xx, yy;
8390
8391         // check if element to clone is still there
8392         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8393         {
8394           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8395           {
8396             can_clone = TRUE;
8397
8398             break;
8399           }
8400         }
8401
8402         // cannot clone or target field not free anymore -- do not clone
8403         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8404           Store[x][y] = EL_EMPTY;
8405       }
8406
8407       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8408       {
8409         if (IS_MV_DIAGONAL(MovDir[x][y]))
8410         {
8411           int diagonal_move_dir = MovDir[x][y];
8412           int stored = Store[x][y];
8413           int change_delay = 8;
8414           int graphic;
8415
8416           // android is moving diagonally
8417
8418           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8419
8420           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8421           GfxElement[x][y] = EL_EMC_ANDROID;
8422           GfxAction[x][y] = ACTION_SHRINKING;
8423           GfxDir[x][y] = diagonal_move_dir;
8424           ChangeDelay[x][y] = change_delay;
8425
8426           if (Store[x][y] == EL_EMPTY)
8427             Store[x][y] = GfxElementEmpty[x][y];
8428
8429           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8430                                    GfxDir[x][y]);
8431
8432           DrawLevelGraphicAnimation(x, y, graphic);
8433           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8434
8435           if (Tile[newx][newy] == EL_ACID)
8436           {
8437             SplashAcid(newx, newy);
8438
8439             return;
8440           }
8441
8442           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8443
8444           Store[newx][newy] = EL_EMC_ANDROID;
8445           GfxElement[newx][newy] = EL_EMC_ANDROID;
8446           GfxAction[newx][newy] = ACTION_GROWING;
8447           GfxDir[newx][newy] = diagonal_move_dir;
8448           ChangeDelay[newx][newy] = change_delay;
8449
8450           graphic = el_act_dir2img(GfxElement[newx][newy],
8451                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8452
8453           DrawLevelGraphicAnimation(newx, newy, graphic);
8454           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8455
8456           return;
8457         }
8458         else
8459         {
8460           Tile[newx][newy] = EL_EMPTY;
8461           TEST_DrawLevelField(newx, newy);
8462
8463           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8464         }
8465       }
8466       else if (!IS_FREE(newx, newy))
8467       {
8468         return;
8469       }
8470     }
8471     else if (IS_CUSTOM_ELEMENT(element) &&
8472              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8473     {
8474       if (!DigFieldByCE(newx, newy, element))
8475         return;
8476
8477       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8478       {
8479         RunnerVisit[x][y] = FrameCounter;
8480         PlayerVisit[x][y] /= 8;         // expire player visit path
8481       }
8482     }
8483     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8484     {
8485       if (!IS_FREE(newx, newy))
8486       {
8487         if (IS_PLAYER(x, y))
8488           DrawPlayerField(x, y);
8489         else
8490           TEST_DrawLevelField(x, y);
8491
8492         return;
8493       }
8494       else
8495       {
8496         boolean wanna_flame = !RND(10);
8497         int dx = newx - x, dy = newy - y;
8498         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8499         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8500         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8501                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8502         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8503                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8504
8505         if ((wanna_flame ||
8506              IS_CLASSIC_ENEMY(element1) ||
8507              IS_CLASSIC_ENEMY(element2)) &&
8508             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8509             element1 != EL_FLAMES && element2 != EL_FLAMES)
8510         {
8511           ResetGfxAnimation(x, y);
8512           GfxAction[x][y] = ACTION_ATTACKING;
8513
8514           if (IS_PLAYER(x, y))
8515             DrawPlayerField(x, y);
8516           else
8517             TEST_DrawLevelField(x, y);
8518
8519           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8520
8521           MovDelay[x][y] = 50;
8522
8523           Tile[newx][newy] = EL_FLAMES;
8524           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8525             Tile[newx1][newy1] = EL_FLAMES;
8526           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8527             Tile[newx2][newy2] = EL_FLAMES;
8528
8529           return;
8530         }
8531       }
8532     }
8533     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8534              Tile[newx][newy] == EL_DIAMOND)
8535     {
8536       if (IS_MOVING(newx, newy))
8537         RemoveMovingField(newx, newy);
8538       else
8539       {
8540         Tile[newx][newy] = EL_EMPTY;
8541         TEST_DrawLevelField(newx, newy);
8542       }
8543
8544       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8545     }
8546     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8547              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8548     {
8549       if (AmoebaNr[newx][newy])
8550       {
8551         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8552         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8553             Tile[newx][newy] == EL_BD_AMOEBA)
8554           AmoebaCnt[AmoebaNr[newx][newy]]--;
8555       }
8556
8557       if (IS_MOVING(newx, newy))
8558       {
8559         RemoveMovingField(newx, newy);
8560       }
8561       else
8562       {
8563         Tile[newx][newy] = EL_EMPTY;
8564         TEST_DrawLevelField(newx, newy);
8565       }
8566
8567       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8568     }
8569     else if ((element == EL_PACMAN || element == EL_MOLE)
8570              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8571     {
8572       if (AmoebaNr[newx][newy])
8573       {
8574         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8575         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8576             Tile[newx][newy] == EL_BD_AMOEBA)
8577           AmoebaCnt[AmoebaNr[newx][newy]]--;
8578       }
8579
8580       if (element == EL_MOLE)
8581       {
8582         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8583         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8584
8585         ResetGfxAnimation(x, y);
8586         GfxAction[x][y] = ACTION_DIGGING;
8587         TEST_DrawLevelField(x, y);
8588
8589         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8590
8591         return;                         // wait for shrinking amoeba
8592       }
8593       else      // element == EL_PACMAN
8594       {
8595         Tile[newx][newy] = EL_EMPTY;
8596         TEST_DrawLevelField(newx, newy);
8597         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8598       }
8599     }
8600     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8601              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8602               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8603     {
8604       // wait for shrinking amoeba to completely disappear
8605       return;
8606     }
8607     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8608     {
8609       // object was running against a wall
8610
8611       TurnRound(x, y);
8612
8613       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8614         DrawLevelElementAnimation(x, y, element);
8615
8616       if (DONT_TOUCH(element))
8617         TestIfBadThingTouchesPlayer(x, y);
8618
8619       return;
8620     }
8621
8622     InitMovingField(x, y, MovDir[x][y]);
8623
8624     PlayLevelSoundAction(x, y, ACTION_MOVING);
8625   }
8626
8627   if (MovDir[x][y])
8628     ContinueMoving(x, y);
8629 }
8630
8631 void ContinueMoving(int x, int y)
8632 {
8633   int element = Tile[x][y];
8634   struct ElementInfo *ei = &element_info[element];
8635   int direction = MovDir[x][y];
8636   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8637   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8638   int newx = x + dx, newy = y + dy;
8639   int stored = Store[x][y];
8640   int stored_new = Store[newx][newy];
8641   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8642   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8643   boolean last_line = (newy == lev_fieldy - 1);
8644   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8645
8646   if (pushed_by_player)         // special case: moving object pushed by player
8647   {
8648     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8649   }
8650   else if (use_step_delay)      // special case: moving object has step delay
8651   {
8652     if (!MovDelay[x][y])
8653       MovPos[x][y] += getElementMoveStepsize(x, y);
8654
8655     if (MovDelay[x][y])
8656       MovDelay[x][y]--;
8657     else
8658       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8659
8660     if (MovDelay[x][y])
8661     {
8662       TEST_DrawLevelField(x, y);
8663
8664       return;   // element is still waiting
8665     }
8666   }
8667   else                          // normal case: generically moving object
8668   {
8669     MovPos[x][y] += getElementMoveStepsize(x, y);
8670   }
8671
8672   if (ABS(MovPos[x][y]) < TILEX)
8673   {
8674     TEST_DrawLevelField(x, y);
8675
8676     return;     // element is still moving
8677   }
8678
8679   // element reached destination field
8680
8681   Tile[x][y] = EL_EMPTY;
8682   Tile[newx][newy] = element;
8683   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8684
8685   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8686   {
8687     element = Tile[newx][newy] = EL_ACID;
8688   }
8689   else if (element == EL_MOLE)
8690   {
8691     Tile[x][y] = EL_SAND;
8692
8693     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8694   }
8695   else if (element == EL_QUICKSAND_FILLING)
8696   {
8697     element = Tile[newx][newy] = get_next_element(element);
8698     Store[newx][newy] = Store[x][y];
8699   }
8700   else if (element == EL_QUICKSAND_EMPTYING)
8701   {
8702     Tile[x][y] = get_next_element(element);
8703     element = Tile[newx][newy] = Store[x][y];
8704   }
8705   else if (element == EL_QUICKSAND_FAST_FILLING)
8706   {
8707     element = Tile[newx][newy] = get_next_element(element);
8708     Store[newx][newy] = Store[x][y];
8709   }
8710   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8711   {
8712     Tile[x][y] = get_next_element(element);
8713     element = Tile[newx][newy] = Store[x][y];
8714   }
8715   else if (element == EL_MAGIC_WALL_FILLING)
8716   {
8717     element = Tile[newx][newy] = get_next_element(element);
8718     if (!game.magic_wall_active)
8719       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_MAGIC_WALL_EMPTYING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8727     element = Tile[newx][newy] = Store[x][y];
8728
8729     InitField(newx, newy, FALSE);
8730   }
8731   else if (element == EL_BD_MAGIC_WALL_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     if (!game.magic_wall_active)
8735       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8739   {
8740     Tile[x][y] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8743     element = Tile[newx][newy] = Store[x][y];
8744
8745     InitField(newx, newy, FALSE);
8746   }
8747   else if (element == EL_DC_MAGIC_WALL_FILLING)
8748   {
8749     element = Tile[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8759     element = Tile[newx][newy] = Store[x][y];
8760
8761     InitField(newx, newy, FALSE);
8762   }
8763   else if (element == EL_AMOEBA_DROPPING)
8764   {
8765     Tile[x][y] = get_next_element(element);
8766     element = Tile[newx][newy] = Store[x][y];
8767   }
8768   else if (element == EL_SOKOBAN_OBJECT)
8769   {
8770     if (Back[x][y])
8771       Tile[x][y] = Back[x][y];
8772
8773     if (Back[newx][newy])
8774       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8775
8776     Back[x][y] = Back[newx][newy] = 0;
8777   }
8778
8779   Store[x][y] = EL_EMPTY;
8780   MovPos[x][y] = 0;
8781   MovDir[x][y] = 0;
8782   MovDelay[x][y] = 0;
8783
8784   MovDelay[newx][newy] = 0;
8785
8786   if (CAN_CHANGE_OR_HAS_ACTION(element))
8787   {
8788     // copy element change control values to new field
8789     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8790     ChangePage[newx][newy]  = ChangePage[x][y];
8791     ChangeCount[newx][newy] = ChangeCount[x][y];
8792     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8793   }
8794
8795   CustomValue[newx][newy] = CustomValue[x][y];
8796
8797   ChangeDelay[x][y] = 0;
8798   ChangePage[x][y] = -1;
8799   ChangeCount[x][y] = 0;
8800   ChangeEvent[x][y] = -1;
8801
8802   CustomValue[x][y] = 0;
8803
8804   // copy animation control values to new field
8805   GfxFrame[newx][newy]  = GfxFrame[x][y];
8806   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8807   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8808   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8809
8810   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8811
8812   // some elements can leave other elements behind after moving
8813   if (ei->move_leave_element != EL_EMPTY &&
8814       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8815       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8816   {
8817     int move_leave_element = ei->move_leave_element;
8818
8819     // this makes it possible to leave the removed element again
8820     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8821       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8822
8823     Tile[x][y] = move_leave_element;
8824
8825     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8826       MovDir[x][y] = direction;
8827
8828     InitField(x, y, FALSE);
8829
8830     if (GFX_CRUMBLED(Tile[x][y]))
8831       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8832
8833     if (IS_PLAYER_ELEMENT(move_leave_element))
8834       RelocatePlayer(x, y, move_leave_element);
8835   }
8836
8837   // do this after checking for left-behind element
8838   ResetGfxAnimation(x, y);      // reset animation values for old field
8839
8840   if (!CAN_MOVE(element) ||
8841       (CAN_FALL(element) && direction == MV_DOWN &&
8842        (element == EL_SPRING ||
8843         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8844         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8845     GfxDir[x][y] = MovDir[newx][newy] = 0;
8846
8847   TEST_DrawLevelField(x, y);
8848   TEST_DrawLevelField(newx, newy);
8849
8850   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8851
8852   // prevent pushed element from moving on in pushed direction
8853   if (pushed_by_player && CAN_MOVE(element) &&
8854       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8855       !(element_info[element].move_pattern & direction))
8856     TurnRound(newx, newy);
8857
8858   // prevent elements on conveyor belt from moving on in last direction
8859   if (pushed_by_conveyor && CAN_FALL(element) &&
8860       direction & MV_HORIZONTAL)
8861     MovDir[newx][newy] = 0;
8862
8863   if (!pushed_by_player)
8864   {
8865     int nextx = newx + dx, nexty = newy + dy;
8866     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8867
8868     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8869
8870     if (CAN_FALL(element) && direction == MV_DOWN)
8871       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8872
8873     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8874       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8875
8876     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8877       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8878   }
8879
8880   if (DONT_TOUCH(element))      // object may be nasty to player or others
8881   {
8882     TestIfBadThingTouchesPlayer(newx, newy);
8883     TestIfBadThingTouchesFriend(newx, newy);
8884
8885     if (!IS_CUSTOM_ELEMENT(element))
8886       TestIfBadThingTouchesOtherBadThing(newx, newy);
8887   }
8888   else if (element == EL_PENGUIN)
8889     TestIfFriendTouchesBadThing(newx, newy);
8890
8891   if (DONT_GET_HIT_BY(element))
8892   {
8893     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8894   }
8895
8896   // give the player one last chance (one more frame) to move away
8897   if (CAN_FALL(element) && direction == MV_DOWN &&
8898       (last_line || (!IS_FREE(x, newy + 1) &&
8899                      (!IS_PLAYER(x, newy + 1) ||
8900                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8901     Impact(x, newy);
8902
8903   if (pushed_by_player && !game.use_change_when_pushing_bug)
8904   {
8905     int push_side = MV_DIR_OPPOSITE(direction);
8906     struct PlayerInfo *player = PLAYERINFO(x, y);
8907
8908     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8909                                player->index_bit, push_side);
8910     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8911                                         player->index_bit, push_side);
8912   }
8913
8914   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8915     MovDelay[newx][newy] = 1;
8916
8917   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8918
8919   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8920   TestIfElementHitsCustomElement(newx, newy, direction);
8921   TestIfPlayerTouchesCustomElement(newx, newy);
8922   TestIfElementTouchesCustomElement(newx, newy);
8923
8924   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8925       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8926     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8927                              MV_DIR_OPPOSITE(direction));
8928 }
8929
8930 int AmoebaNeighbourNr(int ax, int ay)
8931 {
8932   int i;
8933   int element = Tile[ax][ay];
8934   int group_nr = 0;
8935   static int xy[4][2] =
8936   {
8937     { 0, -1 },
8938     { -1, 0 },
8939     { +1, 0 },
8940     { 0, +1 }
8941   };
8942
8943   for (i = 0; i < NUM_DIRECTIONS; i++)
8944   {
8945     int x = ax + xy[i][0];
8946     int y = ay + xy[i][1];
8947
8948     if (!IN_LEV_FIELD(x, y))
8949       continue;
8950
8951     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8952       group_nr = AmoebaNr[x][y];
8953   }
8954
8955   return group_nr;
8956 }
8957
8958 static void AmoebaMerge(int ax, int ay)
8959 {
8960   int i, x, y, xx, yy;
8961   int new_group_nr = AmoebaNr[ax][ay];
8962   static int xy[4][2] =
8963   {
8964     { 0, -1 },
8965     { -1, 0 },
8966     { +1, 0 },
8967     { 0, +1 }
8968   };
8969
8970   if (new_group_nr == 0)
8971     return;
8972
8973   for (i = 0; i < NUM_DIRECTIONS; i++)
8974   {
8975     x = ax + xy[i][0];
8976     y = ay + xy[i][1];
8977
8978     if (!IN_LEV_FIELD(x, y))
8979       continue;
8980
8981     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8982          Tile[x][y] == EL_BD_AMOEBA ||
8983          Tile[x][y] == EL_AMOEBA_DEAD) &&
8984         AmoebaNr[x][y] != new_group_nr)
8985     {
8986       int old_group_nr = AmoebaNr[x][y];
8987
8988       if (old_group_nr == 0)
8989         return;
8990
8991       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8992       AmoebaCnt[old_group_nr] = 0;
8993       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8994       AmoebaCnt2[old_group_nr] = 0;
8995
8996       SCAN_PLAYFIELD(xx, yy)
8997       {
8998         if (AmoebaNr[xx][yy] == old_group_nr)
8999           AmoebaNr[xx][yy] = new_group_nr;
9000       }
9001     }
9002   }
9003 }
9004
9005 void AmoebaToDiamond(int ax, int ay)
9006 {
9007   int i, x, y;
9008
9009   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9010   {
9011     int group_nr = AmoebaNr[ax][ay];
9012
9013 #ifdef DEBUG
9014     if (group_nr == 0)
9015     {
9016       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9017       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9018
9019       return;
9020     }
9021 #endif
9022
9023     SCAN_PLAYFIELD(x, y)
9024     {
9025       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9026       {
9027         AmoebaNr[x][y] = 0;
9028         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9029       }
9030     }
9031
9032     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9033                             SND_AMOEBA_TURNING_TO_GEM :
9034                             SND_AMOEBA_TURNING_TO_ROCK));
9035     Bang(ax, ay);
9036   }
9037   else
9038   {
9039     static int xy[4][2] =
9040     {
9041       { 0, -1 },
9042       { -1, 0 },
9043       { +1, 0 },
9044       { 0, +1 }
9045     };
9046
9047     for (i = 0; i < NUM_DIRECTIONS; i++)
9048     {
9049       x = ax + xy[i][0];
9050       y = ay + xy[i][1];
9051
9052       if (!IN_LEV_FIELD(x, y))
9053         continue;
9054
9055       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9056       {
9057         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9058                               SND_AMOEBA_TURNING_TO_GEM :
9059                               SND_AMOEBA_TURNING_TO_ROCK));
9060         Bang(x, y);
9061       }
9062     }
9063   }
9064 }
9065
9066 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9067 {
9068   int x, y;
9069   int group_nr = AmoebaNr[ax][ay];
9070   boolean done = FALSE;
9071
9072 #ifdef DEBUG
9073   if (group_nr == 0)
9074   {
9075     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9076     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9077
9078     return;
9079   }
9080 #endif
9081
9082   SCAN_PLAYFIELD(x, y)
9083   {
9084     if (AmoebaNr[x][y] == group_nr &&
9085         (Tile[x][y] == EL_AMOEBA_DEAD ||
9086          Tile[x][y] == EL_BD_AMOEBA ||
9087          Tile[x][y] == EL_AMOEBA_GROWING))
9088     {
9089       AmoebaNr[x][y] = 0;
9090       Tile[x][y] = new_element;
9091       InitField(x, y, FALSE);
9092       TEST_DrawLevelField(x, y);
9093       done = TRUE;
9094     }
9095   }
9096
9097   if (done)
9098     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9099                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9100                             SND_BD_AMOEBA_TURNING_TO_GEM));
9101 }
9102
9103 static void AmoebaGrowing(int x, int y)
9104 {
9105   static unsigned int sound_delay = 0;
9106   static unsigned int sound_delay_value = 0;
9107
9108   if (!MovDelay[x][y])          // start new growing cycle
9109   {
9110     MovDelay[x][y] = 7;
9111
9112     if (DelayReached(&sound_delay, sound_delay_value))
9113     {
9114       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9115       sound_delay_value = 30;
9116     }
9117   }
9118
9119   if (MovDelay[x][y])           // wait some time before growing bigger
9120   {
9121     MovDelay[x][y]--;
9122     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9123     {
9124       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9125                                            6 - MovDelay[x][y]);
9126
9127       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9128     }
9129
9130     if (!MovDelay[x][y])
9131     {
9132       Tile[x][y] = Store[x][y];
9133       Store[x][y] = 0;
9134       TEST_DrawLevelField(x, y);
9135     }
9136   }
9137 }
9138
9139 static void AmoebaShrinking(int x, int y)
9140 {
9141   static unsigned int sound_delay = 0;
9142   static unsigned int sound_delay_value = 0;
9143
9144   if (!MovDelay[x][y])          // start new shrinking cycle
9145   {
9146     MovDelay[x][y] = 7;
9147
9148     if (DelayReached(&sound_delay, sound_delay_value))
9149       sound_delay_value = 30;
9150   }
9151
9152   if (MovDelay[x][y])           // wait some time before shrinking
9153   {
9154     MovDelay[x][y]--;
9155     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9156     {
9157       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9158                                            6 - MovDelay[x][y]);
9159
9160       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9161     }
9162
9163     if (!MovDelay[x][y])
9164     {
9165       Tile[x][y] = EL_EMPTY;
9166       TEST_DrawLevelField(x, y);
9167
9168       // don't let mole enter this field in this cycle;
9169       // (give priority to objects falling to this field from above)
9170       Stop[x][y] = TRUE;
9171     }
9172   }
9173 }
9174
9175 static void AmoebaReproduce(int ax, int ay)
9176 {
9177   int i;
9178   int element = Tile[ax][ay];
9179   int graphic = el2img(element);
9180   int newax = ax, neway = ay;
9181   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9182   static int xy[4][2] =
9183   {
9184     { 0, -1 },
9185     { -1, 0 },
9186     { +1, 0 },
9187     { 0, +1 }
9188   };
9189
9190   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9191   {
9192     Tile[ax][ay] = EL_AMOEBA_DEAD;
9193     TEST_DrawLevelField(ax, ay);
9194     return;
9195   }
9196
9197   if (IS_ANIMATED(graphic))
9198     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9199
9200   if (!MovDelay[ax][ay])        // start making new amoeba field
9201     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9202
9203   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9204   {
9205     MovDelay[ax][ay]--;
9206     if (MovDelay[ax][ay])
9207       return;
9208   }
9209
9210   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9211   {
9212     int start = RND(4);
9213     int x = ax + xy[start][0];
9214     int y = ay + xy[start][1];
9215
9216     if (!IN_LEV_FIELD(x, y))
9217       return;
9218
9219     if (IS_FREE(x, y) ||
9220         CAN_GROW_INTO(Tile[x][y]) ||
9221         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9222         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9223     {
9224       newax = x;
9225       neway = y;
9226     }
9227
9228     if (newax == ax && neway == ay)
9229       return;
9230   }
9231   else                          // normal or "filled" (BD style) amoeba
9232   {
9233     int start = RND(4);
9234     boolean waiting_for_player = FALSE;
9235
9236     for (i = 0; i < NUM_DIRECTIONS; i++)
9237     {
9238       int j = (start + i) % 4;
9239       int x = ax + xy[j][0];
9240       int y = ay + xy[j][1];
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (IS_FREE(x, y) ||
9246           CAN_GROW_INTO(Tile[x][y]) ||
9247           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9248           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9249       {
9250         newax = x;
9251         neway = y;
9252         break;
9253       }
9254       else if (IS_PLAYER(x, y))
9255         waiting_for_player = TRUE;
9256     }
9257
9258     if (newax == ax && neway == ay)             // amoeba cannot grow
9259     {
9260       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9261       {
9262         Tile[ax][ay] = EL_AMOEBA_DEAD;
9263         TEST_DrawLevelField(ax, ay);
9264         AmoebaCnt[AmoebaNr[ax][ay]]--;
9265
9266         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9267         {
9268           if (element == EL_AMOEBA_FULL)
9269             AmoebaToDiamond(ax, ay);
9270           else if (element == EL_BD_AMOEBA)
9271             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9272         }
9273       }
9274       return;
9275     }
9276     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9277     {
9278       // amoeba gets larger by growing in some direction
9279
9280       int new_group_nr = AmoebaNr[ax][ay];
9281
9282 #ifdef DEBUG
9283   if (new_group_nr == 0)
9284   {
9285     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9286           newax, neway);
9287     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9288
9289     return;
9290   }
9291 #endif
9292
9293       AmoebaNr[newax][neway] = new_group_nr;
9294       AmoebaCnt[new_group_nr]++;
9295       AmoebaCnt2[new_group_nr]++;
9296
9297       // if amoeba touches other amoeba(s) after growing, unify them
9298       AmoebaMerge(newax, neway);
9299
9300       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9301       {
9302         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9303         return;
9304       }
9305     }
9306   }
9307
9308   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9309       (neway == lev_fieldy - 1 && newax != ax))
9310   {
9311     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9312     Store[newax][neway] = element;
9313   }
9314   else if (neway == ay || element == EL_EMC_DRIPPER)
9315   {
9316     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9317
9318     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9319   }
9320   else
9321   {
9322     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9323     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9324     Store[ax][ay] = EL_AMOEBA_DROP;
9325     ContinueMoving(ax, ay);
9326     return;
9327   }
9328
9329   TEST_DrawLevelField(newax, neway);
9330 }
9331
9332 static void Life(int ax, int ay)
9333 {
9334   int x1, y1, x2, y2;
9335   int life_time = 40;
9336   int element = Tile[ax][ay];
9337   int graphic = el2img(element);
9338   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9339                          level.biomaze);
9340   boolean changed = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (Stop[ax][ay])
9346     return;
9347
9348   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9349     MovDelay[ax][ay] = life_time;
9350
9351   if (MovDelay[ax][ay])         // wait some time before next cycle
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9359   {
9360     int xx = ax+x1, yy = ay+y1;
9361     int old_element = Tile[xx][yy];
9362     int num_neighbours = 0;
9363
9364     if (!IN_LEV_FIELD(xx, yy))
9365       continue;
9366
9367     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9368     {
9369       int x = xx+x2, y = yy+y2;
9370
9371       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9372         continue;
9373
9374       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9375       boolean is_neighbour = FALSE;
9376
9377       if (level.use_life_bugs)
9378         is_neighbour =
9379           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9380            (IS_FREE(x, y)                             &&  Stop[x][y]));
9381       else
9382         is_neighbour =
9383           (Last[x][y] == element || is_player_cell);
9384
9385       if (is_neighbour)
9386         num_neighbours++;
9387     }
9388
9389     boolean is_free = FALSE;
9390
9391     if (level.use_life_bugs)
9392       is_free = (IS_FREE(xx, yy));
9393     else
9394       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9395
9396     if (xx == ax && yy == ay)           // field in the middle
9397     {
9398       if (num_neighbours < life_parameter[0] ||
9399           num_neighbours > life_parameter[1])
9400       {
9401         Tile[xx][yy] = EL_EMPTY;
9402         if (Tile[xx][yy] != old_element)
9403           TEST_DrawLevelField(xx, yy);
9404         Stop[xx][yy] = TRUE;
9405         changed = TRUE;
9406       }
9407     }
9408     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9409     {                                   // free border field
9410       if (num_neighbours >= life_parameter[2] &&
9411           num_neighbours <= life_parameter[3])
9412       {
9413         Tile[xx][yy] = element;
9414         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9415         if (Tile[xx][yy] != old_element)
9416           TEST_DrawLevelField(xx, yy);
9417         Stop[xx][yy] = TRUE;
9418         changed = TRUE;
9419       }
9420     }
9421   }
9422
9423   if (changed)
9424     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9425                    SND_GAME_OF_LIFE_GROWING);
9426 }
9427
9428 static void InitRobotWheel(int x, int y)
9429 {
9430   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9431 }
9432
9433 static void RunRobotWheel(int x, int y)
9434 {
9435   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9436 }
9437
9438 static void StopRobotWheel(int x, int y)
9439 {
9440   if (game.robot_wheel_x == x &&
9441       game.robot_wheel_y == y)
9442   {
9443     game.robot_wheel_x = -1;
9444     game.robot_wheel_y = -1;
9445     game.robot_wheel_active = FALSE;
9446   }
9447 }
9448
9449 static void InitTimegateWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunTimegateWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9457 }
9458
9459 static void InitMagicBallDelay(int x, int y)
9460 {
9461   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9462 }
9463
9464 static void ActivateMagicBall(int bx, int by)
9465 {
9466   int x, y;
9467
9468   if (level.ball_random)
9469   {
9470     int pos_border = RND(8);    // select one of the eight border elements
9471     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9472     int xx = pos_content % 3;
9473     int yy = pos_content / 3;
9474
9475     x = bx - 1 + xx;
9476     y = by - 1 + yy;
9477
9478     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9479       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9480   }
9481   else
9482   {
9483     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9484     {
9485       int xx = x - bx + 1;
9486       int yy = y - by + 1;
9487
9488       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9489         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9490     }
9491   }
9492
9493   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9494 }
9495
9496 static void CheckExit(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitEM(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_EM_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSteel(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_STEEL_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteelEM(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_EM_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSP(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0)
9599   {
9600     int element = Tile[x][y];
9601     int graphic = el2img(element);
9602
9603     if (IS_ANIMATED(graphic))
9604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9605
9606     return;
9607   }
9608
9609   // do not re-open exit door closed after last player
9610   if (game.all_players_gone)
9611     return;
9612
9613   Tile[x][y] = EL_SP_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9616 }
9617
9618 static void CloseAllOpenTimegates(void)
9619 {
9620   int x, y;
9621
9622   SCAN_PLAYFIELD(x, y)
9623   {
9624     int element = Tile[x][y];
9625
9626     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9627     {
9628       Tile[x][y] = EL_TIMEGATE_CLOSING;
9629
9630       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9631     }
9632   }
9633 }
9634
9635 static void DrawTwinkleOnField(int x, int y)
9636 {
9637   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9638     return;
9639
9640   if (Tile[x][y] == EL_BD_DIAMOND)
9641     return;
9642
9643   if (MovDelay[x][y] == 0)      // next animation frame
9644     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9645
9646   if (MovDelay[x][y] != 0)      // wait some time before next frame
9647   {
9648     MovDelay[x][y]--;
9649
9650     DrawLevelElementAnimation(x, y, Tile[x][y]);
9651
9652     if (MovDelay[x][y] != 0)
9653     {
9654       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9655                                            10 - MovDelay[x][y]);
9656
9657       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9658     }
9659   }
9660 }
9661
9662 static void MauerWaechst(int x, int y)
9663 {
9664   int delay = 6;
9665
9666   if (!MovDelay[x][y])          // next animation frame
9667     MovDelay[x][y] = 3 * delay;
9668
9669   if (MovDelay[x][y])           // wait some time before next frame
9670   {
9671     MovDelay[x][y]--;
9672
9673     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9674     {
9675       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9676       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9677
9678       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9679     }
9680
9681     if (!MovDelay[x][y])
9682     {
9683       if (MovDir[x][y] == MV_LEFT)
9684       {
9685         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9686           TEST_DrawLevelField(x - 1, y);
9687       }
9688       else if (MovDir[x][y] == MV_RIGHT)
9689       {
9690         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9691           TEST_DrawLevelField(x + 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_UP)
9694       {
9695         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9696           TEST_DrawLevelField(x, y - 1);
9697       }
9698       else
9699       {
9700         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9701           TEST_DrawLevelField(x, y + 1);
9702       }
9703
9704       Tile[x][y] = Store[x][y];
9705       Store[x][y] = 0;
9706       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9707       TEST_DrawLevelField(x, y);
9708     }
9709   }
9710 }
9711
9712 static void MauerAbleger(int ax, int ay)
9713 {
9714   int element = Tile[ax][ay];
9715   int graphic = el2img(element);
9716   boolean oben_frei = FALSE, unten_frei = FALSE;
9717   boolean links_frei = FALSE, rechts_frei = FALSE;
9718   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9719   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9720   boolean new_wall = FALSE;
9721
9722   if (IS_ANIMATED(graphic))
9723     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9724
9725   if (!MovDelay[ax][ay])        // start building new wall
9726     MovDelay[ax][ay] = 6;
9727
9728   if (MovDelay[ax][ay])         // wait some time before building new wall
9729   {
9730     MovDelay[ax][ay]--;
9731     if (MovDelay[ax][ay])
9732       return;
9733   }
9734
9735   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9736     oben_frei = TRUE;
9737   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9738     unten_frei = TRUE;
9739   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9740     links_frei = TRUE;
9741   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9742     rechts_frei = TRUE;
9743
9744   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9745       element == EL_EXPANDABLE_WALL_ANY)
9746   {
9747     if (oben_frei)
9748     {
9749       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9750       Store[ax][ay-1] = element;
9751       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9752       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9753         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9754                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9755       new_wall = TRUE;
9756     }
9757     if (unten_frei)
9758     {
9759       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9760       Store[ax][ay+1] = element;
9761       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9762       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9763         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9764                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9765       new_wall = TRUE;
9766     }
9767   }
9768
9769   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9770       element == EL_EXPANDABLE_WALL_ANY ||
9771       element == EL_EXPANDABLE_WALL ||
9772       element == EL_BD_EXPANDABLE_WALL)
9773   {
9774     if (links_frei)
9775     {
9776       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9777       Store[ax-1][ay] = element;
9778       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9779       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9780         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9781                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9782       new_wall = TRUE;
9783     }
9784
9785     if (rechts_frei)
9786     {
9787       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9788       Store[ax+1][ay] = element;
9789       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9790       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9791         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9792                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9793       new_wall = TRUE;
9794     }
9795   }
9796
9797   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9798     TEST_DrawLevelField(ax, ay);
9799
9800   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9801     oben_massiv = TRUE;
9802   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9803     unten_massiv = TRUE;
9804   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9805     links_massiv = TRUE;
9806   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9807     rechts_massiv = TRUE;
9808
9809   if (((oben_massiv && unten_massiv) ||
9810        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9811        element == EL_EXPANDABLE_WALL) &&
9812       ((links_massiv && rechts_massiv) ||
9813        element == EL_EXPANDABLE_WALL_VERTICAL))
9814     Tile[ax][ay] = EL_WALL;
9815
9816   if (new_wall)
9817     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9818 }
9819
9820 static void MauerAblegerStahl(int ax, int ay)
9821 {
9822   int element = Tile[ax][ay];
9823   int graphic = el2img(element);
9824   boolean oben_frei = FALSE, unten_frei = FALSE;
9825   boolean links_frei = FALSE, rechts_frei = FALSE;
9826   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9827   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9828   boolean new_wall = FALSE;
9829
9830   if (IS_ANIMATED(graphic))
9831     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9832
9833   if (!MovDelay[ax][ay])        // start building new wall
9834     MovDelay[ax][ay] = 6;
9835
9836   if (MovDelay[ax][ay])         // wait some time before building new wall
9837   {
9838     MovDelay[ax][ay]--;
9839     if (MovDelay[ax][ay])
9840       return;
9841   }
9842
9843   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9844     oben_frei = TRUE;
9845   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9846     unten_frei = TRUE;
9847   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9848     links_frei = TRUE;
9849   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9850     rechts_frei = TRUE;
9851
9852   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9853       element == EL_EXPANDABLE_STEELWALL_ANY)
9854   {
9855     if (oben_frei)
9856     {
9857       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9858       Store[ax][ay-1] = element;
9859       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9860       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9861         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9862                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9863       new_wall = TRUE;
9864     }
9865     if (unten_frei)
9866     {
9867       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9868       Store[ax][ay+1] = element;
9869       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9870       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9871         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9872                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9873       new_wall = TRUE;
9874     }
9875   }
9876
9877   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9878       element == EL_EXPANDABLE_STEELWALL_ANY)
9879   {
9880     if (links_frei)
9881     {
9882       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9883       Store[ax-1][ay] = element;
9884       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9885       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9886         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9887                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9888       new_wall = TRUE;
9889     }
9890
9891     if (rechts_frei)
9892     {
9893       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9894       Store[ax+1][ay] = element;
9895       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9896       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9897         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9898                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9899       new_wall = TRUE;
9900     }
9901   }
9902
9903   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9904     oben_massiv = TRUE;
9905   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9906     unten_massiv = TRUE;
9907   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9908     links_massiv = TRUE;
9909   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9910     rechts_massiv = TRUE;
9911
9912   if (((oben_massiv && unten_massiv) ||
9913        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9914       ((links_massiv && rechts_massiv) ||
9915        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9916     Tile[ax][ay] = EL_STEELWALL;
9917
9918   if (new_wall)
9919     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9920 }
9921
9922 static void CheckForDragon(int x, int y)
9923 {
9924   int i, j;
9925   boolean dragon_found = FALSE;
9926   static int xy[4][2] =
9927   {
9928     { 0, -1 },
9929     { -1, 0 },
9930     { +1, 0 },
9931     { 0, +1 }
9932   };
9933
9934   for (i = 0; i < NUM_DIRECTIONS; i++)
9935   {
9936     for (j = 0; j < 4; j++)
9937     {
9938       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9939
9940       if (IN_LEV_FIELD(xx, yy) &&
9941           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9942       {
9943         if (Tile[xx][yy] == EL_DRAGON)
9944           dragon_found = TRUE;
9945       }
9946       else
9947         break;
9948     }
9949   }
9950
9951   if (!dragon_found)
9952   {
9953     for (i = 0; i < NUM_DIRECTIONS; i++)
9954     {
9955       for (j = 0; j < 3; j++)
9956       {
9957         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9958   
9959         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9960         {
9961           Tile[xx][yy] = EL_EMPTY;
9962           TEST_DrawLevelField(xx, yy);
9963         }
9964         else
9965           break;
9966       }
9967     }
9968   }
9969 }
9970
9971 static void InitBuggyBase(int x, int y)
9972 {
9973   int element = Tile[x][y];
9974   int activating_delay = FRAMES_PER_SECOND / 4;
9975
9976   ChangeDelay[x][y] =
9977     (element == EL_SP_BUGGY_BASE ?
9978      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9979      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9980      activating_delay :
9981      element == EL_SP_BUGGY_BASE_ACTIVE ?
9982      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9983 }
9984
9985 static void WarnBuggyBase(int x, int y)
9986 {
9987   int i;
9988   static int xy[4][2] =
9989   {
9990     { 0, -1 },
9991     { -1, 0 },
9992     { +1, 0 },
9993     { 0, +1 }
9994   };
9995
9996   for (i = 0; i < NUM_DIRECTIONS; i++)
9997   {
9998     int xx = x + xy[i][0];
9999     int yy = y + xy[i][1];
10000
10001     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10002     {
10003       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10004
10005       break;
10006     }
10007   }
10008 }
10009
10010 static void InitTrap(int x, int y)
10011 {
10012   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10013 }
10014
10015 static void ActivateTrap(int x, int y)
10016 {
10017   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10018 }
10019
10020 static void ChangeActiveTrap(int x, int y)
10021 {
10022   int graphic = IMG_TRAP_ACTIVE;
10023
10024   // if new animation frame was drawn, correct crumbled sand border
10025   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10026     TEST_DrawLevelFieldCrumbled(x, y);
10027 }
10028
10029 static int getSpecialActionElement(int element, int number, int base_element)
10030 {
10031   return (element != EL_EMPTY ? element :
10032           number != -1 ? base_element + number - 1 :
10033           EL_EMPTY);
10034 }
10035
10036 static int getModifiedActionNumber(int value_old, int operator, int operand,
10037                                    int value_min, int value_max)
10038 {
10039   int value_new = (operator == CA_MODE_SET      ? operand :
10040                    operator == CA_MODE_ADD      ? value_old + operand :
10041                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10042                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10043                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10044                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10045                    value_old);
10046
10047   return (value_new < value_min ? value_min :
10048           value_new > value_max ? value_max :
10049           value_new);
10050 }
10051
10052 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10053 {
10054   struct ElementInfo *ei = &element_info[element];
10055   struct ElementChangeInfo *change = &ei->change_page[page];
10056   int target_element = change->target_element;
10057   int action_type = change->action_type;
10058   int action_mode = change->action_mode;
10059   int action_arg = change->action_arg;
10060   int action_element = change->action_element;
10061   int i;
10062
10063   if (!change->has_action)
10064     return;
10065
10066   // ---------- determine action paramater values -----------------------------
10067
10068   int level_time_value =
10069     (level.time > 0 ? TimeLeft :
10070      TimePlayed);
10071
10072   int action_arg_element_raw =
10073     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10074      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10075      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10076      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10077      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10078      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10079      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10080      EL_EMPTY);
10081   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10082
10083   int action_arg_direction =
10084     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10085      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10086      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10087      change->actual_trigger_side :
10088      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10089      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10090      MV_NONE);
10091
10092   int action_arg_number_min =
10093     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10094      CA_ARG_MIN);
10095
10096   int action_arg_number_max =
10097     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10098      action_type == CA_SET_LEVEL_GEMS ? 999 :
10099      action_type == CA_SET_LEVEL_TIME ? 9999 :
10100      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10101      action_type == CA_SET_CE_VALUE ? 9999 :
10102      action_type == CA_SET_CE_SCORE ? 9999 :
10103      CA_ARG_MAX);
10104
10105   int action_arg_number_reset =
10106     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10107      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10108      action_type == CA_SET_LEVEL_TIME ? level.time :
10109      action_type == CA_SET_LEVEL_SCORE ? 0 :
10110      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10111      action_type == CA_SET_CE_SCORE ? 0 :
10112      0);
10113
10114   int action_arg_number =
10115     (action_arg <= CA_ARG_MAX ? action_arg :
10116      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10117      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10118      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10119      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10120      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10121      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10122      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10123      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10124      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10125      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10126      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10127      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10128      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10129      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10130      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10131      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10132      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10133      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10134      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10135      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10136      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10137      -1);
10138
10139   int action_arg_number_old =
10140     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10141      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10142      action_type == CA_SET_LEVEL_SCORE ? game.score :
10143      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10144      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10145      0);
10146
10147   int action_arg_number_new =
10148     getModifiedActionNumber(action_arg_number_old,
10149                             action_mode, action_arg_number,
10150                             action_arg_number_min, action_arg_number_max);
10151
10152   int trigger_player_bits =
10153     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10154      change->actual_trigger_player_bits : change->trigger_player);
10155
10156   int action_arg_player_bits =
10157     (action_arg >= CA_ARG_PLAYER_1 &&
10158      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10159      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10160      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10161      PLAYER_BITS_ANY);
10162
10163   // ---------- execute action  -----------------------------------------------
10164
10165   switch (action_type)
10166   {
10167     case CA_NO_ACTION:
10168     {
10169       return;
10170     }
10171
10172     // ---------- level actions  ----------------------------------------------
10173
10174     case CA_RESTART_LEVEL:
10175     {
10176       game.restart_level = TRUE;
10177
10178       break;
10179     }
10180
10181     case CA_SHOW_ENVELOPE:
10182     {
10183       int element = getSpecialActionElement(action_arg_element,
10184                                             action_arg_number, EL_ENVELOPE_1);
10185
10186       if (IS_ENVELOPE(element))
10187         local_player->show_envelope = element;
10188
10189       break;
10190     }
10191
10192     case CA_SET_LEVEL_TIME:
10193     {
10194       if (level.time > 0)       // only modify limited time value
10195       {
10196         TimeLeft = action_arg_number_new;
10197
10198         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10199
10200         DisplayGameControlValues();
10201
10202         if (!TimeLeft && setup.time_limit)
10203           for (i = 0; i < MAX_PLAYERS; i++)
10204             KillPlayer(&stored_player[i]);
10205       }
10206
10207       break;
10208     }
10209
10210     case CA_SET_LEVEL_SCORE:
10211     {
10212       game.score = action_arg_number_new;
10213
10214       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10215
10216       DisplayGameControlValues();
10217
10218       break;
10219     }
10220
10221     case CA_SET_LEVEL_GEMS:
10222     {
10223       game.gems_still_needed = action_arg_number_new;
10224
10225       game.snapshot.collected_item = TRUE;
10226
10227       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10228
10229       DisplayGameControlValues();
10230
10231       break;
10232     }
10233
10234     case CA_SET_LEVEL_WIND:
10235     {
10236       game.wind_direction = action_arg_direction;
10237
10238       break;
10239     }
10240
10241     case CA_SET_LEVEL_RANDOM_SEED:
10242     {
10243       // ensure that setting a new random seed while playing is predictable
10244       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10245
10246       break;
10247     }
10248
10249     // ---------- player actions  ---------------------------------------------
10250
10251     case CA_MOVE_PLAYER:
10252     case CA_MOVE_PLAYER_NEW:
10253     {
10254       // automatically move to the next field in specified direction
10255       for (i = 0; i < MAX_PLAYERS; i++)
10256         if (trigger_player_bits & (1 << i))
10257           if (action_type == CA_MOVE_PLAYER ||
10258               stored_player[i].MovPos == 0)
10259             stored_player[i].programmed_action = action_arg_direction;
10260
10261       break;
10262     }
10263
10264     case CA_EXIT_PLAYER:
10265     {
10266       for (i = 0; i < MAX_PLAYERS; i++)
10267         if (action_arg_player_bits & (1 << i))
10268           ExitPlayer(&stored_player[i]);
10269
10270       if (game.players_still_needed == 0)
10271         LevelSolved();
10272
10273       break;
10274     }
10275
10276     case CA_KILL_PLAYER:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279         if (action_arg_player_bits & (1 << i))
10280           KillPlayer(&stored_player[i]);
10281
10282       break;
10283     }
10284
10285     case CA_SET_PLAYER_KEYS:
10286     {
10287       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10288       int element = getSpecialActionElement(action_arg_element,
10289                                             action_arg_number, EL_KEY_1);
10290
10291       if (IS_KEY(element))
10292       {
10293         for (i = 0; i < MAX_PLAYERS; i++)
10294         {
10295           if (trigger_player_bits & (1 << i))
10296           {
10297             stored_player[i].key[KEY_NR(element)] = key_state;
10298
10299             DrawGameDoorValues();
10300           }
10301         }
10302       }
10303
10304       break;
10305     }
10306
10307     case CA_SET_PLAYER_SPEED:
10308     {
10309       for (i = 0; i < MAX_PLAYERS; i++)
10310       {
10311         if (trigger_player_bits & (1 << i))
10312         {
10313           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10314
10315           if (action_arg == CA_ARG_SPEED_FASTER &&
10316               stored_player[i].cannot_move)
10317           {
10318             action_arg_number = STEPSIZE_VERY_SLOW;
10319           }
10320           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10321                    action_arg == CA_ARG_SPEED_FASTER)
10322           {
10323             action_arg_number = 2;
10324             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10325                            CA_MODE_MULTIPLY);
10326           }
10327           else if (action_arg == CA_ARG_NUMBER_RESET)
10328           {
10329             action_arg_number = level.initial_player_stepsize[i];
10330           }
10331
10332           move_stepsize =
10333             getModifiedActionNumber(move_stepsize,
10334                                     action_mode,
10335                                     action_arg_number,
10336                                     action_arg_number_min,
10337                                     action_arg_number_max);
10338
10339           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10340         }
10341       }
10342
10343       break;
10344     }
10345
10346     case CA_SET_PLAYER_SHIELD:
10347     {
10348       for (i = 0; i < MAX_PLAYERS; i++)
10349       {
10350         if (trigger_player_bits & (1 << i))
10351         {
10352           if (action_arg == CA_ARG_SHIELD_OFF)
10353           {
10354             stored_player[i].shield_normal_time_left = 0;
10355             stored_player[i].shield_deadly_time_left = 0;
10356           }
10357           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10358           {
10359             stored_player[i].shield_normal_time_left = 999999;
10360           }
10361           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10362           {
10363             stored_player[i].shield_normal_time_left = 999999;
10364             stored_player[i].shield_deadly_time_left = 999999;
10365           }
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_PLAYER_GRAVITY:
10373     {
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375       {
10376         if (trigger_player_bits & (1 << i))
10377         {
10378           stored_player[i].gravity =
10379             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10380              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10381              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10382              stored_player[i].gravity);
10383         }
10384       }
10385
10386       break;
10387     }
10388
10389     case CA_SET_PLAYER_ARTWORK:
10390     {
10391       for (i = 0; i < MAX_PLAYERS; i++)
10392       {
10393         if (trigger_player_bits & (1 << i))
10394         {
10395           int artwork_element = action_arg_element;
10396
10397           if (action_arg == CA_ARG_ELEMENT_RESET)
10398             artwork_element =
10399               (level.use_artwork_element[i] ? level.artwork_element[i] :
10400                stored_player[i].element_nr);
10401
10402           if (stored_player[i].artwork_element != artwork_element)
10403             stored_player[i].Frame = 0;
10404
10405           stored_player[i].artwork_element = artwork_element;
10406
10407           SetPlayerWaiting(&stored_player[i], FALSE);
10408
10409           // set number of special actions for bored and sleeping animation
10410           stored_player[i].num_special_action_bored =
10411             get_num_special_action(artwork_element,
10412                                    ACTION_BORING_1, ACTION_BORING_LAST);
10413           stored_player[i].num_special_action_sleeping =
10414             get_num_special_action(artwork_element,
10415                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10416         }
10417       }
10418
10419       break;
10420     }
10421
10422     case CA_SET_PLAYER_INVENTORY:
10423     {
10424       for (i = 0; i < MAX_PLAYERS; i++)
10425       {
10426         struct PlayerInfo *player = &stored_player[i];
10427         int j, k;
10428
10429         if (trigger_player_bits & (1 << i))
10430         {
10431           int inventory_element = action_arg_element;
10432
10433           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10434               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10435               action_arg == CA_ARG_ELEMENT_ACTION)
10436           {
10437             int element = inventory_element;
10438             int collect_count = element_info[element].collect_count_initial;
10439
10440             if (!IS_CUSTOM_ELEMENT(element))
10441               collect_count = 1;
10442
10443             if (collect_count == 0)
10444               player->inventory_infinite_element = element;
10445             else
10446               for (k = 0; k < collect_count; k++)
10447                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10448                   player->inventory_element[player->inventory_size++] =
10449                     element;
10450           }
10451           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10452                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10453                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10454           {
10455             if (player->inventory_infinite_element != EL_UNDEFINED &&
10456                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10457                                      action_arg_element_raw))
10458               player->inventory_infinite_element = EL_UNDEFINED;
10459
10460             for (k = 0, j = 0; j < player->inventory_size; j++)
10461             {
10462               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10463                                         action_arg_element_raw))
10464                 player->inventory_element[k++] = player->inventory_element[j];
10465             }
10466
10467             player->inventory_size = k;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10470           {
10471             if (player->inventory_size > 0)
10472             {
10473               for (j = 0; j < player->inventory_size - 1; j++)
10474                 player->inventory_element[j] = player->inventory_element[j + 1];
10475
10476               player->inventory_size--;
10477             }
10478           }
10479           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10480           {
10481             if (player->inventory_size > 0)
10482               player->inventory_size--;
10483           }
10484           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10485           {
10486             player->inventory_infinite_element = EL_UNDEFINED;
10487             player->inventory_size = 0;
10488           }
10489           else if (action_arg == CA_ARG_INVENTORY_RESET)
10490           {
10491             player->inventory_infinite_element = EL_UNDEFINED;
10492             player->inventory_size = 0;
10493
10494             if (level.use_initial_inventory[i])
10495             {
10496               for (j = 0; j < level.initial_inventory_size[i]; j++)
10497               {
10498                 int element = level.initial_inventory_content[i][j];
10499                 int collect_count = element_info[element].collect_count_initial;
10500
10501                 if (!IS_CUSTOM_ELEMENT(element))
10502                   collect_count = 1;
10503
10504                 if (collect_count == 0)
10505                   player->inventory_infinite_element = element;
10506                 else
10507                   for (k = 0; k < collect_count; k++)
10508                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10509                       player->inventory_element[player->inventory_size++] =
10510                         element;
10511               }
10512             }
10513           }
10514         }
10515       }
10516
10517       break;
10518     }
10519
10520     // ---------- CE actions  -------------------------------------------------
10521
10522     case CA_SET_CE_VALUE:
10523     {
10524       int last_ce_value = CustomValue[x][y];
10525
10526       CustomValue[x][y] = action_arg_number_new;
10527
10528       if (CustomValue[x][y] != last_ce_value)
10529       {
10530         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10531         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10532
10533         if (CustomValue[x][y] == 0)
10534         {
10535           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10536           ChangeCount[x][y] = 0;        // allow at least one more change
10537
10538           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10539           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10540         }
10541       }
10542
10543       break;
10544     }
10545
10546     case CA_SET_CE_SCORE:
10547     {
10548       int last_ce_score = ei->collect_score;
10549
10550       ei->collect_score = action_arg_number_new;
10551
10552       if (ei->collect_score != last_ce_score)
10553       {
10554         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10555         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10556
10557         if (ei->collect_score == 0)
10558         {
10559           int xx, yy;
10560
10561           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10562           ChangeCount[x][y] = 0;        // allow at least one more change
10563
10564           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10565           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10566
10567           /*
10568             This is a very special case that seems to be a mixture between
10569             CheckElementChange() and CheckTriggeredElementChange(): while
10570             the first one only affects single elements that are triggered
10571             directly, the second one affects multiple elements in the playfield
10572             that are triggered indirectly by another element. This is a third
10573             case: Changing the CE score always affects multiple identical CEs,
10574             so every affected CE must be checked, not only the single CE for
10575             which the CE score was changed in the first place (as every instance
10576             of that CE shares the same CE score, and therefore also can change)!
10577           */
10578           SCAN_PLAYFIELD(xx, yy)
10579           {
10580             if (Tile[xx][yy] == element)
10581               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10582                                  CE_SCORE_GETS_ZERO);
10583           }
10584         }
10585       }
10586
10587       break;
10588     }
10589
10590     case CA_SET_CE_ARTWORK:
10591     {
10592       int artwork_element = action_arg_element;
10593       boolean reset_frame = FALSE;
10594       int xx, yy;
10595
10596       if (action_arg == CA_ARG_ELEMENT_RESET)
10597         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10598                            element);
10599
10600       if (ei->gfx_element != artwork_element)
10601         reset_frame = TRUE;
10602
10603       ei->gfx_element = artwork_element;
10604
10605       SCAN_PLAYFIELD(xx, yy)
10606       {
10607         if (Tile[xx][yy] == element)
10608         {
10609           if (reset_frame)
10610           {
10611             ResetGfxAnimation(xx, yy);
10612             ResetRandomAnimationValue(xx, yy);
10613           }
10614
10615           TEST_DrawLevelField(xx, yy);
10616         }
10617       }
10618
10619       break;
10620     }
10621
10622     // ---------- engine actions  ---------------------------------------------
10623
10624     case CA_SET_ENGINE_SCAN_MODE:
10625     {
10626       InitPlayfieldScanMode(action_arg);
10627
10628       break;
10629     }
10630
10631     default:
10632       break;
10633   }
10634 }
10635
10636 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10637 {
10638   int old_element = Tile[x][y];
10639   int new_element = GetElementFromGroupElement(element);
10640   int previous_move_direction = MovDir[x][y];
10641   int last_ce_value = CustomValue[x][y];
10642   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10643   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10644   boolean add_player_onto_element = (new_element_is_player &&
10645                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10646                                      IS_WALKABLE(old_element));
10647
10648   if (!add_player_onto_element)
10649   {
10650     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10651       RemoveMovingField(x, y);
10652     else
10653       RemoveField(x, y);
10654
10655     Tile[x][y] = new_element;
10656
10657     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10658       MovDir[x][y] = previous_move_direction;
10659
10660     if (element_info[new_element].use_last_ce_value)
10661       CustomValue[x][y] = last_ce_value;
10662
10663     InitField_WithBug1(x, y, FALSE);
10664
10665     new_element = Tile[x][y];   // element may have changed
10666
10667     ResetGfxAnimation(x, y);
10668     ResetRandomAnimationValue(x, y);
10669
10670     TEST_DrawLevelField(x, y);
10671
10672     if (GFX_CRUMBLED(new_element))
10673       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10674   }
10675
10676   // check if element under the player changes from accessible to unaccessible
10677   // (needed for special case of dropping element which then changes)
10678   // (must be checked after creating new element for walkable group elements)
10679   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10680       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10681   {
10682     Bang(x, y);
10683
10684     return;
10685   }
10686
10687   // "ChangeCount" not set yet to allow "entered by player" change one time
10688   if (new_element_is_player)
10689     RelocatePlayer(x, y, new_element);
10690
10691   if (is_change)
10692     ChangeCount[x][y]++;        // count number of changes in the same frame
10693
10694   TestIfBadThingTouchesPlayer(x, y);
10695   TestIfPlayerTouchesCustomElement(x, y);
10696   TestIfElementTouchesCustomElement(x, y);
10697 }
10698
10699 static void CreateField(int x, int y, int element)
10700 {
10701   CreateFieldExt(x, y, element, FALSE);
10702 }
10703
10704 static void CreateElementFromChange(int x, int y, int element)
10705 {
10706   element = GET_VALID_RUNTIME_ELEMENT(element);
10707
10708   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10709   {
10710     int old_element = Tile[x][y];
10711
10712     // prevent changed element from moving in same engine frame
10713     // unless both old and new element can either fall or move
10714     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10715         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10716       Stop[x][y] = TRUE;
10717   }
10718
10719   CreateFieldExt(x, y, element, TRUE);
10720 }
10721
10722 static boolean ChangeElement(int x, int y, int element, int page)
10723 {
10724   struct ElementInfo *ei = &element_info[element];
10725   struct ElementChangeInfo *change = &ei->change_page[page];
10726   int ce_value = CustomValue[x][y];
10727   int ce_score = ei->collect_score;
10728   int target_element;
10729   int old_element = Tile[x][y];
10730
10731   // always use default change event to prevent running into a loop
10732   if (ChangeEvent[x][y] == -1)
10733     ChangeEvent[x][y] = CE_DELAY;
10734
10735   if (ChangeEvent[x][y] == CE_DELAY)
10736   {
10737     // reset actual trigger element, trigger player and action element
10738     change->actual_trigger_element = EL_EMPTY;
10739     change->actual_trigger_player = EL_EMPTY;
10740     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10741     change->actual_trigger_side = CH_SIDE_NONE;
10742     change->actual_trigger_ce_value = 0;
10743     change->actual_trigger_ce_score = 0;
10744   }
10745
10746   // do not change elements more than a specified maximum number of changes
10747   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10748     return FALSE;
10749
10750   ChangeCount[x][y]++;          // count number of changes in the same frame
10751
10752   if (change->explode)
10753   {
10754     Bang(x, y);
10755
10756     return TRUE;
10757   }
10758
10759   if (change->use_target_content)
10760   {
10761     boolean complete_replace = TRUE;
10762     boolean can_replace[3][3];
10763     int xx, yy;
10764
10765     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10766     {
10767       boolean is_empty;
10768       boolean is_walkable;
10769       boolean is_diggable;
10770       boolean is_collectible;
10771       boolean is_removable;
10772       boolean is_destructible;
10773       int ex = x + xx - 1;
10774       int ey = y + yy - 1;
10775       int content_element = change->target_content.e[xx][yy];
10776       int e;
10777
10778       can_replace[xx][yy] = TRUE;
10779
10780       if (ex == x && ey == y)   // do not check changing element itself
10781         continue;
10782
10783       if (content_element == EL_EMPTY_SPACE)
10784       {
10785         can_replace[xx][yy] = FALSE;    // do not replace border with space
10786
10787         continue;
10788       }
10789
10790       if (!IN_LEV_FIELD(ex, ey))
10791       {
10792         can_replace[xx][yy] = FALSE;
10793         complete_replace = FALSE;
10794
10795         continue;
10796       }
10797
10798       e = Tile[ex][ey];
10799
10800       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10801         e = MovingOrBlocked2Element(ex, ey);
10802
10803       is_empty = (IS_FREE(ex, ey) ||
10804                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10805
10806       is_walkable     = (is_empty || IS_WALKABLE(e));
10807       is_diggable     = (is_empty || IS_DIGGABLE(e));
10808       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10809       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10810       is_removable    = (is_diggable || is_collectible);
10811
10812       can_replace[xx][yy] =
10813         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10814           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10815           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10816           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10817           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10818           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10819          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10820
10821       if (!can_replace[xx][yy])
10822         complete_replace = FALSE;
10823     }
10824
10825     if (!change->only_if_complete || complete_replace)
10826     {
10827       boolean something_has_changed = FALSE;
10828
10829       if (change->only_if_complete && change->use_random_replace &&
10830           RND(100) < change->random_percentage)
10831         return FALSE;
10832
10833       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10834       {
10835         int ex = x + xx - 1;
10836         int ey = y + yy - 1;
10837         int content_element;
10838
10839         if (can_replace[xx][yy] && (!change->use_random_replace ||
10840                                     RND(100) < change->random_percentage))
10841         {
10842           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10843             RemoveMovingField(ex, ey);
10844
10845           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10846
10847           content_element = change->target_content.e[xx][yy];
10848           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10849                                               ce_value, ce_score);
10850
10851           CreateElementFromChange(ex, ey, target_element);
10852
10853           something_has_changed = TRUE;
10854
10855           // for symmetry reasons, freeze newly created border elements
10856           if (ex != x || ey != y)
10857             Stop[ex][ey] = TRUE;        // no more moving in this frame
10858         }
10859       }
10860
10861       if (something_has_changed)
10862       {
10863         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10864         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10865       }
10866     }
10867   }
10868   else
10869   {
10870     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10871                                         ce_value, ce_score);
10872
10873     if (element == EL_DIAGONAL_GROWING ||
10874         element == EL_DIAGONAL_SHRINKING)
10875     {
10876       target_element = Store[x][y];
10877
10878       Store[x][y] = EL_EMPTY;
10879     }
10880
10881     // special case: element changes to player (and may be kept if walkable)
10882     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10883       CreateElementFromChange(x, y, EL_EMPTY);
10884
10885     CreateElementFromChange(x, y, target_element);
10886
10887     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10888     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10889   }
10890
10891   // this uses direct change before indirect change
10892   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10893
10894   return TRUE;
10895 }
10896
10897 static void HandleElementChange(int x, int y, int page)
10898 {
10899   int element = MovingOrBlocked2Element(x, y);
10900   struct ElementInfo *ei = &element_info[element];
10901   struct ElementChangeInfo *change = &ei->change_page[page];
10902   boolean handle_action_before_change = FALSE;
10903
10904 #ifdef DEBUG
10905   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10906       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10907   {
10908     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10909           x, y, element, element_info[element].token_name);
10910     Debug("game:playing:HandleElementChange", "This should never happen!");
10911   }
10912 #endif
10913
10914   // this can happen with classic bombs on walkable, changing elements
10915   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10916   {
10917     return;
10918   }
10919
10920   if (ChangeDelay[x][y] == 0)           // initialize element change
10921   {
10922     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10923
10924     if (change->can_change)
10925     {
10926       // !!! not clear why graphic animation should be reset at all here !!!
10927       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10928       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10929
10930       /*
10931         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10932
10933         When using an animation frame delay of 1 (this only happens with
10934         "sp_zonk.moving.left/right" in the classic graphics), the default
10935         (non-moving) animation shows wrong animation frames (while the
10936         moving animation, like "sp_zonk.moving.left/right", is correct,
10937         so this graphical bug never shows up with the classic graphics).
10938         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10939         be drawn instead of the correct frames 0,1,2,3. This is caused by
10940         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10941         an element change: First when the change delay ("ChangeDelay[][]")
10942         counter has reached zero after decrementing, then a second time in
10943         the next frame (after "GfxFrame[][]" was already incremented) when
10944         "ChangeDelay[][]" is reset to the initial delay value again.
10945
10946         This causes frame 0 to be drawn twice, while the last frame won't
10947         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10948
10949         As some animations may already be cleverly designed around this bug
10950         (at least the "Snake Bite" snake tail animation does this), it cannot
10951         simply be fixed here without breaking such existing animations.
10952         Unfortunately, it cannot easily be detected if a graphics set was
10953         designed "before" or "after" the bug was fixed. As a workaround,
10954         a new graphics set option "game.graphics_engine_version" was added
10955         to be able to specify the game's major release version for which the
10956         graphics set was designed, which can then be used to decide if the
10957         bugfix should be used (version 4 and above) or not (version 3 or
10958         below, or if no version was specified at all, as with old sets).
10959
10960         (The wrong/fixed animation frames can be tested with the test level set
10961         "test_gfxframe" and level "000", which contains a specially prepared
10962         custom element at level position (x/y) == (11/9) which uses the zonk
10963         animation mentioned above. Using "game.graphics_engine_version: 4"
10964         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10965         This can also be seen from the debug output for this test element.)
10966       */
10967
10968       // when a custom element is about to change (for example by change delay),
10969       // do not reset graphic animation when the custom element is moving
10970       if (game.graphics_engine_version < 4 &&
10971           !IS_MOVING(x, y))
10972       {
10973         ResetGfxAnimation(x, y);
10974         ResetRandomAnimationValue(x, y);
10975       }
10976
10977       if (change->pre_change_function)
10978         change->pre_change_function(x, y);
10979     }
10980   }
10981
10982   ChangeDelay[x][y]--;
10983
10984   if (ChangeDelay[x][y] != 0)           // continue element change
10985   {
10986     if (change->can_change)
10987     {
10988       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10989
10990       if (IS_ANIMATED(graphic))
10991         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10992
10993       if (change->change_function)
10994         change->change_function(x, y);
10995     }
10996   }
10997   else                                  // finish element change
10998   {
10999     if (ChangePage[x][y] != -1)         // remember page from delayed change
11000     {
11001       page = ChangePage[x][y];
11002       ChangePage[x][y] = -1;
11003
11004       change = &ei->change_page[page];
11005     }
11006
11007     if (IS_MOVING(x, y))                // never change a running system ;-)
11008     {
11009       ChangeDelay[x][y] = 1;            // try change after next move step
11010       ChangePage[x][y] = page;          // remember page to use for change
11011
11012       return;
11013     }
11014
11015     // special case: set new level random seed before changing element
11016     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11017       handle_action_before_change = TRUE;
11018
11019     if (change->has_action && handle_action_before_change)
11020       ExecuteCustomElementAction(x, y, element, page);
11021
11022     if (change->can_change)
11023     {
11024       if (ChangeElement(x, y, element, page))
11025       {
11026         if (change->post_change_function)
11027           change->post_change_function(x, y);
11028       }
11029     }
11030
11031     if (change->has_action && !handle_action_before_change)
11032       ExecuteCustomElementAction(x, y, element, page);
11033   }
11034 }
11035
11036 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11037                                               int trigger_element,
11038                                               int trigger_event,
11039                                               int trigger_player,
11040                                               int trigger_side,
11041                                               int trigger_page)
11042 {
11043   boolean change_done_any = FALSE;
11044   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11045   int i;
11046
11047   if (!(trigger_events[trigger_element][trigger_event]))
11048     return FALSE;
11049
11050   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11051
11052   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11053   {
11054     int element = EL_CUSTOM_START + i;
11055     boolean change_done = FALSE;
11056     int p;
11057
11058     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11059         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11060       continue;
11061
11062     for (p = 0; p < element_info[element].num_change_pages; p++)
11063     {
11064       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11065
11066       if (change->can_change_or_has_action &&
11067           change->has_event[trigger_event] &&
11068           change->trigger_side & trigger_side &&
11069           change->trigger_player & trigger_player &&
11070           change->trigger_page & trigger_page_bits &&
11071           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11072       {
11073         change->actual_trigger_element = trigger_element;
11074         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11075         change->actual_trigger_player_bits = trigger_player;
11076         change->actual_trigger_side = trigger_side;
11077         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11078         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11079
11080         if ((change->can_change && !change_done) || change->has_action)
11081         {
11082           int x, y;
11083
11084           SCAN_PLAYFIELD(x, y)
11085           {
11086             if (Tile[x][y] == element)
11087             {
11088               if (change->can_change && !change_done)
11089               {
11090                 // if element already changed in this frame, not only prevent
11091                 // another element change (checked in ChangeElement()), but
11092                 // also prevent additional element actions for this element
11093
11094                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11095                     !level.use_action_after_change_bug)
11096                   continue;
11097
11098                 ChangeDelay[x][y] = 1;
11099                 ChangeEvent[x][y] = trigger_event;
11100
11101                 HandleElementChange(x, y, p);
11102               }
11103               else if (change->has_action)
11104               {
11105                 // if element already changed in this frame, not only prevent
11106                 // another element change (checked in ChangeElement()), but
11107                 // also prevent additional element actions for this element
11108
11109                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11110                     !level.use_action_after_change_bug)
11111                   continue;
11112
11113                 ExecuteCustomElementAction(x, y, element, p);
11114                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11115               }
11116             }
11117           }
11118
11119           if (change->can_change)
11120           {
11121             change_done = TRUE;
11122             change_done_any = TRUE;
11123           }
11124         }
11125       }
11126     }
11127   }
11128
11129   RECURSION_LOOP_DETECTION_END();
11130
11131   return change_done_any;
11132 }
11133
11134 static boolean CheckElementChangeExt(int x, int y,
11135                                      int element,
11136                                      int trigger_element,
11137                                      int trigger_event,
11138                                      int trigger_player,
11139                                      int trigger_side)
11140 {
11141   boolean change_done = FALSE;
11142   int p;
11143
11144   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11145       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11146     return FALSE;
11147
11148   if (Tile[x][y] == EL_BLOCKED)
11149   {
11150     Blocked2Moving(x, y, &x, &y);
11151     element = Tile[x][y];
11152   }
11153
11154   // check if element has already changed or is about to change after moving
11155   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11156        Tile[x][y] != element) ||
11157
11158       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11159        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11160         ChangePage[x][y] != -1)))
11161     return FALSE;
11162
11163   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11164
11165   for (p = 0; p < element_info[element].num_change_pages; p++)
11166   {
11167     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11168
11169     /* check trigger element for all events where the element that is checked
11170        for changing interacts with a directly adjacent element -- this is
11171        different to element changes that affect other elements to change on the
11172        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11173     boolean check_trigger_element =
11174       (trigger_event == CE_NEXT_TO_X ||
11175        trigger_event == CE_TOUCHING_X ||
11176        trigger_event == CE_HITTING_X ||
11177        trigger_event == CE_HIT_BY_X ||
11178        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11179
11180     if (change->can_change_or_has_action &&
11181         change->has_event[trigger_event] &&
11182         change->trigger_side & trigger_side &&
11183         change->trigger_player & trigger_player &&
11184         (!check_trigger_element ||
11185          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11186     {
11187       change->actual_trigger_element = trigger_element;
11188       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11189       change->actual_trigger_player_bits = trigger_player;
11190       change->actual_trigger_side = trigger_side;
11191       change->actual_trigger_ce_value = CustomValue[x][y];
11192       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11193
11194       // special case: trigger element not at (x,y) position for some events
11195       if (check_trigger_element)
11196       {
11197         static struct
11198         {
11199           int dx, dy;
11200         } move_xy[] =
11201           {
11202             {  0,  0 },
11203             { -1,  0 },
11204             { +1,  0 },
11205             {  0,  0 },
11206             {  0, -1 },
11207             {  0,  0 }, { 0, 0 }, { 0, 0 },
11208             {  0, +1 }
11209           };
11210
11211         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11212         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11213
11214         change->actual_trigger_ce_value = CustomValue[xx][yy];
11215         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11216       }
11217
11218       if (change->can_change && !change_done)
11219       {
11220         ChangeDelay[x][y] = 1;
11221         ChangeEvent[x][y] = trigger_event;
11222
11223         HandleElementChange(x, y, p);
11224
11225         change_done = TRUE;
11226       }
11227       else if (change->has_action)
11228       {
11229         ExecuteCustomElementAction(x, y, element, p);
11230         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11231       }
11232     }
11233   }
11234
11235   RECURSION_LOOP_DETECTION_END();
11236
11237   return change_done;
11238 }
11239
11240 static void PlayPlayerSound(struct PlayerInfo *player)
11241 {
11242   int jx = player->jx, jy = player->jy;
11243   int sound_element = player->artwork_element;
11244   int last_action = player->last_action_waiting;
11245   int action = player->action_waiting;
11246
11247   if (player->is_waiting)
11248   {
11249     if (action != last_action)
11250       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11251     else
11252       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11253   }
11254   else
11255   {
11256     if (action != last_action)
11257       StopSound(element_info[sound_element].sound[last_action]);
11258
11259     if (last_action == ACTION_SLEEPING)
11260       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11261   }
11262 }
11263
11264 static void PlayAllPlayersSound(void)
11265 {
11266   int i;
11267
11268   for (i = 0; i < MAX_PLAYERS; i++)
11269     if (stored_player[i].active)
11270       PlayPlayerSound(&stored_player[i]);
11271 }
11272
11273 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11274 {
11275   boolean last_waiting = player->is_waiting;
11276   int move_dir = player->MovDir;
11277
11278   player->dir_waiting = move_dir;
11279   player->last_action_waiting = player->action_waiting;
11280
11281   if (is_waiting)
11282   {
11283     if (!last_waiting)          // not waiting -> waiting
11284     {
11285       player->is_waiting = TRUE;
11286
11287       player->frame_counter_bored =
11288         FrameCounter +
11289         game.player_boring_delay_fixed +
11290         GetSimpleRandom(game.player_boring_delay_random);
11291       player->frame_counter_sleeping =
11292         FrameCounter +
11293         game.player_sleeping_delay_fixed +
11294         GetSimpleRandom(game.player_sleeping_delay_random);
11295
11296       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11297     }
11298
11299     if (game.player_sleeping_delay_fixed +
11300         game.player_sleeping_delay_random > 0 &&
11301         player->anim_delay_counter == 0 &&
11302         player->post_delay_counter == 0 &&
11303         FrameCounter >= player->frame_counter_sleeping)
11304       player->is_sleeping = TRUE;
11305     else if (game.player_boring_delay_fixed +
11306              game.player_boring_delay_random > 0 &&
11307              FrameCounter >= player->frame_counter_bored)
11308       player->is_bored = TRUE;
11309
11310     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11311                               player->is_bored ? ACTION_BORING :
11312                               ACTION_WAITING);
11313
11314     if (player->is_sleeping && player->use_murphy)
11315     {
11316       // special case for sleeping Murphy when leaning against non-free tile
11317
11318       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11319           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11320            !IS_MOVING(player->jx - 1, player->jy)))
11321         move_dir = MV_LEFT;
11322       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11323                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11324                 !IS_MOVING(player->jx + 1, player->jy)))
11325         move_dir = MV_RIGHT;
11326       else
11327         player->is_sleeping = FALSE;
11328
11329       player->dir_waiting = move_dir;
11330     }
11331
11332     if (player->is_sleeping)
11333     {
11334       if (player->num_special_action_sleeping > 0)
11335       {
11336         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11337         {
11338           int last_special_action = player->special_action_sleeping;
11339           int num_special_action = player->num_special_action_sleeping;
11340           int special_action =
11341             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11342              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11343              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11344              last_special_action + 1 : ACTION_SLEEPING);
11345           int special_graphic =
11346             el_act_dir2img(player->artwork_element, special_action, move_dir);
11347
11348           player->anim_delay_counter =
11349             graphic_info[special_graphic].anim_delay_fixed +
11350             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11351           player->post_delay_counter =
11352             graphic_info[special_graphic].post_delay_fixed +
11353             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11354
11355           player->special_action_sleeping = special_action;
11356         }
11357
11358         if (player->anim_delay_counter > 0)
11359         {
11360           player->action_waiting = player->special_action_sleeping;
11361           player->anim_delay_counter--;
11362         }
11363         else if (player->post_delay_counter > 0)
11364         {
11365           player->post_delay_counter--;
11366         }
11367       }
11368     }
11369     else if (player->is_bored)
11370     {
11371       if (player->num_special_action_bored > 0)
11372       {
11373         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11374         {
11375           int special_action =
11376             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11377           int special_graphic =
11378             el_act_dir2img(player->artwork_element, special_action, move_dir);
11379
11380           player->anim_delay_counter =
11381             graphic_info[special_graphic].anim_delay_fixed +
11382             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11383           player->post_delay_counter =
11384             graphic_info[special_graphic].post_delay_fixed +
11385             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11386
11387           player->special_action_bored = special_action;
11388         }
11389
11390         if (player->anim_delay_counter > 0)
11391         {
11392           player->action_waiting = player->special_action_bored;
11393           player->anim_delay_counter--;
11394         }
11395         else if (player->post_delay_counter > 0)
11396         {
11397           player->post_delay_counter--;
11398         }
11399       }
11400     }
11401   }
11402   else if (last_waiting)        // waiting -> not waiting
11403   {
11404     player->is_waiting = FALSE;
11405     player->is_bored = FALSE;
11406     player->is_sleeping = FALSE;
11407
11408     player->frame_counter_bored = -1;
11409     player->frame_counter_sleeping = -1;
11410
11411     player->anim_delay_counter = 0;
11412     player->post_delay_counter = 0;
11413
11414     player->dir_waiting = player->MovDir;
11415     player->action_waiting = ACTION_DEFAULT;
11416
11417     player->special_action_bored = ACTION_DEFAULT;
11418     player->special_action_sleeping = ACTION_DEFAULT;
11419   }
11420 }
11421
11422 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11423 {
11424   if ((!player->is_moving  && player->was_moving) ||
11425       (player->MovPos == 0 && player->was_moving) ||
11426       (player->is_snapping && !player->was_snapping) ||
11427       (player->is_dropping && !player->was_dropping))
11428   {
11429     if (!CheckSaveEngineSnapshotToList())
11430       return;
11431
11432     player->was_moving = FALSE;
11433     player->was_snapping = TRUE;
11434     player->was_dropping = TRUE;
11435   }
11436   else
11437   {
11438     if (player->is_moving)
11439       player->was_moving = TRUE;
11440
11441     if (!player->is_snapping)
11442       player->was_snapping = FALSE;
11443
11444     if (!player->is_dropping)
11445       player->was_dropping = FALSE;
11446   }
11447
11448   static struct MouseActionInfo mouse_action_last = { 0 };
11449   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11450   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11451
11452   if (new_released)
11453     CheckSaveEngineSnapshotToList();
11454
11455   mouse_action_last = mouse_action;
11456 }
11457
11458 static void CheckSingleStepMode(struct PlayerInfo *player)
11459 {
11460   if (tape.single_step && tape.recording && !tape.pausing)
11461   {
11462     // as it is called "single step mode", just return to pause mode when the
11463     // player stopped moving after one tile (or never starts moving at all)
11464     // (reverse logic needed here in case single step mode used in team mode)
11465     if (player->is_moving ||
11466         player->is_pushing ||
11467         player->is_dropping_pressed ||
11468         player->effective_mouse_action.button)
11469       game.enter_single_step_mode = FALSE;
11470   }
11471
11472   CheckSaveEngineSnapshot(player);
11473 }
11474
11475 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11476 {
11477   int left      = player_action & JOY_LEFT;
11478   int right     = player_action & JOY_RIGHT;
11479   int up        = player_action & JOY_UP;
11480   int down      = player_action & JOY_DOWN;
11481   int button1   = player_action & JOY_BUTTON_1;
11482   int button2   = player_action & JOY_BUTTON_2;
11483   int dx        = (left ? -1 : right ? 1 : 0);
11484   int dy        = (up   ? -1 : down  ? 1 : 0);
11485
11486   if (!player->active || tape.pausing)
11487     return 0;
11488
11489   if (player_action)
11490   {
11491     if (button1)
11492       SnapField(player, dx, dy);
11493     else
11494     {
11495       if (button2)
11496         DropElement(player);
11497
11498       MovePlayer(player, dx, dy);
11499     }
11500
11501     CheckSingleStepMode(player);
11502
11503     SetPlayerWaiting(player, FALSE);
11504
11505     return player_action;
11506   }
11507   else
11508   {
11509     // no actions for this player (no input at player's configured device)
11510
11511     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11512     SnapField(player, 0, 0);
11513     CheckGravityMovementWhenNotMoving(player);
11514
11515     if (player->MovPos == 0)
11516       SetPlayerWaiting(player, TRUE);
11517
11518     if (player->MovPos == 0)    // needed for tape.playing
11519       player->is_moving = FALSE;
11520
11521     player->is_dropping = FALSE;
11522     player->is_dropping_pressed = FALSE;
11523     player->drop_pressed_delay = 0;
11524
11525     CheckSingleStepMode(player);
11526
11527     return 0;
11528   }
11529 }
11530
11531 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11532                                          byte *tape_action)
11533 {
11534   if (!tape.use_mouse_actions)
11535     return;
11536
11537   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11538   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11539   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11540 }
11541
11542 static void SetTapeActionFromMouseAction(byte *tape_action,
11543                                          struct MouseActionInfo *mouse_action)
11544 {
11545   if (!tape.use_mouse_actions)
11546     return;
11547
11548   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11549   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11550   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11551 }
11552
11553 static void CheckLevelSolved(void)
11554 {
11555   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11556   {
11557     if (game_em.level_solved &&
11558         !game_em.game_over)                             // game won
11559     {
11560       LevelSolved();
11561
11562       game_em.game_over = TRUE;
11563
11564       game.all_players_gone = TRUE;
11565     }
11566
11567     if (game_em.game_over)                              // game lost
11568       game.all_players_gone = TRUE;
11569   }
11570   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11571   {
11572     if (game_sp.level_solved &&
11573         !game_sp.game_over)                             // game won
11574     {
11575       LevelSolved();
11576
11577       game_sp.game_over = TRUE;
11578
11579       game.all_players_gone = TRUE;
11580     }
11581
11582     if (game_sp.game_over)                              // game lost
11583       game.all_players_gone = TRUE;
11584   }
11585   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11586   {
11587     if (game_mm.level_solved &&
11588         !game_mm.game_over)                             // game won
11589     {
11590       LevelSolved();
11591
11592       game_mm.game_over = TRUE;
11593
11594       game.all_players_gone = TRUE;
11595     }
11596
11597     if (game_mm.game_over)                              // game lost
11598       game.all_players_gone = TRUE;
11599   }
11600 }
11601
11602 static void CheckLevelTime(void)
11603 {
11604   int i;
11605
11606   if (TimeFrames >= FRAMES_PER_SECOND)
11607   {
11608     TimeFrames = 0;
11609     TapeTime++;
11610
11611     for (i = 0; i < MAX_PLAYERS; i++)
11612     {
11613       struct PlayerInfo *player = &stored_player[i];
11614
11615       if (SHIELD_ON(player))
11616       {
11617         player->shield_normal_time_left--;
11618
11619         if (player->shield_deadly_time_left > 0)
11620           player->shield_deadly_time_left--;
11621       }
11622     }
11623
11624     if (!game.LevelSolved && !level.use_step_counter)
11625     {
11626       TimePlayed++;
11627
11628       if (TimeLeft > 0)
11629       {
11630         TimeLeft--;
11631
11632         if (TimeLeft <= 10 && setup.time_limit)
11633           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11634
11635         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11636            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11637
11638         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11639
11640         if (!TimeLeft && setup.time_limit)
11641         {
11642           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11643             game_em.lev->killed_out_of_time = TRUE;
11644           else
11645             for (i = 0; i < MAX_PLAYERS; i++)
11646               KillPlayer(&stored_player[i]);
11647         }
11648       }
11649       else if (game.no_time_limit && !game.all_players_gone)
11650       {
11651         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11652       }
11653
11654       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11655     }
11656
11657     if (tape.recording || tape.playing)
11658       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11659   }
11660
11661   if (tape.recording || tape.playing)
11662     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11663
11664   UpdateAndDisplayGameControlValues();
11665 }
11666
11667 void AdvanceFrameAndPlayerCounters(int player_nr)
11668 {
11669   int i;
11670
11671   // advance frame counters (global frame counter and time frame counter)
11672   FrameCounter++;
11673   TimeFrames++;
11674
11675   // advance player counters (counters for move delay, move animation etc.)
11676   for (i = 0; i < MAX_PLAYERS; i++)
11677   {
11678     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11679     int move_delay_value = stored_player[i].move_delay_value;
11680     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11681
11682     if (!advance_player_counters)       // not all players may be affected
11683       continue;
11684
11685     if (move_frames == 0)       // less than one move per game frame
11686     {
11687       int stepsize = TILEX / move_delay_value;
11688       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11689       int count = (stored_player[i].is_moving ?
11690                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11691
11692       if (count % delay == 0)
11693         move_frames = 1;
11694     }
11695
11696     stored_player[i].Frame += move_frames;
11697
11698     if (stored_player[i].MovPos != 0)
11699       stored_player[i].StepFrame += move_frames;
11700
11701     if (stored_player[i].move_delay > 0)
11702       stored_player[i].move_delay--;
11703
11704     // due to bugs in previous versions, counter must count up, not down
11705     if (stored_player[i].push_delay != -1)
11706       stored_player[i].push_delay++;
11707
11708     if (stored_player[i].drop_delay > 0)
11709       stored_player[i].drop_delay--;
11710
11711     if (stored_player[i].is_dropping_pressed)
11712       stored_player[i].drop_pressed_delay++;
11713   }
11714 }
11715
11716 void StartGameActions(boolean init_network_game, boolean record_tape,
11717                       int random_seed)
11718 {
11719   unsigned int new_random_seed = InitRND(random_seed);
11720
11721   if (record_tape)
11722     TapeStartRecording(new_random_seed);
11723
11724   if (init_network_game)
11725   {
11726     SendToServer_LevelFile();
11727     SendToServer_StartPlaying();
11728
11729     return;
11730   }
11731
11732   InitGame();
11733 }
11734
11735 static void GameActionsExt(void)
11736 {
11737 #if 0
11738   static unsigned int game_frame_delay = 0;
11739 #endif
11740   unsigned int game_frame_delay_value;
11741   byte *recorded_player_action;
11742   byte summarized_player_action = 0;
11743   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11744   int i;
11745
11746   // detect endless loops, caused by custom element programming
11747   if (recursion_loop_detected && recursion_loop_depth == 0)
11748   {
11749     char *message = getStringCat3("Internal Error! Element ",
11750                                   EL_NAME(recursion_loop_element),
11751                                   " caused endless loop! Quit the game?");
11752
11753     Warn("element '%s' caused endless loop in game engine",
11754          EL_NAME(recursion_loop_element));
11755
11756     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11757
11758     recursion_loop_detected = FALSE;    // if game should be continued
11759
11760     free(message);
11761
11762     return;
11763   }
11764
11765   if (game.restart_level)
11766     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11767
11768   CheckLevelSolved();
11769
11770   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11771     GameWon();
11772
11773   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11774     TapeStop();
11775
11776   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11777     return;
11778
11779   game_frame_delay_value =
11780     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11781
11782   if (tape.playing && tape.warp_forward && !tape.pausing)
11783     game_frame_delay_value = 0;
11784
11785   SetVideoFrameDelay(game_frame_delay_value);
11786
11787   // (de)activate virtual buttons depending on current game status
11788   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11789   {
11790     if (game.all_players_gone)  // if no players there to be controlled anymore
11791       SetOverlayActive(FALSE);
11792     else if (!tape.playing)     // if game continues after tape stopped playing
11793       SetOverlayActive(TRUE);
11794   }
11795
11796 #if 0
11797 #if 0
11798   // ---------- main game synchronization point ----------
11799
11800   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11801
11802   Debug("game:playing:skip", "skip == %d", skip);
11803
11804 #else
11805   // ---------- main game synchronization point ----------
11806
11807   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11808 #endif
11809 #endif
11810
11811   if (network_playing && !network_player_action_received)
11812   {
11813     // try to get network player actions in time
11814
11815     // last chance to get network player actions without main loop delay
11816     HandleNetworking();
11817
11818     // game was quit by network peer
11819     if (game_status != GAME_MODE_PLAYING)
11820       return;
11821
11822     // check if network player actions still missing and game still running
11823     if (!network_player_action_received && !checkGameEnded())
11824       return;           // failed to get network player actions in time
11825
11826     // do not yet reset "network_player_action_received" (for tape.pausing)
11827   }
11828
11829   if (tape.pausing)
11830     return;
11831
11832   // at this point we know that we really continue executing the game
11833
11834   network_player_action_received = FALSE;
11835
11836   // when playing tape, read previously recorded player input from tape data
11837   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11838
11839   local_player->effective_mouse_action = local_player->mouse_action;
11840
11841   if (recorded_player_action != NULL)
11842     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11843                                  recorded_player_action);
11844
11845   // TapePlayAction() may return NULL when toggling to "pause before death"
11846   if (tape.pausing)
11847     return;
11848
11849   if (tape.set_centered_player)
11850   {
11851     game.centered_player_nr_next = tape.centered_player_nr_next;
11852     game.set_centered_player = TRUE;
11853   }
11854
11855   for (i = 0; i < MAX_PLAYERS; i++)
11856   {
11857     summarized_player_action |= stored_player[i].action;
11858
11859     if (!network_playing && (game.team_mode || tape.playing))
11860       stored_player[i].effective_action = stored_player[i].action;
11861   }
11862
11863   if (network_playing && !checkGameEnded())
11864     SendToServer_MovePlayer(summarized_player_action);
11865
11866   // summarize all actions at local players mapped input device position
11867   // (this allows using different input devices in single player mode)
11868   if (!network.enabled && !game.team_mode)
11869     stored_player[map_player_action[local_player->index_nr]].effective_action =
11870       summarized_player_action;
11871
11872   // summarize all actions at centered player in local team mode
11873   if (tape.recording &&
11874       setup.team_mode && !network.enabled &&
11875       setup.input_on_focus &&
11876       game.centered_player_nr != -1)
11877   {
11878     for (i = 0; i < MAX_PLAYERS; i++)
11879       stored_player[map_player_action[i]].effective_action =
11880         (i == game.centered_player_nr ? summarized_player_action : 0);
11881   }
11882
11883   if (recorded_player_action != NULL)
11884     for (i = 0; i < MAX_PLAYERS; i++)
11885       stored_player[i].effective_action = recorded_player_action[i];
11886
11887   for (i = 0; i < MAX_PLAYERS; i++)
11888   {
11889     tape_action[i] = stored_player[i].effective_action;
11890
11891     /* (this may happen in the RND game engine if a player was not present on
11892        the playfield on level start, but appeared later from a custom element */
11893     if (setup.team_mode &&
11894         tape.recording &&
11895         tape_action[i] &&
11896         !tape.player_participates[i])
11897       tape.player_participates[i] = TRUE;
11898   }
11899
11900   SetTapeActionFromMouseAction(tape_action,
11901                                &local_player->effective_mouse_action);
11902
11903   // only record actions from input devices, but not programmed actions
11904   if (tape.recording)
11905     TapeRecordAction(tape_action);
11906
11907   // remember if game was played (especially after tape stopped playing)
11908   if (!tape.playing && summarized_player_action)
11909     game.GamePlayed = TRUE;
11910
11911 #if USE_NEW_PLAYER_ASSIGNMENTS
11912   // !!! also map player actions in single player mode !!!
11913   // if (game.team_mode)
11914   if (1)
11915   {
11916     byte mapped_action[MAX_PLAYERS];
11917
11918 #if DEBUG_PLAYER_ACTIONS
11919     for (i = 0; i < MAX_PLAYERS; i++)
11920       DebugContinued("", "%d, ", stored_player[i].effective_action);
11921 #endif
11922
11923     for (i = 0; i < MAX_PLAYERS; i++)
11924       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11925
11926     for (i = 0; i < MAX_PLAYERS; i++)
11927       stored_player[i].effective_action = mapped_action[i];
11928
11929 #if DEBUG_PLAYER_ACTIONS
11930     DebugContinued("", "=> ");
11931     for (i = 0; i < MAX_PLAYERS; i++)
11932       DebugContinued("", "%d, ", stored_player[i].effective_action);
11933     DebugContinued("game:playing:player", "\n");
11934 #endif
11935   }
11936 #if DEBUG_PLAYER_ACTIONS
11937   else
11938   {
11939     for (i = 0; i < MAX_PLAYERS; i++)
11940       DebugContinued("", "%d, ", stored_player[i].effective_action);
11941     DebugContinued("game:playing:player", "\n");
11942   }
11943 #endif
11944 #endif
11945
11946   for (i = 0; i < MAX_PLAYERS; i++)
11947   {
11948     // allow engine snapshot in case of changed movement attempt
11949     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11950         (stored_player[i].effective_action & KEY_MOTION))
11951       game.snapshot.changed_action = TRUE;
11952
11953     // allow engine snapshot in case of snapping/dropping attempt
11954     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11955         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11956       game.snapshot.changed_action = TRUE;
11957
11958     game.snapshot.last_action[i] = stored_player[i].effective_action;
11959   }
11960
11961   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11962   {
11963     GameActions_EM_Main();
11964   }
11965   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11966   {
11967     GameActions_SP_Main();
11968   }
11969   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11970   {
11971     GameActions_MM_Main();
11972   }
11973   else
11974   {
11975     GameActions_RND_Main();
11976   }
11977
11978   BlitScreenToBitmap(backbuffer);
11979
11980   CheckLevelSolved();
11981   CheckLevelTime();
11982
11983   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11984
11985   if (global.show_frames_per_second)
11986   {
11987     static unsigned int fps_counter = 0;
11988     static int fps_frames = 0;
11989     unsigned int fps_delay_ms = Counter() - fps_counter;
11990
11991     fps_frames++;
11992
11993     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11994     {
11995       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11996
11997       fps_frames = 0;
11998       fps_counter = Counter();
11999
12000       // always draw FPS to screen after FPS value was updated
12001       redraw_mask |= REDRAW_FPS;
12002     }
12003
12004     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12005     if (GetDrawDeactivationMask() == REDRAW_NONE)
12006       redraw_mask |= REDRAW_FPS;
12007   }
12008 }
12009
12010 static void GameActions_CheckSaveEngineSnapshot(void)
12011 {
12012   if (!game.snapshot.save_snapshot)
12013     return;
12014
12015   // clear flag for saving snapshot _before_ saving snapshot
12016   game.snapshot.save_snapshot = FALSE;
12017
12018   SaveEngineSnapshotToList();
12019 }
12020
12021 void GameActions(void)
12022 {
12023   GameActionsExt();
12024
12025   GameActions_CheckSaveEngineSnapshot();
12026 }
12027
12028 void GameActions_EM_Main(void)
12029 {
12030   byte effective_action[MAX_PLAYERS];
12031   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12032   int i;
12033
12034   for (i = 0; i < MAX_PLAYERS; i++)
12035     effective_action[i] = stored_player[i].effective_action;
12036
12037   GameActions_EM(effective_action, warp_mode);
12038 }
12039
12040 void GameActions_SP_Main(void)
12041 {
12042   byte effective_action[MAX_PLAYERS];
12043   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12044   int i;
12045
12046   for (i = 0; i < MAX_PLAYERS; i++)
12047     effective_action[i] = stored_player[i].effective_action;
12048
12049   GameActions_SP(effective_action, warp_mode);
12050
12051   for (i = 0; i < MAX_PLAYERS; i++)
12052   {
12053     if (stored_player[i].force_dropping)
12054       stored_player[i].action |= KEY_BUTTON_DROP;
12055
12056     stored_player[i].force_dropping = FALSE;
12057   }
12058 }
12059
12060 void GameActions_MM_Main(void)
12061 {
12062   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12063
12064   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12065 }
12066
12067 void GameActions_RND_Main(void)
12068 {
12069   GameActions_RND();
12070 }
12071
12072 void GameActions_RND(void)
12073 {
12074   static struct MouseActionInfo mouse_action_last = { 0 };
12075   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12076   int magic_wall_x = 0, magic_wall_y = 0;
12077   int i, x, y, element, graphic, last_gfx_frame;
12078
12079   InitPlayfieldScanModeVars();
12080
12081   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12082   {
12083     SCAN_PLAYFIELD(x, y)
12084     {
12085       ChangeCount[x][y] = 0;
12086       ChangeEvent[x][y] = -1;
12087     }
12088   }
12089
12090   if (game.set_centered_player)
12091   {
12092     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12093
12094     // switching to "all players" only possible if all players fit to screen
12095     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12096     {
12097       game.centered_player_nr_next = game.centered_player_nr;
12098       game.set_centered_player = FALSE;
12099     }
12100
12101     // do not switch focus to non-existing (or non-active) player
12102     if (game.centered_player_nr_next >= 0 &&
12103         !stored_player[game.centered_player_nr_next].active)
12104     {
12105       game.centered_player_nr_next = game.centered_player_nr;
12106       game.set_centered_player = FALSE;
12107     }
12108   }
12109
12110   if (game.set_centered_player &&
12111       ScreenMovPos == 0)        // screen currently aligned at tile position
12112   {
12113     int sx, sy;
12114
12115     if (game.centered_player_nr_next == -1)
12116     {
12117       setScreenCenteredToAllPlayers(&sx, &sy);
12118     }
12119     else
12120     {
12121       sx = stored_player[game.centered_player_nr_next].jx;
12122       sy = stored_player[game.centered_player_nr_next].jy;
12123     }
12124
12125     game.centered_player_nr = game.centered_player_nr_next;
12126     game.set_centered_player = FALSE;
12127
12128     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12129     DrawGameDoorValues();
12130   }
12131
12132   // check single step mode (set flag and clear again if any player is active)
12133   game.enter_single_step_mode =
12134     (tape.single_step && tape.recording && !tape.pausing);
12135
12136   for (i = 0; i < MAX_PLAYERS; i++)
12137   {
12138     int actual_player_action = stored_player[i].effective_action;
12139
12140 #if 1
12141     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12142        - rnd_equinox_tetrachloride 048
12143        - rnd_equinox_tetrachloride_ii 096
12144        - rnd_emanuel_schmieg 002
12145        - doctor_sloan_ww 001, 020
12146     */
12147     if (stored_player[i].MovPos == 0)
12148       CheckGravityMovement(&stored_player[i]);
12149 #endif
12150
12151     // overwrite programmed action with tape action
12152     if (stored_player[i].programmed_action)
12153       actual_player_action = stored_player[i].programmed_action;
12154
12155     PlayerActions(&stored_player[i], actual_player_action);
12156
12157     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12158   }
12159
12160   // single step pause mode may already have been toggled by "ScrollPlayer()"
12161   if (game.enter_single_step_mode && !tape.pausing)
12162     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12163
12164   ScrollScreen(NULL, SCROLL_GO_ON);
12165
12166   /* for backwards compatibility, the following code emulates a fixed bug that
12167      occured when pushing elements (causing elements that just made their last
12168      pushing step to already (if possible) make their first falling step in the
12169      same game frame, which is bad); this code is also needed to use the famous
12170      "spring push bug" which is used in older levels and might be wanted to be
12171      used also in newer levels, but in this case the buggy pushing code is only
12172      affecting the "spring" element and no other elements */
12173
12174   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12175   {
12176     for (i = 0; i < MAX_PLAYERS; i++)
12177     {
12178       struct PlayerInfo *player = &stored_player[i];
12179       int x = player->jx;
12180       int y = player->jy;
12181
12182       if (player->active && player->is_pushing && player->is_moving &&
12183           IS_MOVING(x, y) &&
12184           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12185            Tile[x][y] == EL_SPRING))
12186       {
12187         ContinueMoving(x, y);
12188
12189         // continue moving after pushing (this is actually a bug)
12190         if (!IS_MOVING(x, y))
12191           Stop[x][y] = FALSE;
12192       }
12193     }
12194   }
12195
12196   SCAN_PLAYFIELD(x, y)
12197   {
12198     Last[x][y] = Tile[x][y];
12199
12200     ChangeCount[x][y] = 0;
12201     ChangeEvent[x][y] = -1;
12202
12203     // this must be handled before main playfield loop
12204     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12205     {
12206       MovDelay[x][y]--;
12207       if (MovDelay[x][y] <= 0)
12208         RemoveField(x, y);
12209     }
12210
12211     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12212     {
12213       MovDelay[x][y]--;
12214       if (MovDelay[x][y] <= 0)
12215       {
12216         int element = Store[x][y];
12217         int move_direction = MovDir[x][y];
12218         int player_index_bit = Store2[x][y];
12219
12220         Store[x][y] = 0;
12221         Store2[x][y] = 0;
12222
12223         RemoveField(x, y);
12224         TEST_DrawLevelField(x, y);
12225
12226         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12227
12228         if (IS_ENVELOPE(element))
12229           local_player->show_envelope = element;
12230       }
12231     }
12232
12233 #if DEBUG
12234     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12235     {
12236       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12237             x, y);
12238       Debug("game:playing:GameActions_RND", "This should never happen!");
12239
12240       ChangePage[x][y] = -1;
12241     }
12242 #endif
12243
12244     Stop[x][y] = FALSE;
12245     if (WasJustMoving[x][y] > 0)
12246       WasJustMoving[x][y]--;
12247     if (WasJustFalling[x][y] > 0)
12248       WasJustFalling[x][y]--;
12249     if (CheckCollision[x][y] > 0)
12250       CheckCollision[x][y]--;
12251     if (CheckImpact[x][y] > 0)
12252       CheckImpact[x][y]--;
12253
12254     GfxFrame[x][y]++;
12255
12256     /* reset finished pushing action (not done in ContinueMoving() to allow
12257        continuous pushing animation for elements with zero push delay) */
12258     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12259     {
12260       ResetGfxAnimation(x, y);
12261       TEST_DrawLevelField(x, y);
12262     }
12263
12264 #if DEBUG
12265     if (IS_BLOCKED(x, y))
12266     {
12267       int oldx, oldy;
12268
12269       Blocked2Moving(x, y, &oldx, &oldy);
12270       if (!IS_MOVING(oldx, oldy))
12271       {
12272         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12273         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12274         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12275         Debug("game:playing:GameActions_RND", "This should never happen!");
12276       }
12277     }
12278 #endif
12279   }
12280
12281   if (mouse_action.button)
12282   {
12283     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12284     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12285
12286     x = mouse_action.lx;
12287     y = mouse_action.ly;
12288     element = Tile[x][y];
12289
12290     if (new_button)
12291     {
12292       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12293       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12294                                          ch_button);
12295     }
12296
12297     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12298     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12299                                        ch_button);
12300   }
12301
12302   SCAN_PLAYFIELD(x, y)
12303   {
12304     element = Tile[x][y];
12305     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12306     last_gfx_frame = GfxFrame[x][y];
12307
12308     if (element == EL_EMPTY)
12309       graphic = el2img(GfxElementEmpty[x][y]);
12310
12311     ResetGfxFrame(x, y);
12312
12313     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12314       DrawLevelGraphicAnimation(x, y, graphic);
12315
12316     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12317         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12318       ResetRandomAnimationValue(x, y);
12319
12320     SetRandomAnimationValue(x, y);
12321
12322     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12323
12324     if (IS_INACTIVE(element))
12325     {
12326       if (IS_ANIMATED(graphic))
12327         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12328
12329       continue;
12330     }
12331
12332     // this may take place after moving, so 'element' may have changed
12333     if (IS_CHANGING(x, y) &&
12334         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12335     {
12336       int page = element_info[element].event_page_nr[CE_DELAY];
12337
12338       HandleElementChange(x, y, page);
12339
12340       element = Tile[x][y];
12341       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12342     }
12343
12344     CheckNextToConditions(x, y);
12345
12346     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12347     {
12348       StartMoving(x, y);
12349
12350       element = Tile[x][y];
12351       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12352
12353       if (IS_ANIMATED(graphic) &&
12354           !IS_MOVING(x, y) &&
12355           !Stop[x][y])
12356         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12357
12358       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12359         TEST_DrawTwinkleOnField(x, y);
12360     }
12361     else if (element == EL_ACID)
12362     {
12363       if (!Stop[x][y])
12364         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12365     }
12366     else if ((element == EL_EXIT_OPEN ||
12367               element == EL_EM_EXIT_OPEN ||
12368               element == EL_SP_EXIT_OPEN ||
12369               element == EL_STEEL_EXIT_OPEN ||
12370               element == EL_EM_STEEL_EXIT_OPEN ||
12371               element == EL_SP_TERMINAL ||
12372               element == EL_SP_TERMINAL_ACTIVE ||
12373               element == EL_EXTRA_TIME ||
12374               element == EL_SHIELD_NORMAL ||
12375               element == EL_SHIELD_DEADLY) &&
12376              IS_ANIMATED(graphic))
12377       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12378     else if (IS_MOVING(x, y))
12379       ContinueMoving(x, y);
12380     else if (IS_ACTIVE_BOMB(element))
12381       CheckDynamite(x, y);
12382     else if (element == EL_AMOEBA_GROWING)
12383       AmoebaGrowing(x, y);
12384     else if (element == EL_AMOEBA_SHRINKING)
12385       AmoebaShrinking(x, y);
12386
12387 #if !USE_NEW_AMOEBA_CODE
12388     else if (IS_AMOEBALIVE(element))
12389       AmoebaReproduce(x, y);
12390 #endif
12391
12392     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12393       Life(x, y);
12394     else if (element == EL_EXIT_CLOSED)
12395       CheckExit(x, y);
12396     else if (element == EL_EM_EXIT_CLOSED)
12397       CheckExitEM(x, y);
12398     else if (element == EL_STEEL_EXIT_CLOSED)
12399       CheckExitSteel(x, y);
12400     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12401       CheckExitSteelEM(x, y);
12402     else if (element == EL_SP_EXIT_CLOSED)
12403       CheckExitSP(x, y);
12404     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12405              element == EL_EXPANDABLE_STEELWALL_GROWING)
12406       MauerWaechst(x, y);
12407     else if (element == EL_EXPANDABLE_WALL ||
12408              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12409              element == EL_EXPANDABLE_WALL_VERTICAL ||
12410              element == EL_EXPANDABLE_WALL_ANY ||
12411              element == EL_BD_EXPANDABLE_WALL)
12412       MauerAbleger(x, y);
12413     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12414              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12415              element == EL_EXPANDABLE_STEELWALL_ANY)
12416       MauerAblegerStahl(x, y);
12417     else if (element == EL_FLAMES)
12418       CheckForDragon(x, y);
12419     else if (element == EL_EXPLOSION)
12420       ; // drawing of correct explosion animation is handled separately
12421     else if (element == EL_ELEMENT_SNAPPING ||
12422              element == EL_DIAGONAL_SHRINKING ||
12423              element == EL_DIAGONAL_GROWING)
12424     {
12425       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12426
12427       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12428     }
12429     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12430       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12431
12432     if (IS_BELT_ACTIVE(element))
12433       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12434
12435     if (game.magic_wall_active)
12436     {
12437       int jx = local_player->jx, jy = local_player->jy;
12438
12439       // play the element sound at the position nearest to the player
12440       if ((element == EL_MAGIC_WALL_FULL ||
12441            element == EL_MAGIC_WALL_ACTIVE ||
12442            element == EL_MAGIC_WALL_EMPTYING ||
12443            element == EL_BD_MAGIC_WALL_FULL ||
12444            element == EL_BD_MAGIC_WALL_ACTIVE ||
12445            element == EL_BD_MAGIC_WALL_EMPTYING ||
12446            element == EL_DC_MAGIC_WALL_FULL ||
12447            element == EL_DC_MAGIC_WALL_ACTIVE ||
12448            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12449           ABS(x - jx) + ABS(y - jy) <
12450           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12451       {
12452         magic_wall_x = x;
12453         magic_wall_y = y;
12454       }
12455     }
12456   }
12457
12458 #if USE_NEW_AMOEBA_CODE
12459   // new experimental amoeba growth stuff
12460   if (!(FrameCounter % 8))
12461   {
12462     static unsigned int random = 1684108901;
12463
12464     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12465     {
12466       x = RND(lev_fieldx);
12467       y = RND(lev_fieldy);
12468       element = Tile[x][y];
12469
12470       if (!IS_PLAYER(x,y) &&
12471           (element == EL_EMPTY ||
12472            CAN_GROW_INTO(element) ||
12473            element == EL_QUICKSAND_EMPTY ||
12474            element == EL_QUICKSAND_FAST_EMPTY ||
12475            element == EL_ACID_SPLASH_LEFT ||
12476            element == EL_ACID_SPLASH_RIGHT))
12477       {
12478         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12479             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12480             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12481             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12482           Tile[x][y] = EL_AMOEBA_DROP;
12483       }
12484
12485       random = random * 129 + 1;
12486     }
12487   }
12488 #endif
12489
12490   game.explosions_delayed = FALSE;
12491
12492   SCAN_PLAYFIELD(x, y)
12493   {
12494     element = Tile[x][y];
12495
12496     if (ExplodeField[x][y])
12497       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12498     else if (element == EL_EXPLOSION)
12499       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12500
12501     ExplodeField[x][y] = EX_TYPE_NONE;
12502   }
12503
12504   game.explosions_delayed = TRUE;
12505
12506   if (game.magic_wall_active)
12507   {
12508     if (!(game.magic_wall_time_left % 4))
12509     {
12510       int element = Tile[magic_wall_x][magic_wall_y];
12511
12512       if (element == EL_BD_MAGIC_WALL_FULL ||
12513           element == EL_BD_MAGIC_WALL_ACTIVE ||
12514           element == EL_BD_MAGIC_WALL_EMPTYING)
12515         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12516       else if (element == EL_DC_MAGIC_WALL_FULL ||
12517                element == EL_DC_MAGIC_WALL_ACTIVE ||
12518                element == EL_DC_MAGIC_WALL_EMPTYING)
12519         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12520       else
12521         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12522     }
12523
12524     if (game.magic_wall_time_left > 0)
12525     {
12526       game.magic_wall_time_left--;
12527
12528       if (!game.magic_wall_time_left)
12529       {
12530         SCAN_PLAYFIELD(x, y)
12531         {
12532           element = Tile[x][y];
12533
12534           if (element == EL_MAGIC_WALL_ACTIVE ||
12535               element == EL_MAGIC_WALL_FULL)
12536           {
12537             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12538             TEST_DrawLevelField(x, y);
12539           }
12540           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12541                    element == EL_BD_MAGIC_WALL_FULL)
12542           {
12543             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12544             TEST_DrawLevelField(x, y);
12545           }
12546           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12547                    element == EL_DC_MAGIC_WALL_FULL)
12548           {
12549             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12550             TEST_DrawLevelField(x, y);
12551           }
12552         }
12553
12554         game.magic_wall_active = FALSE;
12555       }
12556     }
12557   }
12558
12559   if (game.light_time_left > 0)
12560   {
12561     game.light_time_left--;
12562
12563     if (game.light_time_left == 0)
12564       RedrawAllLightSwitchesAndInvisibleElements();
12565   }
12566
12567   if (game.timegate_time_left > 0)
12568   {
12569     game.timegate_time_left--;
12570
12571     if (game.timegate_time_left == 0)
12572       CloseAllOpenTimegates();
12573   }
12574
12575   if (game.lenses_time_left > 0)
12576   {
12577     game.lenses_time_left--;
12578
12579     if (game.lenses_time_left == 0)
12580       RedrawAllInvisibleElementsForLenses();
12581   }
12582
12583   if (game.magnify_time_left > 0)
12584   {
12585     game.magnify_time_left--;
12586
12587     if (game.magnify_time_left == 0)
12588       RedrawAllInvisibleElementsForMagnifier();
12589   }
12590
12591   for (i = 0; i < MAX_PLAYERS; i++)
12592   {
12593     struct PlayerInfo *player = &stored_player[i];
12594
12595     if (SHIELD_ON(player))
12596     {
12597       if (player->shield_deadly_time_left)
12598         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12599       else if (player->shield_normal_time_left)
12600         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12601     }
12602   }
12603
12604 #if USE_DELAYED_GFX_REDRAW
12605   SCAN_PLAYFIELD(x, y)
12606   {
12607     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12608     {
12609       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12610          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12611
12612       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12613         DrawLevelField(x, y);
12614
12615       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12616         DrawLevelFieldCrumbled(x, y);
12617
12618       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12619         DrawLevelFieldCrumbledNeighbours(x, y);
12620
12621       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12622         DrawTwinkleOnField(x, y);
12623     }
12624
12625     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12626   }
12627 #endif
12628
12629   DrawAllPlayers();
12630   PlayAllPlayersSound();
12631
12632   for (i = 0; i < MAX_PLAYERS; i++)
12633   {
12634     struct PlayerInfo *player = &stored_player[i];
12635
12636     if (player->show_envelope != 0 && (!player->active ||
12637                                        player->MovPos == 0))
12638     {
12639       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12640
12641       player->show_envelope = 0;
12642     }
12643   }
12644
12645   // use random number generator in every frame to make it less predictable
12646   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12647     RND(1);
12648
12649   mouse_action_last = mouse_action;
12650 }
12651
12652 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12653 {
12654   int min_x = x, min_y = y, max_x = x, max_y = y;
12655   int scr_fieldx = getScreenFieldSizeX();
12656   int scr_fieldy = getScreenFieldSizeY();
12657   int i;
12658
12659   for (i = 0; i < MAX_PLAYERS; i++)
12660   {
12661     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12662
12663     if (!stored_player[i].active || &stored_player[i] == player)
12664       continue;
12665
12666     min_x = MIN(min_x, jx);
12667     min_y = MIN(min_y, jy);
12668     max_x = MAX(max_x, jx);
12669     max_y = MAX(max_y, jy);
12670   }
12671
12672   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12673 }
12674
12675 static boolean AllPlayersInVisibleScreen(void)
12676 {
12677   int i;
12678
12679   for (i = 0; i < MAX_PLAYERS; i++)
12680   {
12681     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12682
12683     if (!stored_player[i].active)
12684       continue;
12685
12686     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12687       return FALSE;
12688   }
12689
12690   return TRUE;
12691 }
12692
12693 void ScrollLevel(int dx, int dy)
12694 {
12695   int scroll_offset = 2 * TILEX_VAR;
12696   int x, y;
12697
12698   BlitBitmap(drawto_field, drawto_field,
12699              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12700              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12701              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12702              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12703              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12704              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12705
12706   if (dx != 0)
12707   {
12708     x = (dx == 1 ? BX1 : BX2);
12709     for (y = BY1; y <= BY2; y++)
12710       DrawScreenField(x, y);
12711   }
12712
12713   if (dy != 0)
12714   {
12715     y = (dy == 1 ? BY1 : BY2);
12716     for (x = BX1; x <= BX2; x++)
12717       DrawScreenField(x, y);
12718   }
12719
12720   redraw_mask |= REDRAW_FIELD;
12721 }
12722
12723 static boolean canFallDown(struct PlayerInfo *player)
12724 {
12725   int jx = player->jx, jy = player->jy;
12726
12727   return (IN_LEV_FIELD(jx, jy + 1) &&
12728           (IS_FREE(jx, jy + 1) ||
12729            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12730           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12731           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12732 }
12733
12734 static boolean canPassField(int x, int y, int move_dir)
12735 {
12736   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12737   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12738   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12739   int nextx = x + dx;
12740   int nexty = y + dy;
12741   int element = Tile[x][y];
12742
12743   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12744           !CAN_MOVE(element) &&
12745           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12746           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12747           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12748 }
12749
12750 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12751 {
12752   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12753   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12754   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12755   int newx = x + dx;
12756   int newy = y + dy;
12757
12758   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12759           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12760           (IS_DIGGABLE(Tile[newx][newy]) ||
12761            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12762            canPassField(newx, newy, move_dir)));
12763 }
12764
12765 static void CheckGravityMovement(struct PlayerInfo *player)
12766 {
12767   if (player->gravity && !player->programmed_action)
12768   {
12769     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12770     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12771     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12772     int jx = player->jx, jy = player->jy;
12773     boolean player_is_moving_to_valid_field =
12774       (!player_is_snapping &&
12775        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12776         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12777     boolean player_can_fall_down = canFallDown(player);
12778
12779     if (player_can_fall_down &&
12780         !player_is_moving_to_valid_field)
12781       player->programmed_action = MV_DOWN;
12782   }
12783 }
12784
12785 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12786 {
12787   return CheckGravityMovement(player);
12788
12789   if (player->gravity && !player->programmed_action)
12790   {
12791     int jx = player->jx, jy = player->jy;
12792     boolean field_under_player_is_free =
12793       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12794     boolean player_is_standing_on_valid_field =
12795       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12796        (IS_WALKABLE(Tile[jx][jy]) &&
12797         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12798
12799     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12800       player->programmed_action = MV_DOWN;
12801   }
12802 }
12803
12804 /*
12805   MovePlayerOneStep()
12806   -----------------------------------------------------------------------------
12807   dx, dy:               direction (non-diagonal) to try to move the player to
12808   real_dx, real_dy:     direction as read from input device (can be diagonal)
12809 */
12810
12811 boolean MovePlayerOneStep(struct PlayerInfo *player,
12812                           int dx, int dy, int real_dx, int real_dy)
12813 {
12814   int jx = player->jx, jy = player->jy;
12815   int new_jx = jx + dx, new_jy = jy + dy;
12816   int can_move;
12817   boolean player_can_move = !player->cannot_move;
12818
12819   if (!player->active || (!dx && !dy))
12820     return MP_NO_ACTION;
12821
12822   player->MovDir = (dx < 0 ? MV_LEFT :
12823                     dx > 0 ? MV_RIGHT :
12824                     dy < 0 ? MV_UP :
12825                     dy > 0 ? MV_DOWN :  MV_NONE);
12826
12827   if (!IN_LEV_FIELD(new_jx, new_jy))
12828     return MP_NO_ACTION;
12829
12830   if (!player_can_move)
12831   {
12832     if (player->MovPos == 0)
12833     {
12834       player->is_moving = FALSE;
12835       player->is_digging = FALSE;
12836       player->is_collecting = FALSE;
12837       player->is_snapping = FALSE;
12838       player->is_pushing = FALSE;
12839     }
12840   }
12841
12842   if (!network.enabled && game.centered_player_nr == -1 &&
12843       !AllPlayersInSight(player, new_jx, new_jy))
12844     return MP_NO_ACTION;
12845
12846   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12847   if (can_move != MP_MOVING)
12848     return can_move;
12849
12850   // check if DigField() has caused relocation of the player
12851   if (player->jx != jx || player->jy != jy)
12852     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12853
12854   StorePlayer[jx][jy] = 0;
12855   player->last_jx = jx;
12856   player->last_jy = jy;
12857   player->jx = new_jx;
12858   player->jy = new_jy;
12859   StorePlayer[new_jx][new_jy] = player->element_nr;
12860
12861   if (player->move_delay_value_next != -1)
12862   {
12863     player->move_delay_value = player->move_delay_value_next;
12864     player->move_delay_value_next = -1;
12865   }
12866
12867   player->MovPos =
12868     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12869
12870   player->step_counter++;
12871
12872   PlayerVisit[jx][jy] = FrameCounter;
12873
12874   player->is_moving = TRUE;
12875
12876 #if 1
12877   // should better be called in MovePlayer(), but this breaks some tapes
12878   ScrollPlayer(player, SCROLL_INIT);
12879 #endif
12880
12881   return MP_MOVING;
12882 }
12883
12884 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12885 {
12886   int jx = player->jx, jy = player->jy;
12887   int old_jx = jx, old_jy = jy;
12888   int moved = MP_NO_ACTION;
12889
12890   if (!player->active)
12891     return FALSE;
12892
12893   if (!dx && !dy)
12894   {
12895     if (player->MovPos == 0)
12896     {
12897       player->is_moving = FALSE;
12898       player->is_digging = FALSE;
12899       player->is_collecting = FALSE;
12900       player->is_snapping = FALSE;
12901       player->is_pushing = FALSE;
12902     }
12903
12904     return FALSE;
12905   }
12906
12907   if (player->move_delay > 0)
12908     return FALSE;
12909
12910   player->move_delay = -1;              // set to "uninitialized" value
12911
12912   // store if player is automatically moved to next field
12913   player->is_auto_moving = (player->programmed_action != MV_NONE);
12914
12915   // remove the last programmed player action
12916   player->programmed_action = 0;
12917
12918   if (player->MovPos)
12919   {
12920     // should only happen if pre-1.2 tape recordings are played
12921     // this is only for backward compatibility
12922
12923     int original_move_delay_value = player->move_delay_value;
12924
12925 #if DEBUG
12926     Debug("game:playing:MovePlayer",
12927           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12928           tape.counter);
12929 #endif
12930
12931     // scroll remaining steps with finest movement resolution
12932     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12933
12934     while (player->MovPos)
12935     {
12936       ScrollPlayer(player, SCROLL_GO_ON);
12937       ScrollScreen(NULL, SCROLL_GO_ON);
12938
12939       AdvanceFrameAndPlayerCounters(player->index_nr);
12940
12941       DrawAllPlayers();
12942       BackToFront_WithFrameDelay(0);
12943     }
12944
12945     player->move_delay_value = original_move_delay_value;
12946   }
12947
12948   player->is_active = FALSE;
12949
12950   if (player->last_move_dir & MV_HORIZONTAL)
12951   {
12952     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12953       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12954   }
12955   else
12956   {
12957     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12958       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12959   }
12960
12961   if (!moved && !player->is_active)
12962   {
12963     player->is_moving = FALSE;
12964     player->is_digging = FALSE;
12965     player->is_collecting = FALSE;
12966     player->is_snapping = FALSE;
12967     player->is_pushing = FALSE;
12968   }
12969
12970   jx = player->jx;
12971   jy = player->jy;
12972
12973   if (moved & MP_MOVING && !ScreenMovPos &&
12974       (player->index_nr == game.centered_player_nr ||
12975        game.centered_player_nr == -1))
12976   {
12977     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12978
12979     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12980     {
12981       // actual player has left the screen -- scroll in that direction
12982       if (jx != old_jx)         // player has moved horizontally
12983         scroll_x += (jx - old_jx);
12984       else                      // player has moved vertically
12985         scroll_y += (jy - old_jy);
12986     }
12987     else
12988     {
12989       int offset_raw = game.scroll_delay_value;
12990
12991       if (jx != old_jx)         // player has moved horizontally
12992       {
12993         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12994         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12995         int new_scroll_x = jx - MIDPOSX + offset_x;
12996
12997         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12998             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12999           scroll_x = new_scroll_x;
13000
13001         // don't scroll over playfield boundaries
13002         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13003
13004         // don't scroll more than one field at a time
13005         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13006
13007         // don't scroll against the player's moving direction
13008         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13009             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13010           scroll_x = old_scroll_x;
13011       }
13012       else                      // player has moved vertically
13013       {
13014         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13015         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13016         int new_scroll_y = jy - MIDPOSY + offset_y;
13017
13018         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13019             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13020           scroll_y = new_scroll_y;
13021
13022         // don't scroll over playfield boundaries
13023         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13024
13025         // don't scroll more than one field at a time
13026         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13027
13028         // don't scroll against the player's moving direction
13029         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13030             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13031           scroll_y = old_scroll_y;
13032       }
13033     }
13034
13035     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13036     {
13037       if (!network.enabled && game.centered_player_nr == -1 &&
13038           !AllPlayersInVisibleScreen())
13039       {
13040         scroll_x = old_scroll_x;
13041         scroll_y = old_scroll_y;
13042       }
13043       else
13044       {
13045         ScrollScreen(player, SCROLL_INIT);
13046         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13047       }
13048     }
13049   }
13050
13051   player->StepFrame = 0;
13052
13053   if (moved & MP_MOVING)
13054   {
13055     if (old_jx != jx && old_jy == jy)
13056       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13057     else if (old_jx == jx && old_jy != jy)
13058       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13059
13060     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13061
13062     player->last_move_dir = player->MovDir;
13063     player->is_moving = TRUE;
13064     player->is_snapping = FALSE;
13065     player->is_switching = FALSE;
13066     player->is_dropping = FALSE;
13067     player->is_dropping_pressed = FALSE;
13068     player->drop_pressed_delay = 0;
13069
13070 #if 0
13071     // should better be called here than above, but this breaks some tapes
13072     ScrollPlayer(player, SCROLL_INIT);
13073 #endif
13074   }
13075   else
13076   {
13077     CheckGravityMovementWhenNotMoving(player);
13078
13079     player->is_moving = FALSE;
13080
13081     /* at this point, the player is allowed to move, but cannot move right now
13082        (e.g. because of something blocking the way) -- ensure that the player
13083        is also allowed to move in the next frame (in old versions before 3.1.1,
13084        the player was forced to wait again for eight frames before next try) */
13085
13086     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13087       player->move_delay = 0;   // allow direct movement in the next frame
13088   }
13089
13090   if (player->move_delay == -1)         // not yet initialized by DigField()
13091     player->move_delay = player->move_delay_value;
13092
13093   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13094   {
13095     TestIfPlayerTouchesBadThing(jx, jy);
13096     TestIfPlayerTouchesCustomElement(jx, jy);
13097   }
13098
13099   if (!player->active)
13100     RemovePlayer(player);
13101
13102   return moved;
13103 }
13104
13105 void ScrollPlayer(struct PlayerInfo *player, int mode)
13106 {
13107   int jx = player->jx, jy = player->jy;
13108   int last_jx = player->last_jx, last_jy = player->last_jy;
13109   int move_stepsize = TILEX / player->move_delay_value;
13110
13111   if (!player->active)
13112     return;
13113
13114   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13115     return;
13116
13117   if (mode == SCROLL_INIT)
13118   {
13119     player->actual_frame_counter = FrameCounter;
13120     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13121
13122     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13123         Tile[last_jx][last_jy] == EL_EMPTY)
13124     {
13125       int last_field_block_delay = 0;   // start with no blocking at all
13126       int block_delay_adjustment = player->block_delay_adjustment;
13127
13128       // if player blocks last field, add delay for exactly one move
13129       if (player->block_last_field)
13130       {
13131         last_field_block_delay += player->move_delay_value;
13132
13133         // when blocking enabled, prevent moving up despite gravity
13134         if (player->gravity && player->MovDir == MV_UP)
13135           block_delay_adjustment = -1;
13136       }
13137
13138       // add block delay adjustment (also possible when not blocking)
13139       last_field_block_delay += block_delay_adjustment;
13140
13141       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13142       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13143     }
13144
13145     if (player->MovPos != 0)    // player has not yet reached destination
13146       return;
13147   }
13148   else if (!FrameReached(&player->actual_frame_counter, 1))
13149     return;
13150
13151   if (player->MovPos != 0)
13152   {
13153     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13154     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13155
13156     // before DrawPlayer() to draw correct player graphic for this case
13157     if (player->MovPos == 0)
13158       CheckGravityMovement(player);
13159   }
13160
13161   if (player->MovPos == 0)      // player reached destination field
13162   {
13163     if (player->move_delay_reset_counter > 0)
13164     {
13165       player->move_delay_reset_counter--;
13166
13167       if (player->move_delay_reset_counter == 0)
13168       {
13169         // continue with normal speed after quickly moving through gate
13170         HALVE_PLAYER_SPEED(player);
13171
13172         // be able to make the next move without delay
13173         player->move_delay = 0;
13174       }
13175     }
13176
13177     player->last_jx = jx;
13178     player->last_jy = jy;
13179
13180     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13181         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13182         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13183         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13184         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13185         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13186         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13187         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13188     {
13189       ExitPlayer(player);
13190
13191       if (game.players_still_needed == 0 &&
13192           (game.friends_still_needed == 0 ||
13193            IS_SP_ELEMENT(Tile[jx][jy])))
13194         LevelSolved();
13195     }
13196
13197     // this breaks one level: "machine", level 000
13198     {
13199       int move_direction = player->MovDir;
13200       int enter_side = MV_DIR_OPPOSITE(move_direction);
13201       int leave_side = move_direction;
13202       int old_jx = last_jx;
13203       int old_jy = last_jy;
13204       int old_element = Tile[old_jx][old_jy];
13205       int new_element = Tile[jx][jy];
13206
13207       if (IS_CUSTOM_ELEMENT(old_element))
13208         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13209                                    CE_LEFT_BY_PLAYER,
13210                                    player->index_bit, leave_side);
13211
13212       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13213                                           CE_PLAYER_LEAVES_X,
13214                                           player->index_bit, leave_side);
13215
13216       if (IS_CUSTOM_ELEMENT(new_element))
13217         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13218                                    player->index_bit, enter_side);
13219
13220       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13221                                           CE_PLAYER_ENTERS_X,
13222                                           player->index_bit, enter_side);
13223
13224       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13225                                         CE_MOVE_OF_X, move_direction);
13226     }
13227
13228     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13229     {
13230       TestIfPlayerTouchesBadThing(jx, jy);
13231       TestIfPlayerTouchesCustomElement(jx, jy);
13232
13233       /* needed because pushed element has not yet reached its destination,
13234          so it would trigger a change event at its previous field location */
13235       if (!player->is_pushing)
13236         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13237
13238       if (level.finish_dig_collect &&
13239           (player->is_digging || player->is_collecting))
13240       {
13241         int last_element = player->last_removed_element;
13242         int move_direction = player->MovDir;
13243         int enter_side = MV_DIR_OPPOSITE(move_direction);
13244         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13245                             CE_PLAYER_COLLECTS_X);
13246
13247         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13248                                             player->index_bit, enter_side);
13249
13250         player->last_removed_element = EL_UNDEFINED;
13251       }
13252
13253       if (!player->active)
13254         RemovePlayer(player);
13255     }
13256
13257     if (level.use_step_counter)
13258     {
13259       int i;
13260
13261       TimePlayed++;
13262
13263       if (TimeLeft > 0)
13264       {
13265         TimeLeft--;
13266
13267         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13268           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13269
13270         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13271
13272         DisplayGameControlValues();
13273
13274         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13275           for (i = 0; i < MAX_PLAYERS; i++)
13276             KillPlayer(&stored_player[i]);
13277       }
13278       else if (game.no_time_limit && !game.all_players_gone)
13279       {
13280         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13281
13282         DisplayGameControlValues();
13283       }
13284     }
13285
13286     if (tape.single_step && tape.recording && !tape.pausing &&
13287         !player->programmed_action)
13288       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13289
13290     if (!player->programmed_action)
13291       CheckSaveEngineSnapshot(player);
13292   }
13293 }
13294
13295 void ScrollScreen(struct PlayerInfo *player, int mode)
13296 {
13297   static unsigned int screen_frame_counter = 0;
13298
13299   if (mode == SCROLL_INIT)
13300   {
13301     // set scrolling step size according to actual player's moving speed
13302     ScrollStepSize = TILEX / player->move_delay_value;
13303
13304     screen_frame_counter = FrameCounter;
13305     ScreenMovDir = player->MovDir;
13306     ScreenMovPos = player->MovPos;
13307     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13308     return;
13309   }
13310   else if (!FrameReached(&screen_frame_counter, 1))
13311     return;
13312
13313   if (ScreenMovPos)
13314   {
13315     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13316     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13317     redraw_mask |= REDRAW_FIELD;
13318   }
13319   else
13320     ScreenMovDir = MV_NONE;
13321 }
13322
13323 void CheckNextToConditions(int x, int y)
13324 {
13325   int element = Tile[x][y];
13326
13327   if (IS_PLAYER(x, y))
13328     TestIfPlayerNextToCustomElement(x, y);
13329
13330   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13331       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13332     TestIfElementNextToCustomElement(x, y);
13333 }
13334
13335 void TestIfPlayerNextToCustomElement(int x, int y)
13336 {
13337   static int xy[4][2] =
13338   {
13339     { 0, -1 },
13340     { -1, 0 },
13341     { +1, 0 },
13342     { 0, +1 }
13343   };
13344   static int trigger_sides[4][2] =
13345   {
13346     // center side       border side
13347     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13348     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13349     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13350     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13351   };
13352   int i;
13353
13354   if (!IS_PLAYER(x, y))
13355     return;
13356
13357   struct PlayerInfo *player = PLAYERINFO(x, y);
13358
13359   if (player->is_moving)
13360     return;
13361
13362   for (i = 0; i < NUM_DIRECTIONS; i++)
13363   {
13364     int xx = x + xy[i][0];
13365     int yy = y + xy[i][1];
13366     int border_side = trigger_sides[i][1];
13367     int border_element;
13368
13369     if (!IN_LEV_FIELD(xx, yy))
13370       continue;
13371
13372     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13373       continue;         // center and border element not connected
13374
13375     border_element = Tile[xx][yy];
13376
13377     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13378                                player->index_bit, border_side);
13379     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13380                                         CE_PLAYER_NEXT_TO_X,
13381                                         player->index_bit, border_side);
13382
13383     /* use player element that is initially defined in the level playfield,
13384        not the player element that corresponds to the runtime player number
13385        (example: a level that contains EL_PLAYER_3 as the only player would
13386        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13387
13388     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13389                              CE_NEXT_TO_X, border_side);
13390   }
13391 }
13392
13393 void TestIfPlayerTouchesCustomElement(int x, int y)
13394 {
13395   static int xy[4][2] =
13396   {
13397     { 0, -1 },
13398     { -1, 0 },
13399     { +1, 0 },
13400     { 0, +1 }
13401   };
13402   static int trigger_sides[4][2] =
13403   {
13404     // center side       border side
13405     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13406     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13407     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13408     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13409   };
13410   static int touch_dir[4] =
13411   {
13412     MV_LEFT | MV_RIGHT,
13413     MV_UP   | MV_DOWN,
13414     MV_UP   | MV_DOWN,
13415     MV_LEFT | MV_RIGHT
13416   };
13417   int center_element = Tile[x][y];      // should always be non-moving!
13418   int i;
13419
13420   for (i = 0; i < NUM_DIRECTIONS; i++)
13421   {
13422     int xx = x + xy[i][0];
13423     int yy = y + xy[i][1];
13424     int center_side = trigger_sides[i][0];
13425     int border_side = trigger_sides[i][1];
13426     int border_element;
13427
13428     if (!IN_LEV_FIELD(xx, yy))
13429       continue;
13430
13431     if (IS_PLAYER(x, y))                // player found at center element
13432     {
13433       struct PlayerInfo *player = PLAYERINFO(x, y);
13434
13435       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13436         border_element = Tile[xx][yy];          // may be moving!
13437       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13438         border_element = Tile[xx][yy];
13439       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13440         border_element = MovingOrBlocked2Element(xx, yy);
13441       else
13442         continue;               // center and border element do not touch
13443
13444       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13445                                  player->index_bit, border_side);
13446       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13447                                           CE_PLAYER_TOUCHES_X,
13448                                           player->index_bit, border_side);
13449
13450       {
13451         /* use player element that is initially defined in the level playfield,
13452            not the player element that corresponds to the runtime player number
13453            (example: a level that contains EL_PLAYER_3 as the only player would
13454            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13455         int player_element = PLAYERINFO(x, y)->initial_element;
13456
13457         CheckElementChangeBySide(xx, yy, border_element, player_element,
13458                                  CE_TOUCHING_X, border_side);
13459       }
13460     }
13461     else if (IS_PLAYER(xx, yy))         // player found at border element
13462     {
13463       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13464
13465       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13466       {
13467         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13468           continue;             // center and border element do not touch
13469       }
13470
13471       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13472                                  player->index_bit, center_side);
13473       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13474                                           CE_PLAYER_TOUCHES_X,
13475                                           player->index_bit, center_side);
13476
13477       {
13478         /* use player element that is initially defined in the level playfield,
13479            not the player element that corresponds to the runtime player number
13480            (example: a level that contains EL_PLAYER_3 as the only player would
13481            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13482         int player_element = PLAYERINFO(xx, yy)->initial_element;
13483
13484         CheckElementChangeBySide(x, y, center_element, player_element,
13485                                  CE_TOUCHING_X, center_side);
13486       }
13487
13488       break;
13489     }
13490   }
13491 }
13492
13493 void TestIfElementNextToCustomElement(int x, int y)
13494 {
13495   static int xy[4][2] =
13496   {
13497     { 0, -1 },
13498     { -1, 0 },
13499     { +1, 0 },
13500     { 0, +1 }
13501   };
13502   static int trigger_sides[4][2] =
13503   {
13504     // center side      border side
13505     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13506     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13507     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13508     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13509   };
13510   int center_element = Tile[x][y];      // should always be non-moving!
13511   int i;
13512
13513   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13514     return;
13515
13516   for (i = 0; i < NUM_DIRECTIONS; i++)
13517   {
13518     int xx = x + xy[i][0];
13519     int yy = y + xy[i][1];
13520     int border_side = trigger_sides[i][1];
13521     int border_element;
13522
13523     if (!IN_LEV_FIELD(xx, yy))
13524       continue;
13525
13526     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13527       continue;                 // center and border element not connected
13528
13529     border_element = Tile[xx][yy];
13530
13531     // check for change of center element (but change it only once)
13532     if (CheckElementChangeBySide(x, y, center_element, border_element,
13533                                  CE_NEXT_TO_X, border_side))
13534       break;
13535   }
13536 }
13537
13538 void TestIfElementTouchesCustomElement(int x, int y)
13539 {
13540   static int xy[4][2] =
13541   {
13542     { 0, -1 },
13543     { -1, 0 },
13544     { +1, 0 },
13545     { 0, +1 }
13546   };
13547   static int trigger_sides[4][2] =
13548   {
13549     // center side      border side
13550     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13551     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13552     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13553     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13554   };
13555   static int touch_dir[4] =
13556   {
13557     MV_LEFT | MV_RIGHT,
13558     MV_UP   | MV_DOWN,
13559     MV_UP   | MV_DOWN,
13560     MV_LEFT | MV_RIGHT
13561   };
13562   boolean change_center_element = FALSE;
13563   int center_element = Tile[x][y];      // should always be non-moving!
13564   int border_element_old[NUM_DIRECTIONS];
13565   int i;
13566
13567   for (i = 0; i < NUM_DIRECTIONS; i++)
13568   {
13569     int xx = x + xy[i][0];
13570     int yy = y + xy[i][1];
13571     int border_element;
13572
13573     border_element_old[i] = -1;
13574
13575     if (!IN_LEV_FIELD(xx, yy))
13576       continue;
13577
13578     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13579       border_element = Tile[xx][yy];    // may be moving!
13580     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13581       border_element = Tile[xx][yy];
13582     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13583       border_element = MovingOrBlocked2Element(xx, yy);
13584     else
13585       continue;                 // center and border element do not touch
13586
13587     border_element_old[i] = border_element;
13588   }
13589
13590   for (i = 0; i < NUM_DIRECTIONS; i++)
13591   {
13592     int xx = x + xy[i][0];
13593     int yy = y + xy[i][1];
13594     int center_side = trigger_sides[i][0];
13595     int border_element = border_element_old[i];
13596
13597     if (border_element == -1)
13598       continue;
13599
13600     // check for change of border element
13601     CheckElementChangeBySide(xx, yy, border_element, center_element,
13602                              CE_TOUCHING_X, center_side);
13603
13604     // (center element cannot be player, so we dont have to check this here)
13605   }
13606
13607   for (i = 0; i < NUM_DIRECTIONS; i++)
13608   {
13609     int xx = x + xy[i][0];
13610     int yy = y + xy[i][1];
13611     int border_side = trigger_sides[i][1];
13612     int border_element = border_element_old[i];
13613
13614     if (border_element == -1)
13615       continue;
13616
13617     // check for change of center element (but change it only once)
13618     if (!change_center_element)
13619       change_center_element =
13620         CheckElementChangeBySide(x, y, center_element, border_element,
13621                                  CE_TOUCHING_X, border_side);
13622
13623     if (IS_PLAYER(xx, yy))
13624     {
13625       /* use player element that is initially defined in the level playfield,
13626          not the player element that corresponds to the runtime player number
13627          (example: a level that contains EL_PLAYER_3 as the only player would
13628          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13629       int player_element = PLAYERINFO(xx, yy)->initial_element;
13630
13631       CheckElementChangeBySide(x, y, center_element, player_element,
13632                                CE_TOUCHING_X, border_side);
13633     }
13634   }
13635 }
13636
13637 void TestIfElementHitsCustomElement(int x, int y, int direction)
13638 {
13639   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13640   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13641   int hitx = x + dx, hity = y + dy;
13642   int hitting_element = Tile[x][y];
13643   int touched_element;
13644
13645   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13646     return;
13647
13648   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13649                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13650
13651   if (IN_LEV_FIELD(hitx, hity))
13652   {
13653     int opposite_direction = MV_DIR_OPPOSITE(direction);
13654     int hitting_side = direction;
13655     int touched_side = opposite_direction;
13656     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13657                           MovDir[hitx][hity] != direction ||
13658                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13659
13660     object_hit = TRUE;
13661
13662     if (object_hit)
13663     {
13664       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13665                                CE_HITTING_X, touched_side);
13666
13667       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13668                                CE_HIT_BY_X, hitting_side);
13669
13670       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13671                                CE_HIT_BY_SOMETHING, opposite_direction);
13672
13673       if (IS_PLAYER(hitx, hity))
13674       {
13675         /* use player element that is initially defined in the level playfield,
13676            not the player element that corresponds to the runtime player number
13677            (example: a level that contains EL_PLAYER_3 as the only player would
13678            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13679         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13680
13681         CheckElementChangeBySide(x, y, hitting_element, player_element,
13682                                  CE_HITTING_X, touched_side);
13683       }
13684     }
13685   }
13686
13687   // "hitting something" is also true when hitting the playfield border
13688   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13689                            CE_HITTING_SOMETHING, direction);
13690 }
13691
13692 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13693 {
13694   int i, kill_x = -1, kill_y = -1;
13695
13696   int bad_element = -1;
13697   static int test_xy[4][2] =
13698   {
13699     { 0, -1 },
13700     { -1, 0 },
13701     { +1, 0 },
13702     { 0, +1 }
13703   };
13704   static int test_dir[4] =
13705   {
13706     MV_UP,
13707     MV_LEFT,
13708     MV_RIGHT,
13709     MV_DOWN
13710   };
13711
13712   for (i = 0; i < NUM_DIRECTIONS; i++)
13713   {
13714     int test_x, test_y, test_move_dir, test_element;
13715
13716     test_x = good_x + test_xy[i][0];
13717     test_y = good_y + test_xy[i][1];
13718
13719     if (!IN_LEV_FIELD(test_x, test_y))
13720       continue;
13721
13722     test_move_dir =
13723       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13724
13725     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13726
13727     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13728        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13729     */
13730     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13731         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13732     {
13733       kill_x = test_x;
13734       kill_y = test_y;
13735       bad_element = test_element;
13736
13737       break;
13738     }
13739   }
13740
13741   if (kill_x != -1 || kill_y != -1)
13742   {
13743     if (IS_PLAYER(good_x, good_y))
13744     {
13745       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13746
13747       if (player->shield_deadly_time_left > 0 &&
13748           !IS_INDESTRUCTIBLE(bad_element))
13749         Bang(kill_x, kill_y);
13750       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13751         KillPlayer(player);
13752     }
13753     else
13754       Bang(good_x, good_y);
13755   }
13756 }
13757
13758 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13759 {
13760   int i, kill_x = -1, kill_y = -1;
13761   int bad_element = Tile[bad_x][bad_y];
13762   static int test_xy[4][2] =
13763   {
13764     { 0, -1 },
13765     { -1, 0 },
13766     { +1, 0 },
13767     { 0, +1 }
13768   };
13769   static int touch_dir[4] =
13770   {
13771     MV_LEFT | MV_RIGHT,
13772     MV_UP   | MV_DOWN,
13773     MV_UP   | MV_DOWN,
13774     MV_LEFT | MV_RIGHT
13775   };
13776   static int test_dir[4] =
13777   {
13778     MV_UP,
13779     MV_LEFT,
13780     MV_RIGHT,
13781     MV_DOWN
13782   };
13783
13784   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13785     return;
13786
13787   for (i = 0; i < NUM_DIRECTIONS; i++)
13788   {
13789     int test_x, test_y, test_move_dir, test_element;
13790
13791     test_x = bad_x + test_xy[i][0];
13792     test_y = bad_y + test_xy[i][1];
13793
13794     if (!IN_LEV_FIELD(test_x, test_y))
13795       continue;
13796
13797     test_move_dir =
13798       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13799
13800     test_element = Tile[test_x][test_y];
13801
13802     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13803        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13804     */
13805     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13806         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13807     {
13808       // good thing is player or penguin that does not move away
13809       if (IS_PLAYER(test_x, test_y))
13810       {
13811         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13812
13813         if (bad_element == EL_ROBOT && player->is_moving)
13814           continue;     // robot does not kill player if he is moving
13815
13816         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13817         {
13818           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13819             continue;           // center and border element do not touch
13820         }
13821
13822         kill_x = test_x;
13823         kill_y = test_y;
13824
13825         break;
13826       }
13827       else if (test_element == EL_PENGUIN)
13828       {
13829         kill_x = test_x;
13830         kill_y = test_y;
13831
13832         break;
13833       }
13834     }
13835   }
13836
13837   if (kill_x != -1 || kill_y != -1)
13838   {
13839     if (IS_PLAYER(kill_x, kill_y))
13840     {
13841       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13842
13843       if (player->shield_deadly_time_left > 0 &&
13844           !IS_INDESTRUCTIBLE(bad_element))
13845         Bang(bad_x, bad_y);
13846       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13847         KillPlayer(player);
13848     }
13849     else
13850       Bang(kill_x, kill_y);
13851   }
13852 }
13853
13854 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13855 {
13856   int bad_element = Tile[bad_x][bad_y];
13857   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13858   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13859   int test_x = bad_x + dx, test_y = bad_y + dy;
13860   int test_move_dir, test_element;
13861   int kill_x = -1, kill_y = -1;
13862
13863   if (!IN_LEV_FIELD(test_x, test_y))
13864     return;
13865
13866   test_move_dir =
13867     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13868
13869   test_element = Tile[test_x][test_y];
13870
13871   if (test_move_dir != bad_move_dir)
13872   {
13873     // good thing can be player or penguin that does not move away
13874     if (IS_PLAYER(test_x, test_y))
13875     {
13876       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13877
13878       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13879          player as being hit when he is moving towards the bad thing, because
13880          the "get hit by" condition would be lost after the player stops) */
13881       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13882         return;         // player moves away from bad thing
13883
13884       kill_x = test_x;
13885       kill_y = test_y;
13886     }
13887     else if (test_element == EL_PENGUIN)
13888     {
13889       kill_x = test_x;
13890       kill_y = test_y;
13891     }
13892   }
13893
13894   if (kill_x != -1 || kill_y != -1)
13895   {
13896     if (IS_PLAYER(kill_x, kill_y))
13897     {
13898       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13899
13900       if (player->shield_deadly_time_left > 0 &&
13901           !IS_INDESTRUCTIBLE(bad_element))
13902         Bang(bad_x, bad_y);
13903       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13904         KillPlayer(player);
13905     }
13906     else
13907       Bang(kill_x, kill_y);
13908   }
13909 }
13910
13911 void TestIfPlayerTouchesBadThing(int x, int y)
13912 {
13913   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13914 }
13915
13916 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13917 {
13918   TestIfGoodThingHitsBadThing(x, y, move_dir);
13919 }
13920
13921 void TestIfBadThingTouchesPlayer(int x, int y)
13922 {
13923   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13924 }
13925
13926 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13927 {
13928   TestIfBadThingHitsGoodThing(x, y, move_dir);
13929 }
13930
13931 void TestIfFriendTouchesBadThing(int x, int y)
13932 {
13933   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13934 }
13935
13936 void TestIfBadThingTouchesFriend(int x, int y)
13937 {
13938   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13939 }
13940
13941 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13942 {
13943   int i, kill_x = bad_x, kill_y = bad_y;
13944   static int xy[4][2] =
13945   {
13946     { 0, -1 },
13947     { -1, 0 },
13948     { +1, 0 },
13949     { 0, +1 }
13950   };
13951
13952   for (i = 0; i < NUM_DIRECTIONS; i++)
13953   {
13954     int x, y, element;
13955
13956     x = bad_x + xy[i][0];
13957     y = bad_y + xy[i][1];
13958     if (!IN_LEV_FIELD(x, y))
13959       continue;
13960
13961     element = Tile[x][y];
13962     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13963         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13964     {
13965       kill_x = x;
13966       kill_y = y;
13967       break;
13968     }
13969   }
13970
13971   if (kill_x != bad_x || kill_y != bad_y)
13972     Bang(bad_x, bad_y);
13973 }
13974
13975 void KillPlayer(struct PlayerInfo *player)
13976 {
13977   int jx = player->jx, jy = player->jy;
13978
13979   if (!player->active)
13980     return;
13981
13982 #if 0
13983   Debug("game:playing:KillPlayer",
13984         "0: killed == %d, active == %d, reanimated == %d",
13985         player->killed, player->active, player->reanimated);
13986 #endif
13987
13988   /* the following code was introduced to prevent an infinite loop when calling
13989      -> Bang()
13990      -> CheckTriggeredElementChangeExt()
13991      -> ExecuteCustomElementAction()
13992      -> KillPlayer()
13993      -> (infinitely repeating the above sequence of function calls)
13994      which occurs when killing the player while having a CE with the setting
13995      "kill player X when explosion of <player X>"; the solution using a new
13996      field "player->killed" was chosen for backwards compatibility, although
13997      clever use of the fields "player->active" etc. would probably also work */
13998 #if 1
13999   if (player->killed)
14000     return;
14001 #endif
14002
14003   player->killed = TRUE;
14004
14005   // remove accessible field at the player's position
14006   Tile[jx][jy] = EL_EMPTY;
14007
14008   // deactivate shield (else Bang()/Explode() would not work right)
14009   player->shield_normal_time_left = 0;
14010   player->shield_deadly_time_left = 0;
14011
14012 #if 0
14013   Debug("game:playing:KillPlayer",
14014         "1: killed == %d, active == %d, reanimated == %d",
14015         player->killed, player->active, player->reanimated);
14016 #endif
14017
14018   Bang(jx, jy);
14019
14020 #if 0
14021   Debug("game:playing:KillPlayer",
14022         "2: killed == %d, active == %d, reanimated == %d",
14023         player->killed, player->active, player->reanimated);
14024 #endif
14025
14026   if (player->reanimated)       // killed player may have been reanimated
14027     player->killed = player->reanimated = FALSE;
14028   else
14029     BuryPlayer(player);
14030 }
14031
14032 static void KillPlayerUnlessEnemyProtected(int x, int y)
14033 {
14034   if (!PLAYER_ENEMY_PROTECTED(x, y))
14035     KillPlayer(PLAYERINFO(x, y));
14036 }
14037
14038 static void KillPlayerUnlessExplosionProtected(int x, int y)
14039 {
14040   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14041     KillPlayer(PLAYERINFO(x, y));
14042 }
14043
14044 void BuryPlayer(struct PlayerInfo *player)
14045 {
14046   int jx = player->jx, jy = player->jy;
14047
14048   if (!player->active)
14049     return;
14050
14051   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14052   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14053
14054   RemovePlayer(player);
14055
14056   player->buried = TRUE;
14057
14058   if (game.all_players_gone)
14059     game.GameOver = TRUE;
14060 }
14061
14062 void RemovePlayer(struct PlayerInfo *player)
14063 {
14064   int jx = player->jx, jy = player->jy;
14065   int i, found = FALSE;
14066
14067   player->present = FALSE;
14068   player->active = FALSE;
14069
14070   // required for some CE actions (even if the player is not active anymore)
14071   player->MovPos = 0;
14072
14073   if (!ExplodeField[jx][jy])
14074     StorePlayer[jx][jy] = 0;
14075
14076   if (player->is_moving)
14077     TEST_DrawLevelField(player->last_jx, player->last_jy);
14078
14079   for (i = 0; i < MAX_PLAYERS; i++)
14080     if (stored_player[i].active)
14081       found = TRUE;
14082
14083   if (!found)
14084   {
14085     game.all_players_gone = TRUE;
14086     game.GameOver = TRUE;
14087   }
14088
14089   game.exit_x = game.robot_wheel_x = jx;
14090   game.exit_y = game.robot_wheel_y = jy;
14091 }
14092
14093 void ExitPlayer(struct PlayerInfo *player)
14094 {
14095   DrawPlayer(player);   // needed here only to cleanup last field
14096   RemovePlayer(player);
14097
14098   if (game.players_still_needed > 0)
14099     game.players_still_needed--;
14100 }
14101
14102 static void SetFieldForSnapping(int x, int y, int element, int direction,
14103                                 int player_index_bit)
14104 {
14105   struct ElementInfo *ei = &element_info[element];
14106   int direction_bit = MV_DIR_TO_BIT(direction);
14107   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14108   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14109                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14110
14111   Tile[x][y] = EL_ELEMENT_SNAPPING;
14112   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14113   MovDir[x][y] = direction;
14114   Store[x][y] = element;
14115   Store2[x][y] = player_index_bit;
14116
14117   ResetGfxAnimation(x, y);
14118
14119   GfxElement[x][y] = element;
14120   GfxAction[x][y] = action;
14121   GfxDir[x][y] = direction;
14122   GfxFrame[x][y] = -1;
14123 }
14124
14125 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14126                                    int player_index_bit)
14127 {
14128   TestIfElementTouchesCustomElement(x, y);      // for empty space
14129
14130   if (level.finish_dig_collect)
14131   {
14132     int dig_side = MV_DIR_OPPOSITE(direction);
14133     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14134                         CE_PLAYER_COLLECTS_X);
14135
14136     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14137                                         player_index_bit, dig_side);
14138     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14139                                         player_index_bit, dig_side);
14140   }
14141 }
14142
14143 /*
14144   =============================================================================
14145   checkDiagonalPushing()
14146   -----------------------------------------------------------------------------
14147   check if diagonal input device direction results in pushing of object
14148   (by checking if the alternative direction is walkable, diggable, ...)
14149   =============================================================================
14150 */
14151
14152 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14153                                     int x, int y, int real_dx, int real_dy)
14154 {
14155   int jx, jy, dx, dy, xx, yy;
14156
14157   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14158     return TRUE;
14159
14160   // diagonal direction: check alternative direction
14161   jx = player->jx;
14162   jy = player->jy;
14163   dx = x - jx;
14164   dy = y - jy;
14165   xx = jx + (dx == 0 ? real_dx : 0);
14166   yy = jy + (dy == 0 ? real_dy : 0);
14167
14168   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14169 }
14170
14171 /*
14172   =============================================================================
14173   DigField()
14174   -----------------------------------------------------------------------------
14175   x, y:                 field next to player (non-diagonal) to try to dig to
14176   real_dx, real_dy:     direction as read from input device (can be diagonal)
14177   =============================================================================
14178 */
14179
14180 static int DigField(struct PlayerInfo *player,
14181                     int oldx, int oldy, int x, int y,
14182                     int real_dx, int real_dy, int mode)
14183 {
14184   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14185   boolean player_was_pushing = player->is_pushing;
14186   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14187   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14188   int jx = oldx, jy = oldy;
14189   int dx = x - jx, dy = y - jy;
14190   int nextx = x + dx, nexty = y + dy;
14191   int move_direction = (dx == -1 ? MV_LEFT  :
14192                         dx == +1 ? MV_RIGHT :
14193                         dy == -1 ? MV_UP    :
14194                         dy == +1 ? MV_DOWN  : MV_NONE);
14195   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14196   int dig_side = MV_DIR_OPPOSITE(move_direction);
14197   int old_element = Tile[jx][jy];
14198   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14199   int collect_count;
14200
14201   if (is_player)                // function can also be called by EL_PENGUIN
14202   {
14203     if (player->MovPos == 0)
14204     {
14205       player->is_digging = FALSE;
14206       player->is_collecting = FALSE;
14207     }
14208
14209     if (player->MovPos == 0)    // last pushing move finished
14210       player->is_pushing = FALSE;
14211
14212     if (mode == DF_NO_PUSH)     // player just stopped pushing
14213     {
14214       player->is_switching = FALSE;
14215       player->push_delay = -1;
14216
14217       return MP_NO_ACTION;
14218     }
14219   }
14220   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14221     old_element = Back[jx][jy];
14222
14223   // in case of element dropped at player position, check background
14224   else if (Back[jx][jy] != EL_EMPTY &&
14225            game.engine_version >= VERSION_IDENT(2,2,0,0))
14226     old_element = Back[jx][jy];
14227
14228   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14229     return MP_NO_ACTION;        // field has no opening in this direction
14230
14231   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14232     return MP_NO_ACTION;        // field has no opening in this direction
14233
14234   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14235   {
14236     SplashAcid(x, y);
14237
14238     Tile[jx][jy] = player->artwork_element;
14239     InitMovingField(jx, jy, MV_DOWN);
14240     Store[jx][jy] = EL_ACID;
14241     ContinueMoving(jx, jy);
14242     BuryPlayer(player);
14243
14244     return MP_DONT_RUN_INTO;
14245   }
14246
14247   if (player_can_move && DONT_RUN_INTO(element))
14248   {
14249     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14250
14251     return MP_DONT_RUN_INTO;
14252   }
14253
14254   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14255     return MP_NO_ACTION;
14256
14257   collect_count = element_info[element].collect_count_initial;
14258
14259   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14260     return MP_NO_ACTION;
14261
14262   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14263     player_can_move = player_can_move_or_snap;
14264
14265   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14266       game.engine_version >= VERSION_IDENT(2,2,0,0))
14267   {
14268     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14269                                player->index_bit, dig_side);
14270     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14271                                         player->index_bit, dig_side);
14272
14273     if (element == EL_DC_LANDMINE)
14274       Bang(x, y);
14275
14276     if (Tile[x][y] != element)          // field changed by snapping
14277       return MP_ACTION;
14278
14279     return MP_NO_ACTION;
14280   }
14281
14282   if (player->gravity && is_player && !player->is_auto_moving &&
14283       canFallDown(player) && move_direction != MV_DOWN &&
14284       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14285     return MP_NO_ACTION;        // player cannot walk here due to gravity
14286
14287   if (player_can_move &&
14288       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14289   {
14290     int sound_element = SND_ELEMENT(element);
14291     int sound_action = ACTION_WALKING;
14292
14293     if (IS_RND_GATE(element))
14294     {
14295       if (!player->key[RND_GATE_NR(element)])
14296         return MP_NO_ACTION;
14297     }
14298     else if (IS_RND_GATE_GRAY(element))
14299     {
14300       if (!player->key[RND_GATE_GRAY_NR(element)])
14301         return MP_NO_ACTION;
14302     }
14303     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14304     {
14305       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14306         return MP_NO_ACTION;
14307     }
14308     else if (element == EL_EXIT_OPEN ||
14309              element == EL_EM_EXIT_OPEN ||
14310              element == EL_EM_EXIT_OPENING ||
14311              element == EL_STEEL_EXIT_OPEN ||
14312              element == EL_EM_STEEL_EXIT_OPEN ||
14313              element == EL_EM_STEEL_EXIT_OPENING ||
14314              element == EL_SP_EXIT_OPEN ||
14315              element == EL_SP_EXIT_OPENING)
14316     {
14317       sound_action = ACTION_PASSING;    // player is passing exit
14318     }
14319     else if (element == EL_EMPTY)
14320     {
14321       sound_action = ACTION_MOVING;             // nothing to walk on
14322     }
14323
14324     // play sound from background or player, whatever is available
14325     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14326       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14327     else
14328       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14329   }
14330   else if (player_can_move &&
14331            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14332   {
14333     if (!ACCESS_FROM(element, opposite_direction))
14334       return MP_NO_ACTION;      // field not accessible from this direction
14335
14336     if (CAN_MOVE(element))      // only fixed elements can be passed!
14337       return MP_NO_ACTION;
14338
14339     if (IS_EM_GATE(element))
14340     {
14341       if (!player->key[EM_GATE_NR(element)])
14342         return MP_NO_ACTION;
14343     }
14344     else if (IS_EM_GATE_GRAY(element))
14345     {
14346       if (!player->key[EM_GATE_GRAY_NR(element)])
14347         return MP_NO_ACTION;
14348     }
14349     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14350     {
14351       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14352         return MP_NO_ACTION;
14353     }
14354     else if (IS_EMC_GATE(element))
14355     {
14356       if (!player->key[EMC_GATE_NR(element)])
14357         return MP_NO_ACTION;
14358     }
14359     else if (IS_EMC_GATE_GRAY(element))
14360     {
14361       if (!player->key[EMC_GATE_GRAY_NR(element)])
14362         return MP_NO_ACTION;
14363     }
14364     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14365     {
14366       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14367         return MP_NO_ACTION;
14368     }
14369     else if (element == EL_DC_GATE_WHITE ||
14370              element == EL_DC_GATE_WHITE_GRAY ||
14371              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14372     {
14373       if (player->num_white_keys == 0)
14374         return MP_NO_ACTION;
14375
14376       player->num_white_keys--;
14377     }
14378     else if (IS_SP_PORT(element))
14379     {
14380       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14381           element == EL_SP_GRAVITY_PORT_RIGHT ||
14382           element == EL_SP_GRAVITY_PORT_UP ||
14383           element == EL_SP_GRAVITY_PORT_DOWN)
14384         player->gravity = !player->gravity;
14385       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14386                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14387                element == EL_SP_GRAVITY_ON_PORT_UP ||
14388                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14389         player->gravity = TRUE;
14390       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14391                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14392                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14393                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14394         player->gravity = FALSE;
14395     }
14396
14397     // automatically move to the next field with double speed
14398     player->programmed_action = move_direction;
14399
14400     if (player->move_delay_reset_counter == 0)
14401     {
14402       player->move_delay_reset_counter = 2;     // two double speed steps
14403
14404       DOUBLE_PLAYER_SPEED(player);
14405     }
14406
14407     PlayLevelSoundAction(x, y, ACTION_PASSING);
14408   }
14409   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14410   {
14411     RemoveField(x, y);
14412
14413     if (mode != DF_SNAP)
14414     {
14415       GfxElement[x][y] = GFX_ELEMENT(element);
14416       player->is_digging = TRUE;
14417     }
14418
14419     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14420
14421     // use old behaviour for old levels (digging)
14422     if (!level.finish_dig_collect)
14423     {
14424       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14425                                           player->index_bit, dig_side);
14426
14427       // if digging triggered player relocation, finish digging tile
14428       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14429         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14430     }
14431
14432     if (mode == DF_SNAP)
14433     {
14434       if (level.block_snap_field)
14435         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14436       else
14437         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14438
14439       // use old behaviour for old levels (snapping)
14440       if (!level.finish_dig_collect)
14441         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14442                                             player->index_bit, dig_side);
14443     }
14444   }
14445   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14446   {
14447     RemoveField(x, y);
14448
14449     if (is_player && mode != DF_SNAP)
14450     {
14451       GfxElement[x][y] = element;
14452       player->is_collecting = TRUE;
14453     }
14454
14455     if (element == EL_SPEED_PILL)
14456     {
14457       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14458     }
14459     else if (element == EL_EXTRA_TIME && level.time > 0)
14460     {
14461       TimeLeft += level.extra_time;
14462
14463       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14464
14465       DisplayGameControlValues();
14466     }
14467     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14468     {
14469       player->shield_normal_time_left += level.shield_normal_time;
14470       if (element == EL_SHIELD_DEADLY)
14471         player->shield_deadly_time_left += level.shield_deadly_time;
14472     }
14473     else if (element == EL_DYNAMITE ||
14474              element == EL_EM_DYNAMITE ||
14475              element == EL_SP_DISK_RED)
14476     {
14477       if (player->inventory_size < MAX_INVENTORY_SIZE)
14478         player->inventory_element[player->inventory_size++] = element;
14479
14480       DrawGameDoorValues();
14481     }
14482     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14483     {
14484       player->dynabomb_count++;
14485       player->dynabombs_left++;
14486     }
14487     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14488     {
14489       player->dynabomb_size++;
14490     }
14491     else if (element == EL_DYNABOMB_INCREASE_POWER)
14492     {
14493       player->dynabomb_xl = TRUE;
14494     }
14495     else if (IS_KEY(element))
14496     {
14497       player->key[KEY_NR(element)] = TRUE;
14498
14499       DrawGameDoorValues();
14500     }
14501     else if (element == EL_DC_KEY_WHITE)
14502     {
14503       player->num_white_keys++;
14504
14505       // display white keys?
14506       // DrawGameDoorValues();
14507     }
14508     else if (IS_ENVELOPE(element))
14509     {
14510       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14511
14512       if (!wait_for_snapping)
14513         player->show_envelope = element;
14514     }
14515     else if (element == EL_EMC_LENSES)
14516     {
14517       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14518
14519       RedrawAllInvisibleElementsForLenses();
14520     }
14521     else if (element == EL_EMC_MAGNIFIER)
14522     {
14523       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14524
14525       RedrawAllInvisibleElementsForMagnifier();
14526     }
14527     else if (IS_DROPPABLE(element) ||
14528              IS_THROWABLE(element))     // can be collected and dropped
14529     {
14530       int i;
14531
14532       if (collect_count == 0)
14533         player->inventory_infinite_element = element;
14534       else
14535         for (i = 0; i < collect_count; i++)
14536           if (player->inventory_size < MAX_INVENTORY_SIZE)
14537             player->inventory_element[player->inventory_size++] = element;
14538
14539       DrawGameDoorValues();
14540     }
14541     else if (collect_count > 0)
14542     {
14543       game.gems_still_needed -= collect_count;
14544       if (game.gems_still_needed < 0)
14545         game.gems_still_needed = 0;
14546
14547       game.snapshot.collected_item = TRUE;
14548
14549       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14550
14551       DisplayGameControlValues();
14552     }
14553
14554     RaiseScoreElement(element);
14555     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14556
14557     // use old behaviour for old levels (collecting)
14558     if (!level.finish_dig_collect && is_player)
14559     {
14560       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14561                                           player->index_bit, dig_side);
14562
14563       // if collecting triggered player relocation, finish collecting tile
14564       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14565         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14566     }
14567
14568     if (mode == DF_SNAP)
14569     {
14570       if (level.block_snap_field)
14571         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14572       else
14573         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14574
14575       // use old behaviour for old levels (snapping)
14576       if (!level.finish_dig_collect)
14577         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14578                                             player->index_bit, dig_side);
14579     }
14580   }
14581   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14582   {
14583     if (mode == DF_SNAP && element != EL_BD_ROCK)
14584       return MP_NO_ACTION;
14585
14586     if (CAN_FALL(element) && dy)
14587       return MP_NO_ACTION;
14588
14589     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14590         !(element == EL_SPRING && level.use_spring_bug))
14591       return MP_NO_ACTION;
14592
14593     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14594         ((move_direction & MV_VERTICAL &&
14595           ((element_info[element].move_pattern & MV_LEFT &&
14596             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14597            (element_info[element].move_pattern & MV_RIGHT &&
14598             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14599          (move_direction & MV_HORIZONTAL &&
14600           ((element_info[element].move_pattern & MV_UP &&
14601             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14602            (element_info[element].move_pattern & MV_DOWN &&
14603             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14604       return MP_NO_ACTION;
14605
14606     // do not push elements already moving away faster than player
14607     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14608         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14609       return MP_NO_ACTION;
14610
14611     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14612     {
14613       if (player->push_delay_value == -1 || !player_was_pushing)
14614         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14615     }
14616     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14617     {
14618       if (player->push_delay_value == -1)
14619         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14620     }
14621     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14622     {
14623       if (!player->is_pushing)
14624         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14625     }
14626
14627     player->is_pushing = TRUE;
14628     player->is_active = TRUE;
14629
14630     if (!(IN_LEV_FIELD(nextx, nexty) &&
14631           (IS_FREE(nextx, nexty) ||
14632            (IS_SB_ELEMENT(element) &&
14633             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14634            (IS_CUSTOM_ELEMENT(element) &&
14635             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14636       return MP_NO_ACTION;
14637
14638     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14639       return MP_NO_ACTION;
14640
14641     if (player->push_delay == -1)       // new pushing; restart delay
14642       player->push_delay = 0;
14643
14644     if (player->push_delay < player->push_delay_value &&
14645         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14646         element != EL_SPRING && element != EL_BALLOON)
14647     {
14648       // make sure that there is no move delay before next try to push
14649       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14650         player->move_delay = 0;
14651
14652       return MP_NO_ACTION;
14653     }
14654
14655     if (IS_CUSTOM_ELEMENT(element) &&
14656         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14657     {
14658       if (!DigFieldByCE(nextx, nexty, element))
14659         return MP_NO_ACTION;
14660     }
14661
14662     if (IS_SB_ELEMENT(element))
14663     {
14664       boolean sokoban_task_solved = FALSE;
14665
14666       if (element == EL_SOKOBAN_FIELD_FULL)
14667       {
14668         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14669
14670         IncrementSokobanFieldsNeeded();
14671         IncrementSokobanObjectsNeeded();
14672       }
14673
14674       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14675       {
14676         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14677
14678         DecrementSokobanFieldsNeeded();
14679         DecrementSokobanObjectsNeeded();
14680
14681         // sokoban object was pushed from empty field to sokoban field
14682         if (Back[x][y] == EL_EMPTY)
14683           sokoban_task_solved = TRUE;
14684       }
14685
14686       Tile[x][y] = EL_SOKOBAN_OBJECT;
14687
14688       if (Back[x][y] == Back[nextx][nexty])
14689         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14690       else if (Back[x][y] != 0)
14691         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14692                                     ACTION_EMPTYING);
14693       else
14694         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14695                                     ACTION_FILLING);
14696
14697       if (sokoban_task_solved &&
14698           game.sokoban_fields_still_needed == 0 &&
14699           game.sokoban_objects_still_needed == 0 &&
14700           level.auto_exit_sokoban)
14701       {
14702         game.players_still_needed = 0;
14703
14704         LevelSolved();
14705
14706         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14707       }
14708     }
14709     else
14710       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14711
14712     InitMovingField(x, y, move_direction);
14713     GfxAction[x][y] = ACTION_PUSHING;
14714
14715     if (mode == DF_SNAP)
14716       ContinueMoving(x, y);
14717     else
14718       MovPos[x][y] = (dx != 0 ? dx : dy);
14719
14720     Pushed[x][y] = TRUE;
14721     Pushed[nextx][nexty] = TRUE;
14722
14723     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14724       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14725     else
14726       player->push_delay_value = -1;    // get new value later
14727
14728     // check for element change _after_ element has been pushed
14729     if (game.use_change_when_pushing_bug)
14730     {
14731       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14732                                  player->index_bit, dig_side);
14733       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14734                                           player->index_bit, dig_side);
14735     }
14736   }
14737   else if (IS_SWITCHABLE(element))
14738   {
14739     if (PLAYER_SWITCHING(player, x, y))
14740     {
14741       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14742                                           player->index_bit, dig_side);
14743
14744       return MP_ACTION;
14745     }
14746
14747     player->is_switching = TRUE;
14748     player->switch_x = x;
14749     player->switch_y = y;
14750
14751     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14752
14753     if (element == EL_ROBOT_WHEEL)
14754     {
14755       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14756
14757       game.robot_wheel_x = x;
14758       game.robot_wheel_y = y;
14759       game.robot_wheel_active = TRUE;
14760
14761       TEST_DrawLevelField(x, y);
14762     }
14763     else if (element == EL_SP_TERMINAL)
14764     {
14765       int xx, yy;
14766
14767       SCAN_PLAYFIELD(xx, yy)
14768       {
14769         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14770         {
14771           Bang(xx, yy);
14772         }
14773         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14774         {
14775           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14776
14777           ResetGfxAnimation(xx, yy);
14778           TEST_DrawLevelField(xx, yy);
14779         }
14780       }
14781     }
14782     else if (IS_BELT_SWITCH(element))
14783     {
14784       ToggleBeltSwitch(x, y);
14785     }
14786     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14787              element == EL_SWITCHGATE_SWITCH_DOWN ||
14788              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14789              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14790     {
14791       ToggleSwitchgateSwitch(x, y);
14792     }
14793     else if (element == EL_LIGHT_SWITCH ||
14794              element == EL_LIGHT_SWITCH_ACTIVE)
14795     {
14796       ToggleLightSwitch(x, y);
14797     }
14798     else if (element == EL_TIMEGATE_SWITCH ||
14799              element == EL_DC_TIMEGATE_SWITCH)
14800     {
14801       ActivateTimegateSwitch(x, y);
14802     }
14803     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14804              element == EL_BALLOON_SWITCH_RIGHT ||
14805              element == EL_BALLOON_SWITCH_UP    ||
14806              element == EL_BALLOON_SWITCH_DOWN  ||
14807              element == EL_BALLOON_SWITCH_NONE  ||
14808              element == EL_BALLOON_SWITCH_ANY)
14809     {
14810       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14811                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14812                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14813                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14814                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14815                              move_direction);
14816     }
14817     else if (element == EL_LAMP)
14818     {
14819       Tile[x][y] = EL_LAMP_ACTIVE;
14820       game.lights_still_needed--;
14821
14822       ResetGfxAnimation(x, y);
14823       TEST_DrawLevelField(x, y);
14824     }
14825     else if (element == EL_TIME_ORB_FULL)
14826     {
14827       Tile[x][y] = EL_TIME_ORB_EMPTY;
14828
14829       if (level.time > 0 || level.use_time_orb_bug)
14830       {
14831         TimeLeft += level.time_orb_time;
14832         game.no_time_limit = FALSE;
14833
14834         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14835
14836         DisplayGameControlValues();
14837       }
14838
14839       ResetGfxAnimation(x, y);
14840       TEST_DrawLevelField(x, y);
14841     }
14842     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14843              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14844     {
14845       int xx, yy;
14846
14847       game.ball_active = !game.ball_active;
14848
14849       SCAN_PLAYFIELD(xx, yy)
14850       {
14851         int e = Tile[xx][yy];
14852
14853         if (game.ball_active)
14854         {
14855           if (e == EL_EMC_MAGIC_BALL)
14856             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14857           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14858             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14859         }
14860         else
14861         {
14862           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14863             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14864           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14865             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14866         }
14867       }
14868     }
14869
14870     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14871                                         player->index_bit, dig_side);
14872
14873     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14874                                         player->index_bit, dig_side);
14875
14876     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14877                                         player->index_bit, dig_side);
14878
14879     return MP_ACTION;
14880   }
14881   else
14882   {
14883     if (!PLAYER_SWITCHING(player, x, y))
14884     {
14885       player->is_switching = TRUE;
14886       player->switch_x = x;
14887       player->switch_y = y;
14888
14889       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14890                                  player->index_bit, dig_side);
14891       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14892                                           player->index_bit, dig_side);
14893
14894       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14895                                  player->index_bit, dig_side);
14896       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14897                                           player->index_bit, dig_side);
14898     }
14899
14900     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14901                                player->index_bit, dig_side);
14902     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14903                                         player->index_bit, dig_side);
14904
14905     return MP_NO_ACTION;
14906   }
14907
14908   player->push_delay = -1;
14909
14910   if (is_player)                // function can also be called by EL_PENGUIN
14911   {
14912     if (Tile[x][y] != element)          // really digged/collected something
14913     {
14914       player->is_collecting = !player->is_digging;
14915       player->is_active = TRUE;
14916
14917       player->last_removed_element = element;
14918     }
14919   }
14920
14921   return MP_MOVING;
14922 }
14923
14924 static boolean DigFieldByCE(int x, int y, int digging_element)
14925 {
14926   int element = Tile[x][y];
14927
14928   if (!IS_FREE(x, y))
14929   {
14930     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14931                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14932                   ACTION_BREAKING);
14933
14934     // no element can dig solid indestructible elements
14935     if (IS_INDESTRUCTIBLE(element) &&
14936         !IS_DIGGABLE(element) &&
14937         !IS_COLLECTIBLE(element))
14938       return FALSE;
14939
14940     if (AmoebaNr[x][y] &&
14941         (element == EL_AMOEBA_FULL ||
14942          element == EL_BD_AMOEBA ||
14943          element == EL_AMOEBA_GROWING))
14944     {
14945       AmoebaCnt[AmoebaNr[x][y]]--;
14946       AmoebaCnt2[AmoebaNr[x][y]]--;
14947     }
14948
14949     if (IS_MOVING(x, y))
14950       RemoveMovingField(x, y);
14951     else
14952     {
14953       RemoveField(x, y);
14954       TEST_DrawLevelField(x, y);
14955     }
14956
14957     // if digged element was about to explode, prevent the explosion
14958     ExplodeField[x][y] = EX_TYPE_NONE;
14959
14960     PlayLevelSoundAction(x, y, action);
14961   }
14962
14963   Store[x][y] = EL_EMPTY;
14964
14965   // this makes it possible to leave the removed element again
14966   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14967     Store[x][y] = element;
14968
14969   return TRUE;
14970 }
14971
14972 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14973 {
14974   int jx = player->jx, jy = player->jy;
14975   int x = jx + dx, y = jy + dy;
14976   int snap_direction = (dx == -1 ? MV_LEFT  :
14977                         dx == +1 ? MV_RIGHT :
14978                         dy == -1 ? MV_UP    :
14979                         dy == +1 ? MV_DOWN  : MV_NONE);
14980   boolean can_continue_snapping = (level.continuous_snapping &&
14981                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14982
14983   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14984     return FALSE;
14985
14986   if (!player->active || !IN_LEV_FIELD(x, y))
14987     return FALSE;
14988
14989   if (dx && dy)
14990     return FALSE;
14991
14992   if (!dx && !dy)
14993   {
14994     if (player->MovPos == 0)
14995       player->is_pushing = FALSE;
14996
14997     player->is_snapping = FALSE;
14998
14999     if (player->MovPos == 0)
15000     {
15001       player->is_moving = FALSE;
15002       player->is_digging = FALSE;
15003       player->is_collecting = FALSE;
15004     }
15005
15006     return FALSE;
15007   }
15008
15009   // prevent snapping with already pressed snap key when not allowed
15010   if (player->is_snapping && !can_continue_snapping)
15011     return FALSE;
15012
15013   player->MovDir = snap_direction;
15014
15015   if (player->MovPos == 0)
15016   {
15017     player->is_moving = FALSE;
15018     player->is_digging = FALSE;
15019     player->is_collecting = FALSE;
15020   }
15021
15022   player->is_dropping = FALSE;
15023   player->is_dropping_pressed = FALSE;
15024   player->drop_pressed_delay = 0;
15025
15026   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15027     return FALSE;
15028
15029   player->is_snapping = TRUE;
15030   player->is_active = TRUE;
15031
15032   if (player->MovPos == 0)
15033   {
15034     player->is_moving = FALSE;
15035     player->is_digging = FALSE;
15036     player->is_collecting = FALSE;
15037   }
15038
15039   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15040     TEST_DrawLevelField(player->last_jx, player->last_jy);
15041
15042   TEST_DrawLevelField(x, y);
15043
15044   return TRUE;
15045 }
15046
15047 static boolean DropElement(struct PlayerInfo *player)
15048 {
15049   int old_element, new_element;
15050   int dropx = player->jx, dropy = player->jy;
15051   int drop_direction = player->MovDir;
15052   int drop_side = drop_direction;
15053   int drop_element = get_next_dropped_element(player);
15054
15055   /* do not drop an element on top of another element; when holding drop key
15056      pressed without moving, dropped element must move away before the next
15057      element can be dropped (this is especially important if the next element
15058      is dynamite, which can be placed on background for historical reasons) */
15059   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15060     return MP_ACTION;
15061
15062   if (IS_THROWABLE(drop_element))
15063   {
15064     dropx += GET_DX_FROM_DIR(drop_direction);
15065     dropy += GET_DY_FROM_DIR(drop_direction);
15066
15067     if (!IN_LEV_FIELD(dropx, dropy))
15068       return FALSE;
15069   }
15070
15071   old_element = Tile[dropx][dropy];     // old element at dropping position
15072   new_element = drop_element;           // default: no change when dropping
15073
15074   // check if player is active, not moving and ready to drop
15075   if (!player->active || player->MovPos || player->drop_delay > 0)
15076     return FALSE;
15077
15078   // check if player has anything that can be dropped
15079   if (new_element == EL_UNDEFINED)
15080     return FALSE;
15081
15082   // only set if player has anything that can be dropped
15083   player->is_dropping_pressed = TRUE;
15084
15085   // check if drop key was pressed long enough for EM style dynamite
15086   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15087     return FALSE;
15088
15089   // check if anything can be dropped at the current position
15090   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15091     return FALSE;
15092
15093   // collected custom elements can only be dropped on empty fields
15094   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15095     return FALSE;
15096
15097   if (old_element != EL_EMPTY)
15098     Back[dropx][dropy] = old_element;   // store old element on this field
15099
15100   ResetGfxAnimation(dropx, dropy);
15101   ResetRandomAnimationValue(dropx, dropy);
15102
15103   if (player->inventory_size > 0 ||
15104       player->inventory_infinite_element != EL_UNDEFINED)
15105   {
15106     if (player->inventory_size > 0)
15107     {
15108       player->inventory_size--;
15109
15110       DrawGameDoorValues();
15111
15112       if (new_element == EL_DYNAMITE)
15113         new_element = EL_DYNAMITE_ACTIVE;
15114       else if (new_element == EL_EM_DYNAMITE)
15115         new_element = EL_EM_DYNAMITE_ACTIVE;
15116       else if (new_element == EL_SP_DISK_RED)
15117         new_element = EL_SP_DISK_RED_ACTIVE;
15118     }
15119
15120     Tile[dropx][dropy] = new_element;
15121
15122     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15123       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15124                           el2img(Tile[dropx][dropy]), 0);
15125
15126     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15127
15128     // needed if previous element just changed to "empty" in the last frame
15129     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15130
15131     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15132                                player->index_bit, drop_side);
15133     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15134                                         CE_PLAYER_DROPS_X,
15135                                         player->index_bit, drop_side);
15136
15137     TestIfElementTouchesCustomElement(dropx, dropy);
15138   }
15139   else          // player is dropping a dyna bomb
15140   {
15141     player->dynabombs_left--;
15142
15143     Tile[dropx][dropy] = new_element;
15144
15145     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15146       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15147                           el2img(Tile[dropx][dropy]), 0);
15148
15149     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15150   }
15151
15152   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15153     InitField_WithBug1(dropx, dropy, FALSE);
15154
15155   new_element = Tile[dropx][dropy];     // element might have changed
15156
15157   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15158       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15159   {
15160     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15161       MovDir[dropx][dropy] = drop_direction;
15162
15163     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15164
15165     // do not cause impact style collision by dropping elements that can fall
15166     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15167   }
15168
15169   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15170   player->is_dropping = TRUE;
15171
15172   player->drop_pressed_delay = 0;
15173   player->is_dropping_pressed = FALSE;
15174
15175   player->drop_x = dropx;
15176   player->drop_y = dropy;
15177
15178   return TRUE;
15179 }
15180
15181 // ----------------------------------------------------------------------------
15182 // game sound playing functions
15183 // ----------------------------------------------------------------------------
15184
15185 static int *loop_sound_frame = NULL;
15186 static int *loop_sound_volume = NULL;
15187
15188 void InitPlayLevelSound(void)
15189 {
15190   int num_sounds = getSoundListSize();
15191
15192   checked_free(loop_sound_frame);
15193   checked_free(loop_sound_volume);
15194
15195   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15196   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15197 }
15198
15199 static void PlayLevelSound(int x, int y, int nr)
15200 {
15201   int sx = SCREENX(x), sy = SCREENY(y);
15202   int volume, stereo_position;
15203   int max_distance = 8;
15204   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15205
15206   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15207       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15208     return;
15209
15210   if (!IN_LEV_FIELD(x, y) ||
15211       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15212       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15213     return;
15214
15215   volume = SOUND_MAX_VOLUME;
15216
15217   if (!IN_SCR_FIELD(sx, sy))
15218   {
15219     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15220     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15221
15222     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15223   }
15224
15225   stereo_position = (SOUND_MAX_LEFT +
15226                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15227                      (SCR_FIELDX + 2 * max_distance));
15228
15229   if (IS_LOOP_SOUND(nr))
15230   {
15231     /* This assures that quieter loop sounds do not overwrite louder ones,
15232        while restarting sound volume comparison with each new game frame. */
15233
15234     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15235       return;
15236
15237     loop_sound_volume[nr] = volume;
15238     loop_sound_frame[nr] = FrameCounter;
15239   }
15240
15241   PlaySoundExt(nr, volume, stereo_position, type);
15242 }
15243
15244 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15245 {
15246   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15247                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15248                  y < LEVELY(BY1) ? LEVELY(BY1) :
15249                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15250                  sound_action);
15251 }
15252
15253 static void PlayLevelSoundAction(int x, int y, int action)
15254 {
15255   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15256 }
15257
15258 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15259 {
15260   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15261
15262   if (sound_effect != SND_UNDEFINED)
15263     PlayLevelSound(x, y, sound_effect);
15264 }
15265
15266 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15267                                               int action)
15268 {
15269   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15270
15271   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15272     PlayLevelSound(x, y, sound_effect);
15273 }
15274
15275 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15276 {
15277   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15278
15279   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15280     PlayLevelSound(x, y, sound_effect);
15281 }
15282
15283 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15284 {
15285   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15286
15287   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15288     StopSound(sound_effect);
15289 }
15290
15291 static int getLevelMusicNr(void)
15292 {
15293   if (levelset.music[level_nr] != MUS_UNDEFINED)
15294     return levelset.music[level_nr];            // from config file
15295   else
15296     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15297 }
15298
15299 static void FadeLevelSounds(void)
15300 {
15301   FadeSounds();
15302 }
15303
15304 static void FadeLevelMusic(void)
15305 {
15306   int music_nr = getLevelMusicNr();
15307   char *curr_music = getCurrentlyPlayingMusicFilename();
15308   char *next_music = getMusicInfoEntryFilename(music_nr);
15309
15310   if (!strEqual(curr_music, next_music))
15311     FadeMusic();
15312 }
15313
15314 void FadeLevelSoundsAndMusic(void)
15315 {
15316   FadeLevelSounds();
15317   FadeLevelMusic();
15318 }
15319
15320 static void PlayLevelMusic(void)
15321 {
15322   int music_nr = getLevelMusicNr();
15323   char *curr_music = getCurrentlyPlayingMusicFilename();
15324   char *next_music = getMusicInfoEntryFilename(music_nr);
15325
15326   if (!strEqual(curr_music, next_music))
15327     PlayMusicLoop(music_nr);
15328 }
15329
15330 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15331 {
15332   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15333   int offset = 0;
15334   int x = xx - offset;
15335   int y = yy - offset;
15336
15337   switch (sample)
15338   {
15339     case SOUND_blank:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15341       break;
15342
15343     case SOUND_roll:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15345       break;
15346
15347     case SOUND_stone:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15349       break;
15350
15351     case SOUND_nut:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15353       break;
15354
15355     case SOUND_crack:
15356       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15357       break;
15358
15359     case SOUND_bug:
15360       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15361       break;
15362
15363     case SOUND_tank:
15364       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15365       break;
15366
15367     case SOUND_android_clone:
15368       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15369       break;
15370
15371     case SOUND_android_move:
15372       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15373       break;
15374
15375     case SOUND_spring:
15376       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15377       break;
15378
15379     case SOUND_slurp:
15380       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15381       break;
15382
15383     case SOUND_eater:
15384       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15385       break;
15386
15387     case SOUND_eater_eat:
15388       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15389       break;
15390
15391     case SOUND_alien:
15392       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15393       break;
15394
15395     case SOUND_collect:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15397       break;
15398
15399     case SOUND_diamond:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15401       break;
15402
15403     case SOUND_squash:
15404       // !!! CHECK THIS !!!
15405 #if 1
15406       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15407 #else
15408       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15409 #endif
15410       break;
15411
15412     case SOUND_wonderfall:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15414       break;
15415
15416     case SOUND_drip:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15418       break;
15419
15420     case SOUND_push:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15422       break;
15423
15424     case SOUND_dirt:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15426       break;
15427
15428     case SOUND_acid:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15430       break;
15431
15432     case SOUND_ball:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15434       break;
15435
15436     case SOUND_slide:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15438       break;
15439
15440     case SOUND_wonder:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15442       break;
15443
15444     case SOUND_door:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15446       break;
15447
15448     case SOUND_exit_open:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15450       break;
15451
15452     case SOUND_exit_leave:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15454       break;
15455
15456     case SOUND_dynamite:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15458       break;
15459
15460     case SOUND_tick:
15461       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15462       break;
15463
15464     case SOUND_press:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15466       break;
15467
15468     case SOUND_wheel:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15470       break;
15471
15472     case SOUND_boom:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15474       break;
15475
15476     case SOUND_die:
15477       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15478       break;
15479
15480     case SOUND_time:
15481       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15482       break;
15483
15484     default:
15485       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15486       break;
15487   }
15488 }
15489
15490 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15491 {
15492   int element = map_element_SP_to_RND(element_sp);
15493   int action = map_action_SP_to_RND(action_sp);
15494   int offset = (setup.sp_show_border_elements ? 0 : 1);
15495   int x = xx - offset;
15496   int y = yy - offset;
15497
15498   PlayLevelSoundElementAction(x, y, element, action);
15499 }
15500
15501 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15502 {
15503   int element = map_element_MM_to_RND(element_mm);
15504   int action = map_action_MM_to_RND(action_mm);
15505   int offset = 0;
15506   int x = xx - offset;
15507   int y = yy - offset;
15508
15509   if (!IS_MM_ELEMENT(element))
15510     element = EL_MM_DEFAULT;
15511
15512   PlayLevelSoundElementAction(x, y, element, action);
15513 }
15514
15515 void PlaySound_MM(int sound_mm)
15516 {
15517   int sound = map_sound_MM_to_RND(sound_mm);
15518
15519   if (sound == SND_UNDEFINED)
15520     return;
15521
15522   PlaySound(sound);
15523 }
15524
15525 void PlaySoundLoop_MM(int sound_mm)
15526 {
15527   int sound = map_sound_MM_to_RND(sound_mm);
15528
15529   if (sound == SND_UNDEFINED)
15530     return;
15531
15532   PlaySoundLoop(sound);
15533 }
15534
15535 void StopSound_MM(int sound_mm)
15536 {
15537   int sound = map_sound_MM_to_RND(sound_mm);
15538
15539   if (sound == SND_UNDEFINED)
15540     return;
15541
15542   StopSound(sound);
15543 }
15544
15545 void RaiseScore(int value)
15546 {
15547   game.score += value;
15548
15549   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15550
15551   DisplayGameControlValues();
15552 }
15553
15554 void RaiseScoreElement(int element)
15555 {
15556   switch (element)
15557   {
15558     case EL_EMERALD:
15559     case EL_BD_DIAMOND:
15560     case EL_EMERALD_YELLOW:
15561     case EL_EMERALD_RED:
15562     case EL_EMERALD_PURPLE:
15563     case EL_SP_INFOTRON:
15564       RaiseScore(level.score[SC_EMERALD]);
15565       break;
15566     case EL_DIAMOND:
15567       RaiseScore(level.score[SC_DIAMOND]);
15568       break;
15569     case EL_CRYSTAL:
15570       RaiseScore(level.score[SC_CRYSTAL]);
15571       break;
15572     case EL_PEARL:
15573       RaiseScore(level.score[SC_PEARL]);
15574       break;
15575     case EL_BUG:
15576     case EL_BD_BUTTERFLY:
15577     case EL_SP_ELECTRON:
15578       RaiseScore(level.score[SC_BUG]);
15579       break;
15580     case EL_SPACESHIP:
15581     case EL_BD_FIREFLY:
15582     case EL_SP_SNIKSNAK:
15583       RaiseScore(level.score[SC_SPACESHIP]);
15584       break;
15585     case EL_YAMYAM:
15586     case EL_DARK_YAMYAM:
15587       RaiseScore(level.score[SC_YAMYAM]);
15588       break;
15589     case EL_ROBOT:
15590       RaiseScore(level.score[SC_ROBOT]);
15591       break;
15592     case EL_PACMAN:
15593       RaiseScore(level.score[SC_PACMAN]);
15594       break;
15595     case EL_NUT:
15596       RaiseScore(level.score[SC_NUT]);
15597       break;
15598     case EL_DYNAMITE:
15599     case EL_EM_DYNAMITE:
15600     case EL_SP_DISK_RED:
15601     case EL_DYNABOMB_INCREASE_NUMBER:
15602     case EL_DYNABOMB_INCREASE_SIZE:
15603     case EL_DYNABOMB_INCREASE_POWER:
15604       RaiseScore(level.score[SC_DYNAMITE]);
15605       break;
15606     case EL_SHIELD_NORMAL:
15607     case EL_SHIELD_DEADLY:
15608       RaiseScore(level.score[SC_SHIELD]);
15609       break;
15610     case EL_EXTRA_TIME:
15611       RaiseScore(level.extra_time_score);
15612       break;
15613     case EL_KEY_1:
15614     case EL_KEY_2:
15615     case EL_KEY_3:
15616     case EL_KEY_4:
15617     case EL_EM_KEY_1:
15618     case EL_EM_KEY_2:
15619     case EL_EM_KEY_3:
15620     case EL_EM_KEY_4:
15621     case EL_EMC_KEY_5:
15622     case EL_EMC_KEY_6:
15623     case EL_EMC_KEY_7:
15624     case EL_EMC_KEY_8:
15625     case EL_DC_KEY_WHITE:
15626       RaiseScore(level.score[SC_KEY]);
15627       break;
15628     default:
15629       RaiseScore(element_info[element].collect_score);
15630       break;
15631   }
15632 }
15633
15634 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15635 {
15636   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15637   {
15638     if (!quick_quit)
15639     {
15640       // prevent short reactivation of overlay buttons while closing door
15641       SetOverlayActive(FALSE);
15642
15643       // door may still be open due to skipped or envelope style request
15644       CloseDoor(DOOR_CLOSE_1);
15645     }
15646
15647     if (network.enabled)
15648       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15649     else
15650     {
15651       if (quick_quit)
15652         FadeSkipNextFadeIn();
15653
15654       SetGameStatus(GAME_MODE_MAIN);
15655
15656       DrawMainMenu();
15657     }
15658   }
15659   else          // continue playing the game
15660   {
15661     if (tape.playing && tape.deactivate_display)
15662       TapeDeactivateDisplayOff(TRUE);
15663
15664     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15665
15666     if (tape.playing && tape.deactivate_display)
15667       TapeDeactivateDisplayOn();
15668   }
15669 }
15670
15671 void RequestQuitGame(boolean escape_key_pressed)
15672 {
15673   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15674   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15675                         level_editor_test_game);
15676   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15677                           quick_quit);
15678
15679   RequestQuitGameExt(skip_request, quick_quit,
15680                      "Do you really want to quit the game?");
15681 }
15682
15683 void RequestRestartGame(char *message)
15684 {
15685   game.restart_game_message = NULL;
15686
15687   boolean has_started_game = hasStartedNetworkGame();
15688   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15689
15690   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15691   {
15692     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15693   }
15694   else
15695   {
15696     // needed in case of envelope request to close game panel
15697     CloseDoor(DOOR_CLOSE_1);
15698
15699     SetGameStatus(GAME_MODE_MAIN);
15700
15701     DrawMainMenu();
15702   }
15703 }
15704
15705 void CheckGameOver(void)
15706 {
15707   static boolean last_game_over = FALSE;
15708   static int game_over_delay = 0;
15709   int game_over_delay_value = 50;
15710   boolean game_over = checkGameFailed();
15711
15712   // do not handle game over if request dialog is already active
15713   if (game.request_active)
15714     return;
15715
15716   // do not ask to play again if game was never actually played
15717   if (!game.GamePlayed)
15718     return;
15719
15720   if (!game_over)
15721   {
15722     last_game_over = FALSE;
15723     game_over_delay = game_over_delay_value;
15724
15725     return;
15726   }
15727
15728   if (game_over_delay > 0)
15729   {
15730     game_over_delay--;
15731
15732     return;
15733   }
15734
15735   if (last_game_over != game_over)
15736     game.restart_game_message = (hasStartedNetworkGame() ?
15737                                  "Game over! Play it again?" :
15738                                  "Game over!");
15739
15740   last_game_over = game_over;
15741 }
15742
15743 boolean checkGameSolved(void)
15744 {
15745   // set for all game engines if level was solved
15746   return game.LevelSolved_GameEnd;
15747 }
15748
15749 boolean checkGameFailed(void)
15750 {
15751   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15752     return (game_em.game_over && !game_em.level_solved);
15753   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15754     return (game_sp.game_over && !game_sp.level_solved);
15755   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15756     return (game_mm.game_over && !game_mm.level_solved);
15757   else                          // GAME_ENGINE_TYPE_RND
15758     return (game.GameOver && !game.LevelSolved);
15759 }
15760
15761 boolean checkGameEnded(void)
15762 {
15763   return (checkGameSolved() || checkGameFailed());
15764 }
15765
15766
15767 // ----------------------------------------------------------------------------
15768 // random generator functions
15769 // ----------------------------------------------------------------------------
15770
15771 unsigned int InitEngineRandom_RND(int seed)
15772 {
15773   game.num_random_calls = 0;
15774
15775   return InitEngineRandom(seed);
15776 }
15777
15778 unsigned int RND(int max)
15779 {
15780   if (max > 0)
15781   {
15782     game.num_random_calls++;
15783
15784     return GetEngineRandom(max);
15785   }
15786
15787   return 0;
15788 }
15789
15790
15791 // ----------------------------------------------------------------------------
15792 // game engine snapshot handling functions
15793 // ----------------------------------------------------------------------------
15794
15795 struct EngineSnapshotInfo
15796 {
15797   // runtime values for custom element collect score
15798   int collect_score[NUM_CUSTOM_ELEMENTS];
15799
15800   // runtime values for group element choice position
15801   int choice_pos[NUM_GROUP_ELEMENTS];
15802
15803   // runtime values for belt position animations
15804   int belt_graphic[4][NUM_BELT_PARTS];
15805   int belt_anim_mode[4][NUM_BELT_PARTS];
15806 };
15807
15808 static struct EngineSnapshotInfo engine_snapshot_rnd;
15809 static char *snapshot_level_identifier = NULL;
15810 static int snapshot_level_nr = -1;
15811
15812 static void SaveEngineSnapshotValues_RND(void)
15813 {
15814   static int belt_base_active_element[4] =
15815   {
15816     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15817     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15818     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15819     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15820   };
15821   int i, j;
15822
15823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15824   {
15825     int element = EL_CUSTOM_START + i;
15826
15827     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15828   }
15829
15830   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15831   {
15832     int element = EL_GROUP_START + i;
15833
15834     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15835   }
15836
15837   for (i = 0; i < 4; i++)
15838   {
15839     for (j = 0; j < NUM_BELT_PARTS; j++)
15840     {
15841       int element = belt_base_active_element[i] + j;
15842       int graphic = el2img(element);
15843       int anim_mode = graphic_info[graphic].anim_mode;
15844
15845       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15846       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15847     }
15848   }
15849 }
15850
15851 static void LoadEngineSnapshotValues_RND(void)
15852 {
15853   unsigned int num_random_calls = game.num_random_calls;
15854   int i, j;
15855
15856   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15857   {
15858     int element = EL_CUSTOM_START + i;
15859
15860     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15861   }
15862
15863   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15864   {
15865     int element = EL_GROUP_START + i;
15866
15867     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15868   }
15869
15870   for (i = 0; i < 4; i++)
15871   {
15872     for (j = 0; j < NUM_BELT_PARTS; j++)
15873     {
15874       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15875       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15876
15877       graphic_info[graphic].anim_mode = anim_mode;
15878     }
15879   }
15880
15881   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15882   {
15883     InitRND(tape.random_seed);
15884     for (i = 0; i < num_random_calls; i++)
15885       RND(1);
15886   }
15887
15888   if (game.num_random_calls != num_random_calls)
15889   {
15890     Error("number of random calls out of sync");
15891     Error("number of random calls should be %d", num_random_calls);
15892     Error("number of random calls is %d", game.num_random_calls);
15893
15894     Fail("this should not happen -- please debug");
15895   }
15896 }
15897
15898 void FreeEngineSnapshotSingle(void)
15899 {
15900   FreeSnapshotSingle();
15901
15902   setString(&snapshot_level_identifier, NULL);
15903   snapshot_level_nr = -1;
15904 }
15905
15906 void FreeEngineSnapshotList(void)
15907 {
15908   FreeSnapshotList();
15909 }
15910
15911 static ListNode *SaveEngineSnapshotBuffers(void)
15912 {
15913   ListNode *buffers = NULL;
15914
15915   // copy some special values to a structure better suited for the snapshot
15916
15917   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15918     SaveEngineSnapshotValues_RND();
15919   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15920     SaveEngineSnapshotValues_EM();
15921   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15922     SaveEngineSnapshotValues_SP(&buffers);
15923   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15924     SaveEngineSnapshotValues_MM(&buffers);
15925
15926   // save values stored in special snapshot structure
15927
15928   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15929     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15930   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15931     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15932   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15933     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15934   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15935     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15936
15937   // save further RND engine values
15938
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15942
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15948
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15952
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15954
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15957
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15979
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15983
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15986
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15993
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15996
15997 #if 0
15998   ListNode *node = engine_snapshot_list_rnd;
15999   int num_bytes = 0;
16000
16001   while (node != NULL)
16002   {
16003     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16004
16005     node = node->next;
16006   }
16007
16008   Debug("game:playing:SaveEngineSnapshotBuffers",
16009         "size of engine snapshot: %d bytes", num_bytes);
16010 #endif
16011
16012   return buffers;
16013 }
16014
16015 void SaveEngineSnapshotSingle(void)
16016 {
16017   ListNode *buffers = SaveEngineSnapshotBuffers();
16018
16019   // finally save all snapshot buffers to single snapshot
16020   SaveSnapshotSingle(buffers);
16021
16022   // save level identification information
16023   setString(&snapshot_level_identifier, leveldir_current->identifier);
16024   snapshot_level_nr = level_nr;
16025 }
16026
16027 boolean CheckSaveEngineSnapshotToList(void)
16028 {
16029   boolean save_snapshot =
16030     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16031      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16032       game.snapshot.changed_action) ||
16033      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16034       game.snapshot.collected_item));
16035
16036   game.snapshot.changed_action = FALSE;
16037   game.snapshot.collected_item = FALSE;
16038   game.snapshot.save_snapshot = save_snapshot;
16039
16040   return save_snapshot;
16041 }
16042
16043 void SaveEngineSnapshotToList(void)
16044 {
16045   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16046       tape.quick_resume)
16047     return;
16048
16049   ListNode *buffers = SaveEngineSnapshotBuffers();
16050
16051   // finally save all snapshot buffers to snapshot list
16052   SaveSnapshotToList(buffers);
16053 }
16054
16055 void SaveEngineSnapshotToListInitial(void)
16056 {
16057   FreeEngineSnapshotList();
16058
16059   SaveEngineSnapshotToList();
16060 }
16061
16062 static void LoadEngineSnapshotValues(void)
16063 {
16064   // restore special values from snapshot structure
16065
16066   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16067     LoadEngineSnapshotValues_RND();
16068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16069     LoadEngineSnapshotValues_EM();
16070   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16071     LoadEngineSnapshotValues_SP();
16072   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16073     LoadEngineSnapshotValues_MM();
16074 }
16075
16076 void LoadEngineSnapshotSingle(void)
16077 {
16078   LoadSnapshotSingle();
16079
16080   LoadEngineSnapshotValues();
16081 }
16082
16083 static void LoadEngineSnapshot_Undo(int steps)
16084 {
16085   LoadSnapshotFromList_Older(steps);
16086
16087   LoadEngineSnapshotValues();
16088 }
16089
16090 static void LoadEngineSnapshot_Redo(int steps)
16091 {
16092   LoadSnapshotFromList_Newer(steps);
16093
16094   LoadEngineSnapshotValues();
16095 }
16096
16097 boolean CheckEngineSnapshotSingle(void)
16098 {
16099   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16100           snapshot_level_nr == level_nr);
16101 }
16102
16103 boolean CheckEngineSnapshotList(void)
16104 {
16105   return CheckSnapshotList();
16106 }
16107
16108
16109 // ---------- new game button stuff -------------------------------------------
16110
16111 static struct
16112 {
16113   int graphic;
16114   struct XY *pos;
16115   int gadget_id;
16116   boolean *setup_value;
16117   boolean allowed_on_tape;
16118   boolean is_touch_button;
16119   char *infotext;
16120 } gamebutton_info[NUM_GAME_BUTTONS] =
16121 {
16122   {
16123     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16124     GAME_CTRL_ID_STOP,                          NULL,
16125     TRUE, FALSE,                                "stop game"
16126   },
16127   {
16128     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16129     GAME_CTRL_ID_PAUSE,                         NULL,
16130     TRUE, FALSE,                                "pause game"
16131   },
16132   {
16133     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16134     GAME_CTRL_ID_PLAY,                          NULL,
16135     TRUE, FALSE,                                "play game"
16136   },
16137   {
16138     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16139     GAME_CTRL_ID_UNDO,                          NULL,
16140     TRUE, FALSE,                                "undo step"
16141   },
16142   {
16143     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16144     GAME_CTRL_ID_REDO,                          NULL,
16145     TRUE, FALSE,                                "redo step"
16146   },
16147   {
16148     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16149     GAME_CTRL_ID_SAVE,                          NULL,
16150     TRUE, FALSE,                                "save game"
16151   },
16152   {
16153     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16154     GAME_CTRL_ID_PAUSE2,                        NULL,
16155     TRUE, FALSE,                                "pause game"
16156   },
16157   {
16158     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16159     GAME_CTRL_ID_LOAD,                          NULL,
16160     TRUE, FALSE,                                "load game"
16161   },
16162   {
16163     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16164     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16165     FALSE, FALSE,                               "stop game"
16166   },
16167   {
16168     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16169     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16170     FALSE, FALSE,                               "pause game"
16171   },
16172   {
16173     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16174     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16175     FALSE, FALSE,                               "play game"
16176   },
16177   {
16178     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16179     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16180     FALSE, TRUE,                                "stop game"
16181   },
16182   {
16183     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16184     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16185     FALSE, TRUE,                                "pause game"
16186   },
16187   {
16188     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16189     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16190     TRUE, FALSE,                                "background music on/off"
16191   },
16192   {
16193     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16194     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16195     TRUE, FALSE,                                "sound loops on/off"
16196   },
16197   {
16198     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16199     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16200     TRUE, FALSE,                                "normal sounds on/off"
16201   },
16202   {
16203     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16204     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16205     FALSE, FALSE,                               "background music on/off"
16206   },
16207   {
16208     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16209     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16210     FALSE, FALSE,                               "sound loops on/off"
16211   },
16212   {
16213     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16214     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16215     FALSE, FALSE,                               "normal sounds on/off"
16216   }
16217 };
16218
16219 void CreateGameButtons(void)
16220 {
16221   int i;
16222
16223   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16224   {
16225     int graphic = gamebutton_info[i].graphic;
16226     struct GraphicInfo *gfx = &graphic_info[graphic];
16227     struct XY *pos = gamebutton_info[i].pos;
16228     struct GadgetInfo *gi;
16229     int button_type;
16230     boolean checked;
16231     unsigned int event_mask;
16232     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16233     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16234     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16235     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16236     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16237     int gd_x   = gfx->src_x;
16238     int gd_y   = gfx->src_y;
16239     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16240     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16241     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16242     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16243     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16244     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16245     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16246     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16247     int id = i;
16248
16249     if (gfx->bitmap == NULL)
16250     {
16251       game_gadget[id] = NULL;
16252
16253       continue;
16254     }
16255
16256     if (id == GAME_CTRL_ID_STOP ||
16257         id == GAME_CTRL_ID_PANEL_STOP ||
16258         id == GAME_CTRL_ID_TOUCH_STOP ||
16259         id == GAME_CTRL_ID_PLAY ||
16260         id == GAME_CTRL_ID_PANEL_PLAY ||
16261         id == GAME_CTRL_ID_SAVE ||
16262         id == GAME_CTRL_ID_LOAD)
16263     {
16264       button_type = GD_TYPE_NORMAL_BUTTON;
16265       checked = FALSE;
16266       event_mask = GD_EVENT_RELEASED;
16267     }
16268     else if (id == GAME_CTRL_ID_UNDO ||
16269              id == GAME_CTRL_ID_REDO)
16270     {
16271       button_type = GD_TYPE_NORMAL_BUTTON;
16272       checked = FALSE;
16273       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16274     }
16275     else
16276     {
16277       button_type = GD_TYPE_CHECK_BUTTON;
16278       checked = (gamebutton_info[i].setup_value != NULL ?
16279                  *gamebutton_info[i].setup_value : FALSE);
16280       event_mask = GD_EVENT_PRESSED;
16281     }
16282
16283     gi = CreateGadget(GDI_CUSTOM_ID, id,
16284                       GDI_IMAGE_ID, graphic,
16285                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16286                       GDI_X, base_x + x,
16287                       GDI_Y, base_y + y,
16288                       GDI_WIDTH, gfx->width,
16289                       GDI_HEIGHT, gfx->height,
16290                       GDI_TYPE, button_type,
16291                       GDI_STATE, GD_BUTTON_UNPRESSED,
16292                       GDI_CHECKED, checked,
16293                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16294                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16295                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16296                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16297                       GDI_DIRECT_DRAW, FALSE,
16298                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16299                       GDI_EVENT_MASK, event_mask,
16300                       GDI_CALLBACK_ACTION, HandleGameButtons,
16301                       GDI_END);
16302
16303     if (gi == NULL)
16304       Fail("cannot create gadget");
16305
16306     game_gadget[id] = gi;
16307   }
16308 }
16309
16310 void FreeGameButtons(void)
16311 {
16312   int i;
16313
16314   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16315     FreeGadget(game_gadget[i]);
16316 }
16317
16318 static void UnmapGameButtonsAtSamePosition(int id)
16319 {
16320   int i;
16321
16322   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16323     if (i != id &&
16324         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16325         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16326       UnmapGadget(game_gadget[i]);
16327 }
16328
16329 static void UnmapGameButtonsAtSamePosition_All(void)
16330 {
16331   if (setup.show_load_save_buttons)
16332   {
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16335     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16336   }
16337   else if (setup.show_undo_redo_buttons)
16338   {
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16340     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16341     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16342   }
16343   else
16344   {
16345     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16346     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16347     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16348
16349     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16350     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16351     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16352   }
16353 }
16354
16355 void MapLoadSaveButtons(void)
16356 {
16357   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16358   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16359
16360   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16361   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16362 }
16363
16364 void MapUndoRedoButtons(void)
16365 {
16366   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16367   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16368
16369   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16370   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16371 }
16372
16373 void ModifyPauseButtons(void)
16374 {
16375   static int ids[] =
16376   {
16377     GAME_CTRL_ID_PAUSE,
16378     GAME_CTRL_ID_PAUSE2,
16379     GAME_CTRL_ID_PANEL_PAUSE,
16380     GAME_CTRL_ID_TOUCH_PAUSE,
16381     -1
16382   };
16383   int i;
16384
16385   for (i = 0; ids[i] > -1; i++)
16386     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16387 }
16388
16389 static void MapGameButtonsExt(boolean on_tape)
16390 {
16391   int i;
16392
16393   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16394     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16395       MapGadget(game_gadget[i]);
16396
16397   UnmapGameButtonsAtSamePosition_All();
16398
16399   RedrawGameButtons();
16400 }
16401
16402 static void UnmapGameButtonsExt(boolean on_tape)
16403 {
16404   int i;
16405
16406   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16407     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16408       UnmapGadget(game_gadget[i]);
16409 }
16410
16411 static void RedrawGameButtonsExt(boolean on_tape)
16412 {
16413   int i;
16414
16415   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16416     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16417       RedrawGadget(game_gadget[i]);
16418 }
16419
16420 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16421 {
16422   if (gi == NULL)
16423     return;
16424
16425   gi->checked = state;
16426 }
16427
16428 static void RedrawSoundButtonGadget(int id)
16429 {
16430   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16431              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16432              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16433              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16434              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16435              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16436              id);
16437
16438   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16439   RedrawGadget(game_gadget[id2]);
16440 }
16441
16442 void MapGameButtons(void)
16443 {
16444   MapGameButtonsExt(FALSE);
16445 }
16446
16447 void UnmapGameButtons(void)
16448 {
16449   UnmapGameButtonsExt(FALSE);
16450 }
16451
16452 void RedrawGameButtons(void)
16453 {
16454   RedrawGameButtonsExt(FALSE);
16455 }
16456
16457 void MapGameButtonsOnTape(void)
16458 {
16459   MapGameButtonsExt(TRUE);
16460 }
16461
16462 void UnmapGameButtonsOnTape(void)
16463 {
16464   UnmapGameButtonsExt(TRUE);
16465 }
16466
16467 void RedrawGameButtonsOnTape(void)
16468 {
16469   RedrawGameButtonsExt(TRUE);
16470 }
16471
16472 static void GameUndoRedoExt(void)
16473 {
16474   ClearPlayerAction();
16475
16476   tape.pausing = TRUE;
16477
16478   RedrawPlayfield();
16479   UpdateAndDisplayGameControlValues();
16480
16481   DrawCompleteVideoDisplay();
16482   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16483   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16484   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16485
16486   ModifyPauseButtons();
16487
16488   BackToFront();
16489 }
16490
16491 static void GameUndo(int steps)
16492 {
16493   if (!CheckEngineSnapshotList())
16494     return;
16495
16496   int tape_property_bits = tape.property_bits;
16497
16498   LoadEngineSnapshot_Undo(steps);
16499
16500   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16501
16502   GameUndoRedoExt();
16503 }
16504
16505 static void GameRedo(int steps)
16506 {
16507   if (!CheckEngineSnapshotList())
16508     return;
16509
16510   int tape_property_bits = tape.property_bits;
16511
16512   LoadEngineSnapshot_Redo(steps);
16513
16514   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16515
16516   GameUndoRedoExt();
16517 }
16518
16519 static void HandleGameButtonsExt(int id, int button)
16520 {
16521   static boolean game_undo_executed = FALSE;
16522   int steps = BUTTON_STEPSIZE(button);
16523   boolean handle_game_buttons =
16524     (game_status == GAME_MODE_PLAYING ||
16525      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16526
16527   if (!handle_game_buttons)
16528     return;
16529
16530   switch (id)
16531   {
16532     case GAME_CTRL_ID_STOP:
16533     case GAME_CTRL_ID_PANEL_STOP:
16534     case GAME_CTRL_ID_TOUCH_STOP:
16535       if (game_status == GAME_MODE_MAIN)
16536         break;
16537
16538       if (tape.playing)
16539         TapeStop();
16540       else
16541         RequestQuitGame(FALSE);
16542
16543       break;
16544
16545     case GAME_CTRL_ID_PAUSE:
16546     case GAME_CTRL_ID_PAUSE2:
16547     case GAME_CTRL_ID_PANEL_PAUSE:
16548     case GAME_CTRL_ID_TOUCH_PAUSE:
16549       if (network.enabled && game_status == GAME_MODE_PLAYING)
16550       {
16551         if (tape.pausing)
16552           SendToServer_ContinuePlaying();
16553         else
16554           SendToServer_PausePlaying();
16555       }
16556       else
16557         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16558
16559       game_undo_executed = FALSE;
16560
16561       break;
16562
16563     case GAME_CTRL_ID_PLAY:
16564     case GAME_CTRL_ID_PANEL_PLAY:
16565       if (game_status == GAME_MODE_MAIN)
16566       {
16567         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16568       }
16569       else if (tape.pausing)
16570       {
16571         if (network.enabled)
16572           SendToServer_ContinuePlaying();
16573         else
16574           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16575       }
16576       break;
16577
16578     case GAME_CTRL_ID_UNDO:
16579       // Important: When using "save snapshot when collecting an item" mode,
16580       // load last (current) snapshot for first "undo" after pressing "pause"
16581       // (else the last-but-one snapshot would be loaded, because the snapshot
16582       // pointer already points to the last snapshot when pressing "pause",
16583       // which is fine for "every step/move" mode, but not for "every collect")
16584       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16585           !game_undo_executed)
16586         steps--;
16587
16588       game_undo_executed = TRUE;
16589
16590       GameUndo(steps);
16591       break;
16592
16593     case GAME_CTRL_ID_REDO:
16594       GameRedo(steps);
16595       break;
16596
16597     case GAME_CTRL_ID_SAVE:
16598       TapeQuickSave();
16599       break;
16600
16601     case GAME_CTRL_ID_LOAD:
16602       TapeQuickLoad();
16603       break;
16604
16605     case SOUND_CTRL_ID_MUSIC:
16606     case SOUND_CTRL_ID_PANEL_MUSIC:
16607       if (setup.sound_music)
16608       { 
16609         setup.sound_music = FALSE;
16610
16611         FadeMusic();
16612       }
16613       else if (audio.music_available)
16614       { 
16615         setup.sound = setup.sound_music = TRUE;
16616
16617         SetAudioMode(setup.sound);
16618
16619         if (game_status == GAME_MODE_PLAYING)
16620           PlayLevelMusic();
16621       }
16622
16623       RedrawSoundButtonGadget(id);
16624
16625       break;
16626
16627     case SOUND_CTRL_ID_LOOPS:
16628     case SOUND_CTRL_ID_PANEL_LOOPS:
16629       if (setup.sound_loops)
16630         setup.sound_loops = FALSE;
16631       else if (audio.loops_available)
16632       {
16633         setup.sound = setup.sound_loops = TRUE;
16634
16635         SetAudioMode(setup.sound);
16636       }
16637
16638       RedrawSoundButtonGadget(id);
16639
16640       break;
16641
16642     case SOUND_CTRL_ID_SIMPLE:
16643     case SOUND_CTRL_ID_PANEL_SIMPLE:
16644       if (setup.sound_simple)
16645         setup.sound_simple = FALSE;
16646       else if (audio.sound_available)
16647       {
16648         setup.sound = setup.sound_simple = TRUE;
16649
16650         SetAudioMode(setup.sound);
16651       }
16652
16653       RedrawSoundButtonGadget(id);
16654
16655       break;
16656
16657     default:
16658       break;
16659   }
16660 }
16661
16662 static void HandleGameButtons(struct GadgetInfo *gi)
16663 {
16664   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16665 }
16666
16667 void HandleSoundButtonKeys(Key key)
16668 {
16669   if (key == setup.shortcut.sound_simple)
16670     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16671   else if (key == setup.shortcut.sound_loops)
16672     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16673   else if (key == setup.shortcut.sound_music)
16674     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16675 }