moved handling game over condition from MM game engine to main engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576   boolean restarting = (game_status == GAME_MODE_PLAYING);
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   if (restarting)
3589   {
3590     // force fading out global animations displayed during game play
3591     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3592   }
3593   else
3594   {
3595     SetGameStatus(GAME_MODE_PLAYING);
3596   }
3597
3598   if (level_editor_test_game)
3599     FadeSkipNextFadeOut();
3600   else
3601     FadeSetEnterScreen();
3602
3603   if (CheckFadeAll())
3604     fade_mask = REDRAW_ALL;
3605
3606   FadeLevelSoundsAndMusic();
3607
3608   ExpireSoundLoops(TRUE);
3609
3610   FadeOut(fade_mask);
3611
3612   if (restarting)
3613   {
3614     // force restarting global animations displayed during game play
3615     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3616
3617     SetGameStatus(GAME_MODE_PLAYING);
3618   }
3619
3620   if (level_editor_test_game)
3621     FadeSkipNextFadeIn();
3622
3623   // needed if different viewport properties defined for playing
3624   ChangeViewportPropertiesIfNeeded();
3625
3626   ClearField();
3627
3628   DrawCompleteVideoDisplay();
3629
3630   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3631
3632   InitGameEngine();
3633   InitGameControlValues();
3634
3635   if (tape.recording)
3636   {
3637     // initialize tape actions from game when recording tape
3638     tape.use_key_actions   = game.use_key_actions;
3639     tape.use_mouse_actions = game.use_mouse_actions;
3640
3641     // initialize visible playfield size when recording tape (for team mode)
3642     tape.scr_fieldx = SCR_FIELDX;
3643     tape.scr_fieldy = SCR_FIELDY;
3644   }
3645
3646   // don't play tapes over network
3647   network_playing = (network.enabled && !tape.playing);
3648
3649   for (i = 0; i < MAX_PLAYERS; i++)
3650   {
3651     struct PlayerInfo *player = &stored_player[i];
3652
3653     player->index_nr = i;
3654     player->index_bit = (1 << i);
3655     player->element_nr = EL_PLAYER_1 + i;
3656
3657     player->present = FALSE;
3658     player->active = FALSE;
3659     player->mapped = FALSE;
3660
3661     player->killed = FALSE;
3662     player->reanimated = FALSE;
3663     player->buried = FALSE;
3664
3665     player->action = 0;
3666     player->effective_action = 0;
3667     player->programmed_action = 0;
3668     player->snap_action = 0;
3669
3670     player->mouse_action.lx = 0;
3671     player->mouse_action.ly = 0;
3672     player->mouse_action.button = 0;
3673     player->mouse_action.button_hint = 0;
3674
3675     player->effective_mouse_action.lx = 0;
3676     player->effective_mouse_action.ly = 0;
3677     player->effective_mouse_action.button = 0;
3678     player->effective_mouse_action.button_hint = 0;
3679
3680     for (j = 0; j < MAX_NUM_KEYS; j++)
3681       player->key[j] = FALSE;
3682
3683     player->num_white_keys = 0;
3684
3685     player->dynabomb_count = 0;
3686     player->dynabomb_size = 1;
3687     player->dynabombs_left = 0;
3688     player->dynabomb_xl = FALSE;
3689
3690     player->MovDir = initial_move_dir;
3691     player->MovPos = 0;
3692     player->GfxPos = 0;
3693     player->GfxDir = initial_move_dir;
3694     player->GfxAction = ACTION_DEFAULT;
3695     player->Frame = 0;
3696     player->StepFrame = 0;
3697
3698     player->initial_element = player->element_nr;
3699     player->artwork_element =
3700       (level.use_artwork_element[i] ? level.artwork_element[i] :
3701        player->element_nr);
3702     player->use_murphy = FALSE;
3703
3704     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3705     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3706
3707     player->gravity = level.initial_player_gravity[i];
3708
3709     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3710
3711     player->actual_frame_counter.count = 0;
3712     player->actual_frame_counter.value = 1;
3713
3714     player->step_counter = 0;
3715
3716     player->last_move_dir = initial_move_dir;
3717
3718     player->is_active = FALSE;
3719
3720     player->is_waiting = FALSE;
3721     player->is_moving = FALSE;
3722     player->is_auto_moving = FALSE;
3723     player->is_digging = FALSE;
3724     player->is_snapping = FALSE;
3725     player->is_collecting = FALSE;
3726     player->is_pushing = FALSE;
3727     player->is_switching = FALSE;
3728     player->is_dropping = FALSE;
3729     player->is_dropping_pressed = FALSE;
3730
3731     player->is_bored = FALSE;
3732     player->is_sleeping = FALSE;
3733
3734     player->was_waiting = TRUE;
3735     player->was_moving = FALSE;
3736     player->was_snapping = FALSE;
3737     player->was_dropping = FALSE;
3738
3739     player->force_dropping = FALSE;
3740
3741     player->frame_counter_bored = -1;
3742     player->frame_counter_sleeping = -1;
3743
3744     player->anim_delay_counter = 0;
3745     player->post_delay_counter = 0;
3746
3747     player->dir_waiting = initial_move_dir;
3748     player->action_waiting = ACTION_DEFAULT;
3749     player->last_action_waiting = ACTION_DEFAULT;
3750     player->special_action_bored = ACTION_DEFAULT;
3751     player->special_action_sleeping = ACTION_DEFAULT;
3752
3753     player->switch_x = -1;
3754     player->switch_y = -1;
3755
3756     player->drop_x = -1;
3757     player->drop_y = -1;
3758
3759     player->show_envelope = 0;
3760
3761     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3762
3763     player->push_delay       = -1;      // initialized when pushing starts
3764     player->push_delay_value = game.initial_push_delay_value;
3765
3766     player->drop_delay = 0;
3767     player->drop_pressed_delay = 0;
3768
3769     player->last_jx = -1;
3770     player->last_jy = -1;
3771     player->jx = -1;
3772     player->jy = -1;
3773
3774     player->shield_normal_time_left = 0;
3775     player->shield_deadly_time_left = 0;
3776
3777     player->last_removed_element = EL_UNDEFINED;
3778
3779     player->inventory_infinite_element = EL_UNDEFINED;
3780     player->inventory_size = 0;
3781
3782     if (level.use_initial_inventory[i])
3783     {
3784       for (j = 0; j < level.initial_inventory_size[i]; j++)
3785       {
3786         int element = level.initial_inventory_content[i][j];
3787         int collect_count = element_info[element].collect_count_initial;
3788         int k;
3789
3790         if (!IS_CUSTOM_ELEMENT(element))
3791           collect_count = 1;
3792
3793         if (collect_count == 0)
3794           player->inventory_infinite_element = element;
3795         else
3796           for (k = 0; k < collect_count; k++)
3797             if (player->inventory_size < MAX_INVENTORY_SIZE)
3798               player->inventory_element[player->inventory_size++] = element;
3799       }
3800     }
3801
3802     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3803     SnapField(player, 0, 0);
3804
3805     map_player_action[i] = i;
3806   }
3807
3808   network_player_action_received = FALSE;
3809
3810   // initial null action
3811   if (network_playing)
3812     SendToServer_MovePlayer(MV_NONE);
3813
3814   FrameCounter = 0;
3815   TimeFrames = 0;
3816   TimePlayed = 0;
3817   TimeLeft = level.time;
3818   TapeTime = 0;
3819
3820   ScreenMovDir = MV_NONE;
3821   ScreenMovPos = 0;
3822   ScreenGfxPos = 0;
3823
3824   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3825
3826   game.robot_wheel_x = -1;
3827   game.robot_wheel_y = -1;
3828
3829   game.exit_x = -1;
3830   game.exit_y = -1;
3831
3832   game.all_players_gone = FALSE;
3833
3834   game.LevelSolved = FALSE;
3835   game.GameOver = FALSE;
3836
3837   game.GamePlayed = !tape.playing;
3838
3839   game.LevelSolved_GameWon = FALSE;
3840   game.LevelSolved_GameEnd = FALSE;
3841   game.LevelSolved_SaveTape = FALSE;
3842   game.LevelSolved_SaveScore = FALSE;
3843
3844   game.LevelSolved_CountingTime = 0;
3845   game.LevelSolved_CountingScore = 0;
3846   game.LevelSolved_CountingHealth = 0;
3847
3848   game.panel.active = TRUE;
3849
3850   game.no_level_time_limit = (level.time == 0);
3851   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3852
3853   game.yamyam_content_nr = 0;
3854   game.robot_wheel_active = FALSE;
3855   game.magic_wall_active = FALSE;
3856   game.magic_wall_time_left = 0;
3857   game.light_time_left = 0;
3858   game.timegate_time_left = 0;
3859   game.switchgate_pos = 0;
3860   game.wind_direction = level.wind_direction_initial;
3861
3862   game.time_final = 0;
3863   game.score_time_final = 0;
3864
3865   game.score = 0;
3866   game.score_final = 0;
3867
3868   game.health = MAX_HEALTH;
3869   game.health_final = MAX_HEALTH;
3870
3871   game.gems_still_needed = level.gems_needed;
3872   game.sokoban_fields_still_needed = 0;
3873   game.sokoban_objects_still_needed = 0;
3874   game.lights_still_needed = 0;
3875   game.players_still_needed = 0;
3876   game.friends_still_needed = 0;
3877
3878   game.lenses_time_left = 0;
3879   game.magnify_time_left = 0;
3880
3881   game.ball_active = level.ball_active_initial;
3882   game.ball_content_nr = 0;
3883
3884   game.explosions_delayed = TRUE;
3885
3886   game.envelope_active = FALSE;
3887
3888   // special case: set custom artwork setting to initial value
3889   game.use_masked_elements = game.use_masked_elements_initial;
3890
3891   for (i = 0; i < NUM_BELTS; i++)
3892   {
3893     game.belt_dir[i] = MV_NONE;
3894     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3895   }
3896
3897   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3898     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3899
3900 #if DEBUG_INIT_PLAYER
3901   DebugPrintPlayerStatus("Player status at level initialization");
3902 #endif
3903
3904   SCAN_PLAYFIELD(x, y)
3905   {
3906     Tile[x][y] = Last[x][y] = level.field[x][y];
3907     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3908     ChangeDelay[x][y] = 0;
3909     ChangePage[x][y] = -1;
3910     CustomValue[x][y] = 0;              // initialized in InitField()
3911     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3912     AmoebaNr[x][y] = 0;
3913     WasJustMoving[x][y] = 0;
3914     WasJustFalling[x][y] = 0;
3915     CheckCollision[x][y] = 0;
3916     CheckImpact[x][y] = 0;
3917     Stop[x][y] = FALSE;
3918     Pushed[x][y] = FALSE;
3919
3920     ChangeCount[x][y] = 0;
3921     ChangeEvent[x][y] = -1;
3922
3923     ExplodePhase[x][y] = 0;
3924     ExplodeDelay[x][y] = 0;
3925     ExplodeField[x][y] = EX_TYPE_NONE;
3926
3927     RunnerVisit[x][y] = 0;
3928     PlayerVisit[x][y] = 0;
3929
3930     GfxFrame[x][y] = 0;
3931     GfxRandom[x][y] = INIT_GFX_RANDOM();
3932     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3933     GfxElement[x][y] = EL_UNDEFINED;
3934     GfxElementEmpty[x][y] = EL_EMPTY;
3935     GfxAction[x][y] = ACTION_DEFAULT;
3936     GfxDir[x][y] = MV_NONE;
3937     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3938   }
3939
3940   SCAN_PLAYFIELD(x, y)
3941   {
3942     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3943       emulate_bd = FALSE;
3944     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3945       emulate_sp = FALSE;
3946
3947     InitField(x, y, TRUE);
3948
3949     ResetGfxAnimation(x, y);
3950   }
3951
3952   InitBeltMovement();
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955   {
3956     struct PlayerInfo *player = &stored_player[i];
3957
3958     // set number of special actions for bored and sleeping animation
3959     player->num_special_action_bored =
3960       get_num_special_action(player->artwork_element,
3961                              ACTION_BORING_1, ACTION_BORING_LAST);
3962     player->num_special_action_sleeping =
3963       get_num_special_action(player->artwork_element,
3964                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3965   }
3966
3967   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3968                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3969
3970   // initialize type of slippery elements
3971   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3972   {
3973     if (!IS_CUSTOM_ELEMENT(i))
3974     {
3975       // default: elements slip down either to the left or right randomly
3976       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3977
3978       // SP style elements prefer to slip down on the left side
3979       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3980         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3981
3982       // BD style elements prefer to slip down on the left side
3983       if (game.emulation == EMU_BOULDERDASH)
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985     }
3986   }
3987
3988   // initialize explosion and ignition delay
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       int num_phase = 8;
3994       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3995                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3996                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3997       int last_phase = (num_phase + 1) * delay;
3998       int half_phase = (num_phase / 2) * delay;
3999
4000       element_info[i].explosion_delay = last_phase - 1;
4001       element_info[i].ignition_delay = half_phase;
4002
4003       if (i == EL_BLACK_ORB)
4004         element_info[i].ignition_delay = 1;
4005     }
4006   }
4007
4008   // correct non-moving belts to start moving left
4009   for (i = 0; i < NUM_BELTS; i++)
4010     if (game.belt_dir[i] == MV_NONE)
4011       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4012
4013 #if USE_NEW_PLAYER_ASSIGNMENTS
4014   // use preferred player also in local single-player mode
4015   if (!network.enabled && !game.team_mode)
4016   {
4017     int new_index_nr = setup.network_player_nr;
4018
4019     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4020     {
4021       for (i = 0; i < MAX_PLAYERS; i++)
4022         stored_player[i].connected_locally = FALSE;
4023
4024       stored_player[new_index_nr].connected_locally = TRUE;
4025     }
4026   }
4027
4028   for (i = 0; i < MAX_PLAYERS; i++)
4029   {
4030     stored_player[i].connected = FALSE;
4031
4032     // in network game mode, the local player might not be the first player
4033     if (stored_player[i].connected_locally)
4034       local_player = &stored_player[i];
4035   }
4036
4037   if (!network.enabled)
4038     local_player->connected = TRUE;
4039
4040   if (tape.playing)
4041   {
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043       stored_player[i].connected = tape.player_participates[i];
4044   }
4045   else if (network.enabled)
4046   {
4047     // add team mode players connected over the network (needed for correct
4048     // assignment of player figures from level to locally playing players)
4049
4050     for (i = 0; i < MAX_PLAYERS; i++)
4051       if (stored_player[i].connected_network)
4052         stored_player[i].connected = TRUE;
4053   }
4054   else if (game.team_mode)
4055   {
4056     // try to guess locally connected team mode players (needed for correct
4057     // assignment of player figures from level to locally playing players)
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060       if (setup.input[i].use_joystick ||
4061           setup.input[i].key.left != KSYM_UNDEFINED)
4062         stored_player[i].connected = TRUE;
4063   }
4064
4065 #if DEBUG_INIT_PLAYER
4066   DebugPrintPlayerStatus("Player status after level initialization");
4067 #endif
4068
4069 #if DEBUG_INIT_PLAYER
4070   Debug("game:init:player", "Reassigning players ...");
4071 #endif
4072
4073   // check if any connected player was not found in playfield
4074   for (i = 0; i < MAX_PLAYERS; i++)
4075   {
4076     struct PlayerInfo *player = &stored_player[i];
4077
4078     if (player->connected && !player->present)
4079     {
4080       struct PlayerInfo *field_player = NULL;
4081
4082 #if DEBUG_INIT_PLAYER
4083       Debug("game:init:player",
4084             "- looking for field player for player %d ...", i + 1);
4085 #endif
4086
4087       // assign first free player found that is present in the playfield
4088
4089       // first try: look for unmapped playfield player that is not connected
4090       for (j = 0; j < MAX_PLAYERS; j++)
4091         if (field_player == NULL &&
4092             stored_player[j].present &&
4093             !stored_player[j].mapped &&
4094             !stored_player[j].connected)
4095           field_player = &stored_player[j];
4096
4097       // second try: look for *any* unmapped playfield player
4098       for (j = 0; j < MAX_PLAYERS; j++)
4099         if (field_player == NULL &&
4100             stored_player[j].present &&
4101             !stored_player[j].mapped)
4102           field_player = &stored_player[j];
4103
4104       if (field_player != NULL)
4105       {
4106         int jx = field_player->jx, jy = field_player->jy;
4107
4108 #if DEBUG_INIT_PLAYER
4109         Debug("game:init:player", "- found player %d",
4110               field_player->index_nr + 1);
4111 #endif
4112
4113         player->present = FALSE;
4114         player->active = FALSE;
4115
4116         field_player->present = TRUE;
4117         field_player->active = TRUE;
4118
4119         /*
4120         player->initial_element = field_player->initial_element;
4121         player->artwork_element = field_player->artwork_element;
4122
4123         player->block_last_field       = field_player->block_last_field;
4124         player->block_delay_adjustment = field_player->block_delay_adjustment;
4125         */
4126
4127         StorePlayer[jx][jy] = field_player->element_nr;
4128
4129         field_player->jx = field_player->last_jx = jx;
4130         field_player->jy = field_player->last_jy = jy;
4131
4132         if (local_player == player)
4133           local_player = field_player;
4134
4135         map_player_action[field_player->index_nr] = i;
4136
4137         field_player->mapped = TRUE;
4138
4139 #if DEBUG_INIT_PLAYER
4140         Debug("game:init:player", "- map_player_action[%d] == %d",
4141               field_player->index_nr + 1, i + 1);
4142 #endif
4143       }
4144     }
4145
4146     if (player->connected && player->present)
4147       player->mapped = TRUE;
4148   }
4149
4150 #if DEBUG_INIT_PLAYER
4151   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4152 #endif
4153
4154 #else
4155
4156   // check if any connected player was not found in playfield
4157   for (i = 0; i < MAX_PLAYERS; i++)
4158   {
4159     struct PlayerInfo *player = &stored_player[i];
4160
4161     if (player->connected && !player->present)
4162     {
4163       for (j = 0; j < MAX_PLAYERS; j++)
4164       {
4165         struct PlayerInfo *field_player = &stored_player[j];
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168         // assign first free player found that is present in the playfield
4169         if (field_player->present && !field_player->connected)
4170         {
4171           player->present = TRUE;
4172           player->active = TRUE;
4173
4174           field_player->present = FALSE;
4175           field_player->active = FALSE;
4176
4177           player->initial_element = field_player->initial_element;
4178           player->artwork_element = field_player->artwork_element;
4179
4180           player->block_last_field       = field_player->block_last_field;
4181           player->block_delay_adjustment = field_player->block_delay_adjustment;
4182
4183           StorePlayer[jx][jy] = player->element_nr;
4184
4185           player->jx = player->last_jx = jx;
4186           player->jy = player->last_jy = jy;
4187
4188           break;
4189         }
4190       }
4191     }
4192   }
4193 #endif
4194
4195 #if 0
4196   Debug("game:init:player", "local_player->present == %d",
4197         local_player->present);
4198 #endif
4199
4200   // set focus to local player for network games, else to all players
4201   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4202   game.centered_player_nr_next = game.centered_player_nr;
4203   game.set_centered_player = FALSE;
4204   game.set_centered_player_wrap = FALSE;
4205
4206   if (network_playing && tape.recording)
4207   {
4208     // store client dependent player focus when recording network games
4209     tape.centered_player_nr_next = game.centered_player_nr_next;
4210     tape.set_centered_player = TRUE;
4211   }
4212
4213   if (tape.playing)
4214   {
4215     // when playing a tape, eliminate all players who do not participate
4216
4217 #if USE_NEW_PLAYER_ASSIGNMENTS
4218
4219     if (!game.team_mode)
4220     {
4221       for (i = 0; i < MAX_PLAYERS; i++)
4222       {
4223         if (stored_player[i].active &&
4224             !tape.player_participates[map_player_action[i]])
4225         {
4226           struct PlayerInfo *player = &stored_player[i];
4227           int jx = player->jx, jy = player->jy;
4228
4229 #if DEBUG_INIT_PLAYER
4230           Debug("game:init:player", "Removing player %d at (%d, %d)",
4231                 i + 1, jx, jy);
4232 #endif
4233
4234           player->active = FALSE;
4235           StorePlayer[jx][jy] = 0;
4236           Tile[jx][jy] = EL_EMPTY;
4237         }
4238       }
4239     }
4240
4241 #else
4242
4243     for (i = 0; i < MAX_PLAYERS; i++)
4244     {
4245       if (stored_player[i].active &&
4246           !tape.player_participates[i])
4247       {
4248         struct PlayerInfo *player = &stored_player[i];
4249         int jx = player->jx, jy = player->jy;
4250
4251         player->active = FALSE;
4252         StorePlayer[jx][jy] = 0;
4253         Tile[jx][jy] = EL_EMPTY;
4254       }
4255     }
4256 #endif
4257   }
4258   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4259   {
4260     // when in single player mode, eliminate all but the local player
4261
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263     {
4264       struct PlayerInfo *player = &stored_player[i];
4265
4266       if (player->active && player != local_player)
4267       {
4268         int jx = player->jx, jy = player->jy;
4269
4270         player->active = FALSE;
4271         player->present = FALSE;
4272
4273         StorePlayer[jx][jy] = 0;
4274         Tile[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277   }
4278
4279   for (i = 0; i < MAX_PLAYERS; i++)
4280     if (stored_player[i].active)
4281       game.players_still_needed++;
4282
4283   if (level.solved_by_one_player)
4284     game.players_still_needed = 1;
4285
4286   // when recording the game, store which players take part in the game
4287   if (tape.recording)
4288   {
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291       if (stored_player[i].connected)
4292         tape.player_participates[i] = TRUE;
4293 #else
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].active)
4296         tape.player_participates[i] = TRUE;
4297 #endif
4298   }
4299
4300 #if DEBUG_INIT_PLAYER
4301   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4302 #endif
4303
4304   if (BorderElement == EL_EMPTY)
4305   {
4306     SBX_Left = 0;
4307     SBX_Right = lev_fieldx - SCR_FIELDX;
4308     SBY_Upper = 0;
4309     SBY_Lower = lev_fieldy - SCR_FIELDY;
4310   }
4311   else
4312   {
4313     SBX_Left = -1;
4314     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4315     SBY_Upper = -1;
4316     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4317   }
4318
4319   if (full_lev_fieldx <= SCR_FIELDX)
4320     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4321   if (full_lev_fieldy <= SCR_FIELDY)
4322     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4323
4324   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4325     SBX_Left--;
4326   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4327     SBY_Upper--;
4328
4329   // if local player not found, look for custom element that might create
4330   // the player (make some assumptions about the right custom element)
4331   if (!local_player->present)
4332   {
4333     int start_x = 0, start_y = 0;
4334     int found_rating = 0;
4335     int found_element = EL_UNDEFINED;
4336     int player_nr = local_player->index_nr;
4337
4338     SCAN_PLAYFIELD(x, y)
4339     {
4340       int element = Tile[x][y];
4341       int content;
4342       int xx, yy;
4343       boolean is_player;
4344
4345       if (level.use_start_element[player_nr] &&
4346           level.start_element[player_nr] == element &&
4347           found_rating < 4)
4348       {
4349         start_x = x;
4350         start_y = y;
4351
4352         found_rating = 4;
4353         found_element = element;
4354       }
4355
4356       if (!IS_CUSTOM_ELEMENT(element))
4357         continue;
4358
4359       if (CAN_CHANGE(element))
4360       {
4361         for (i = 0; i < element_info[element].num_change_pages; i++)
4362         {
4363           // check for player created from custom element as single target
4364           content = element_info[element].change_page[i].target_element;
4365           is_player = IS_PLAYER_ELEMENT(content);
4366
4367           if (is_player && (found_rating < 3 ||
4368                             (found_rating == 3 && element < found_element)))
4369           {
4370             start_x = x;
4371             start_y = y;
4372
4373             found_rating = 3;
4374             found_element = element;
4375           }
4376         }
4377       }
4378
4379       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4380       {
4381         // check for player created from custom element as explosion content
4382         content = element_info[element].content.e[xx][yy];
4383         is_player = IS_PLAYER_ELEMENT(content);
4384
4385         if (is_player && (found_rating < 2 ||
4386                           (found_rating == 2 && element < found_element)))
4387         {
4388           start_x = x + xx - 1;
4389           start_y = y + yy - 1;
4390
4391           found_rating = 2;
4392           found_element = element;
4393         }
4394
4395         if (!CAN_CHANGE(element))
4396           continue;
4397
4398         for (i = 0; i < element_info[element].num_change_pages; i++)
4399         {
4400           // check for player created from custom element as extended target
4401           content =
4402             element_info[element].change_page[i].target_content.e[xx][yy];
4403
4404           is_player = IS_PLAYER_ELEMENT(content);
4405
4406           if (is_player && (found_rating < 1 ||
4407                             (found_rating == 1 && element < found_element)))
4408           {
4409             start_x = x + xx - 1;
4410             start_y = y + yy - 1;
4411
4412             found_rating = 1;
4413             found_element = element;
4414           }
4415         }
4416       }
4417     }
4418
4419     scroll_x = SCROLL_POSITION_X(start_x);
4420     scroll_y = SCROLL_POSITION_Y(start_y);
4421   }
4422   else
4423   {
4424     scroll_x = SCROLL_POSITION_X(local_player->jx);
4425     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4426   }
4427
4428   // !!! FIX THIS (START) !!!
4429   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4430   {
4431     InitGameEngine_EM();
4432   }
4433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4434   {
4435     InitGameEngine_SP();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4438   {
4439     InitGameEngine_MM();
4440   }
4441   else
4442   {
4443     DrawLevel(REDRAW_FIELD);
4444     DrawAllPlayers();
4445
4446     // after drawing the level, correct some elements
4447     if (game.timegate_time_left == 0)
4448       CloseAllOpenTimegates();
4449   }
4450
4451   // blit playfield from scroll buffer to normal back buffer for fading in
4452   BlitScreenToBitmap(backbuffer);
4453   // !!! FIX THIS (END) !!!
4454
4455   DrawMaskedBorder(fade_mask);
4456
4457   FadeIn(fade_mask);
4458
4459 #if 1
4460   // full screen redraw is required at this point in the following cases:
4461   // - special editor door undrawn when game was started from level editor
4462   // - drawing area (playfield) was changed and has to be removed completely
4463   redraw_mask = REDRAW_ALL;
4464   BackToFront();
4465 #endif
4466
4467   if (!game.restart_level)
4468   {
4469     // copy default game door content to main double buffer
4470
4471     // !!! CHECK AGAIN !!!
4472     SetPanelBackground();
4473     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4474     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4475   }
4476
4477   SetPanelBackground();
4478   SetDrawBackgroundMask(REDRAW_DOOR_1);
4479
4480   UpdateAndDisplayGameControlValues();
4481
4482   if (!game.restart_level)
4483   {
4484     UnmapGameButtons();
4485     UnmapTapeButtons();
4486
4487     FreeGameButtons();
4488     CreateGameButtons();
4489
4490     MapGameButtons();
4491     MapTapeButtons();
4492
4493     // copy actual game door content to door double buffer for OpenDoor()
4494     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4495
4496     OpenDoor(DOOR_OPEN_ALL);
4497
4498     KeyboardAutoRepeatOffUnlessAutoplay();
4499
4500 #if DEBUG_INIT_PLAYER
4501     DebugPrintPlayerStatus("Player status (final)");
4502 #endif
4503   }
4504
4505   UnmapAllGadgets();
4506
4507   MapGameButtons();
4508   MapTapeButtons();
4509
4510   if (!game.restart_level && !tape.playing)
4511   {
4512     LevelStats_incPlayed(level_nr);
4513
4514     SaveLevelSetup_SeriesInfo();
4515   }
4516
4517   game.restart_level = FALSE;
4518   game.restart_game_message = NULL;
4519
4520   game.request_active = FALSE;
4521   game.request_active_or_moving = FALSE;
4522
4523   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4524     InitGameActions_MM();
4525
4526   SaveEngineSnapshotToListInitial();
4527
4528   if (!game.restart_level)
4529   {
4530     PlaySound(SND_GAME_STARTING);
4531
4532     if (setup.sound_music)
4533       PlayLevelMusic();
4534   }
4535
4536   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4537 }
4538
4539 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4540                         int actual_player_x, int actual_player_y)
4541 {
4542   // this is used for non-R'n'D game engines to update certain engine values
4543
4544   // needed to determine if sounds are played within the visible screen area
4545   scroll_x = actual_scroll_x;
4546   scroll_y = actual_scroll_y;
4547
4548   // needed to get player position for "follow finger" playing input method
4549   local_player->jx = actual_player_x;
4550   local_player->jy = actual_player_y;
4551 }
4552
4553 void InitMovDir(int x, int y)
4554 {
4555   int i, element = Tile[x][y];
4556   static int xy[4][2] =
4557   {
4558     {  0, +1 },
4559     { +1,  0 },
4560     {  0, -1 },
4561     { -1,  0 }
4562   };
4563   static int direction[3][4] =
4564   {
4565     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4566     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4567     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4568   };
4569
4570   switch (element)
4571   {
4572     case EL_BUG_RIGHT:
4573     case EL_BUG_UP:
4574     case EL_BUG_LEFT:
4575     case EL_BUG_DOWN:
4576       Tile[x][y] = EL_BUG;
4577       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4578       break;
4579
4580     case EL_SPACESHIP_RIGHT:
4581     case EL_SPACESHIP_UP:
4582     case EL_SPACESHIP_LEFT:
4583     case EL_SPACESHIP_DOWN:
4584       Tile[x][y] = EL_SPACESHIP;
4585       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4586       break;
4587
4588     case EL_BD_BUTTERFLY_RIGHT:
4589     case EL_BD_BUTTERFLY_UP:
4590     case EL_BD_BUTTERFLY_LEFT:
4591     case EL_BD_BUTTERFLY_DOWN:
4592       Tile[x][y] = EL_BD_BUTTERFLY;
4593       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4594       break;
4595
4596     case EL_BD_FIREFLY_RIGHT:
4597     case EL_BD_FIREFLY_UP:
4598     case EL_BD_FIREFLY_LEFT:
4599     case EL_BD_FIREFLY_DOWN:
4600       Tile[x][y] = EL_BD_FIREFLY;
4601       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4602       break;
4603
4604     case EL_PACMAN_RIGHT:
4605     case EL_PACMAN_UP:
4606     case EL_PACMAN_LEFT:
4607     case EL_PACMAN_DOWN:
4608       Tile[x][y] = EL_PACMAN;
4609       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4610       break;
4611
4612     case EL_YAMYAM_LEFT:
4613     case EL_YAMYAM_RIGHT:
4614     case EL_YAMYAM_UP:
4615     case EL_YAMYAM_DOWN:
4616       Tile[x][y] = EL_YAMYAM;
4617       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4618       break;
4619
4620     case EL_SP_SNIKSNAK:
4621       MovDir[x][y] = MV_UP;
4622       break;
4623
4624     case EL_SP_ELECTRON:
4625       MovDir[x][y] = MV_LEFT;
4626       break;
4627
4628     case EL_MOLE_LEFT:
4629     case EL_MOLE_RIGHT:
4630     case EL_MOLE_UP:
4631     case EL_MOLE_DOWN:
4632       Tile[x][y] = EL_MOLE;
4633       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4634       break;
4635
4636     case EL_SPRING_LEFT:
4637     case EL_SPRING_RIGHT:
4638       Tile[x][y] = EL_SPRING;
4639       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4640       break;
4641
4642     default:
4643       if (IS_CUSTOM_ELEMENT(element))
4644       {
4645         struct ElementInfo *ei = &element_info[element];
4646         int move_direction_initial = ei->move_direction_initial;
4647         int move_pattern = ei->move_pattern;
4648
4649         if (move_direction_initial == MV_START_PREVIOUS)
4650         {
4651           if (MovDir[x][y] != MV_NONE)
4652             return;
4653
4654           move_direction_initial = MV_START_AUTOMATIC;
4655         }
4656
4657         if (move_direction_initial == MV_START_RANDOM)
4658           MovDir[x][y] = 1 << RND(4);
4659         else if (move_direction_initial & MV_ANY_DIRECTION)
4660           MovDir[x][y] = move_direction_initial;
4661         else if (move_pattern == MV_ALL_DIRECTIONS ||
4662                  move_pattern == MV_TURNING_LEFT ||
4663                  move_pattern == MV_TURNING_RIGHT ||
4664                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4665                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4666                  move_pattern == MV_TURNING_RANDOM)
4667           MovDir[x][y] = 1 << RND(4);
4668         else if (move_pattern == MV_HORIZONTAL)
4669           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4670         else if (move_pattern == MV_VERTICAL)
4671           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4672         else if (move_pattern & MV_ANY_DIRECTION)
4673           MovDir[x][y] = element_info[element].move_pattern;
4674         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4675                  move_pattern == MV_ALONG_RIGHT_SIDE)
4676         {
4677           // use random direction as default start direction
4678           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4679             MovDir[x][y] = 1 << RND(4);
4680
4681           for (i = 0; i < NUM_DIRECTIONS; i++)
4682           {
4683             int x1 = x + xy[i][0];
4684             int y1 = y + xy[i][1];
4685
4686             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4687             {
4688               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4689                 MovDir[x][y] = direction[0][i];
4690               else
4691                 MovDir[x][y] = direction[1][i];
4692
4693               break;
4694             }
4695           }
4696         }                
4697       }
4698       else
4699       {
4700         MovDir[x][y] = 1 << RND(4);
4701
4702         if (element != EL_BUG &&
4703             element != EL_SPACESHIP &&
4704             element != EL_BD_BUTTERFLY &&
4705             element != EL_BD_FIREFLY)
4706           break;
4707
4708         for (i = 0; i < NUM_DIRECTIONS; i++)
4709         {
4710           int x1 = x + xy[i][0];
4711           int y1 = y + xy[i][1];
4712
4713           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4714           {
4715             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4716             {
4717               MovDir[x][y] = direction[0][i];
4718               break;
4719             }
4720             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4721                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4722             {
4723               MovDir[x][y] = direction[1][i];
4724               break;
4725             }
4726           }
4727         }
4728       }
4729       break;
4730   }
4731
4732   GfxDir[x][y] = MovDir[x][y];
4733 }
4734
4735 void InitAmoebaNr(int x, int y)
4736 {
4737   int i;
4738   int group_nr = AmoebaNeighbourNr(x, y);
4739
4740   if (group_nr == 0)
4741   {
4742     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4743     {
4744       if (AmoebaCnt[i] == 0)
4745       {
4746         group_nr = i;
4747         break;
4748       }
4749     }
4750   }
4751
4752   AmoebaNr[x][y] = group_nr;
4753   AmoebaCnt[group_nr]++;
4754   AmoebaCnt2[group_nr]++;
4755 }
4756
4757 static void LevelSolved_SetFinalGameValues(void)
4758 {
4759   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4760   game.score_time_final = (level.use_step_counter ? TimePlayed :
4761                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4762
4763   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4764                       game_em.lev->score :
4765                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4766                       game_mm.score :
4767                       game.score);
4768
4769   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4770                        MM_HEALTH(game_mm.laser_overload_value) :
4771                        game.health);
4772
4773   game.LevelSolved_CountingTime = game.time_final;
4774   game.LevelSolved_CountingScore = game.score_final;
4775   game.LevelSolved_CountingHealth = game.health_final;
4776 }
4777
4778 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4779 {
4780   game.LevelSolved_CountingTime = time;
4781   game.LevelSolved_CountingScore = score;
4782   game.LevelSolved_CountingHealth = health;
4783
4784   game_panel_controls[GAME_PANEL_TIME].value = time;
4785   game_panel_controls[GAME_PANEL_SCORE].value = score;
4786   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4787
4788   DisplayGameControlValues();
4789 }
4790
4791 static void LevelSolved(void)
4792 {
4793   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4794       game.players_still_needed > 0)
4795     return;
4796
4797   game.LevelSolved = TRUE;
4798   game.GameOver = TRUE;
4799
4800   tape.solved = TRUE;
4801
4802   // needed here to display correct panel values while player walks into exit
4803   LevelSolved_SetFinalGameValues();
4804 }
4805
4806 void GameWon(void)
4807 {
4808   static int time_count_steps;
4809   static int time, time_final;
4810   static float score, score_final; // needed for time score < 10 for 10 seconds
4811   static int health, health_final;
4812   static int game_over_delay_1 = 0;
4813   static int game_over_delay_2 = 0;
4814   static int game_over_delay_3 = 0;
4815   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4816   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4817
4818   if (!game.LevelSolved_GameWon)
4819   {
4820     int i;
4821
4822     // do not start end game actions before the player stops moving (to exit)
4823     if (local_player->active && local_player->MovPos)
4824       return;
4825
4826     // calculate final game values after player finished walking into exit
4827     LevelSolved_SetFinalGameValues();
4828
4829     game.LevelSolved_GameWon = TRUE;
4830     game.LevelSolved_SaveTape = tape.recording;
4831     game.LevelSolved_SaveScore = !tape.playing;
4832
4833     if (!tape.playing)
4834     {
4835       LevelStats_incSolved(level_nr);
4836
4837       SaveLevelSetup_SeriesInfo();
4838     }
4839
4840     if (tape.auto_play)         // tape might already be stopped here
4841       tape.auto_play_level_solved = TRUE;
4842
4843     TapeStop();
4844
4845     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4846     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4847     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4848
4849     time = time_final = game.time_final;
4850     score = score_final = game.score_final;
4851     health = health_final = game.health_final;
4852
4853     // update game panel values before (delayed) counting of score (if any)
4854     LevelSolved_DisplayFinalGameValues(time, score, health);
4855
4856     // if level has time score defined, calculate new final game values
4857     if (time_score > 0)
4858     {
4859       int time_final_max = 999;
4860       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4861       int time_frames = 0;
4862       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4863       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4864
4865       if (TimeLeft > 0)
4866       {
4867         time_final = 0;
4868         time_frames = time_frames_left;
4869       }
4870       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4871       {
4872         time_final = time_final_max;
4873         time_frames = time_frames_final_max - time_frames_played;
4874       }
4875
4876       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4877
4878       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4879
4880       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4881       {
4882         health_final = 0;
4883         score_final += health * time_score;
4884       }
4885
4886       game.score_final = score_final;
4887       game.health_final = health_final;
4888     }
4889
4890     // if not counting score after game, immediately update game panel values
4891     if (level_editor_test_game || !setup.count_score_after_game)
4892     {
4893       time = time_final;
4894       score = score_final;
4895
4896       LevelSolved_DisplayFinalGameValues(time, score, health);
4897     }
4898
4899     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4900     {
4901       // check if last player has left the level
4902       if (game.exit_x >= 0 &&
4903           game.exit_y >= 0)
4904       {
4905         int x = game.exit_x;
4906         int y = game.exit_y;
4907         int element = Tile[x][y];
4908
4909         // close exit door after last player
4910         if ((game.all_players_gone &&
4911              (element == EL_EXIT_OPEN ||
4912               element == EL_SP_EXIT_OPEN ||
4913               element == EL_STEEL_EXIT_OPEN)) ||
4914             element == EL_EM_EXIT_OPEN ||
4915             element == EL_EM_STEEL_EXIT_OPEN)
4916         {
4917
4918           Tile[x][y] =
4919             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4920              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4921              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4922              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4923              EL_EM_STEEL_EXIT_CLOSING);
4924
4925           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4926         }
4927
4928         // player disappears
4929         DrawLevelField(x, y);
4930       }
4931
4932       for (i = 0; i < MAX_PLAYERS; i++)
4933       {
4934         struct PlayerInfo *player = &stored_player[i];
4935
4936         if (player->present)
4937         {
4938           RemovePlayer(player);
4939
4940           // player disappears
4941           DrawLevelField(player->jx, player->jy);
4942         }
4943       }
4944     }
4945
4946     PlaySound(SND_GAME_WINNING);
4947   }
4948
4949   if (setup.count_score_after_game)
4950   {
4951     if (time != time_final)
4952     {
4953       if (game_over_delay_1 > 0)
4954       {
4955         game_over_delay_1--;
4956
4957         return;
4958       }
4959
4960       int time_to_go = ABS(time_final - time);
4961       int time_count_dir = (time < time_final ? +1 : -1);
4962
4963       if (time_to_go < time_count_steps)
4964         time_count_steps = 1;
4965
4966       time  += time_count_steps * time_count_dir;
4967       score += time_count_steps * time_score;
4968
4969       // set final score to correct rounding differences after counting score
4970       if (time == time_final)
4971         score = score_final;
4972
4973       LevelSolved_DisplayFinalGameValues(time, score, health);
4974
4975       if (time == time_final)
4976         StopSound(SND_GAME_LEVELTIME_BONUS);
4977       else if (setup.sound_loops)
4978         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4979       else
4980         PlaySound(SND_GAME_LEVELTIME_BONUS);
4981
4982       return;
4983     }
4984
4985     if (health != health_final)
4986     {
4987       if (game_over_delay_2 > 0)
4988       {
4989         game_over_delay_2--;
4990
4991         return;
4992       }
4993
4994       int health_count_dir = (health < health_final ? +1 : -1);
4995
4996       health += health_count_dir;
4997       score  += time_score;
4998
4999       LevelSolved_DisplayFinalGameValues(time, score, health);
5000
5001       if (health == health_final)
5002         StopSound(SND_GAME_LEVELTIME_BONUS);
5003       else if (setup.sound_loops)
5004         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5005       else
5006         PlaySound(SND_GAME_LEVELTIME_BONUS);
5007
5008       return;
5009     }
5010   }
5011
5012   game.panel.active = FALSE;
5013
5014   if (game_over_delay_3 > 0)
5015   {
5016     game_over_delay_3--;
5017
5018     return;
5019   }
5020
5021   GameEnd();
5022 }
5023
5024 void GameEnd(void)
5025 {
5026   // used instead of "level_nr" (needed for network games)
5027   int last_level_nr = levelset.level_nr;
5028   boolean tape_saved = FALSE;
5029
5030   game.LevelSolved_GameEnd = TRUE;
5031
5032   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5033   {
5034     // make sure that request dialog to save tape does not open door again
5035     if (!global.use_envelope_request)
5036       CloseDoor(DOOR_CLOSE_1);
5037
5038     // ask to save tape
5039     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5040
5041     // set unique basename for score tape (also saved in high score table)
5042     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5043   }
5044
5045   // if no tape is to be saved, close both doors simultaneously
5046   CloseDoor(DOOR_CLOSE_ALL);
5047
5048   if (level_editor_test_game || score_info_tape_play)
5049   {
5050     SetGameStatus(GAME_MODE_MAIN);
5051
5052     DrawMainMenu();
5053
5054     return;
5055   }
5056
5057   if (!game.LevelSolved_SaveScore)
5058   {
5059     SetGameStatus(GAME_MODE_MAIN);
5060
5061     DrawMainMenu();
5062
5063     return;
5064   }
5065
5066   if (level_nr == leveldir_current->handicap_level)
5067   {
5068     leveldir_current->handicap_level++;
5069
5070     SaveLevelSetup_SeriesInfo();
5071   }
5072
5073   // save score and score tape before potentially erasing tape below
5074   NewHighScore(last_level_nr, tape_saved);
5075
5076   if (setup.increment_levels &&
5077       level_nr < leveldir_current->last_level &&
5078       !network_playing)
5079   {
5080     level_nr++;         // advance to next level
5081     TapeErase();        // start with empty tape
5082
5083     if (setup.auto_play_next_level)
5084     {
5085       scores.continue_playing = TRUE;
5086       scores.next_level_nr = level_nr;
5087
5088       LoadLevel(level_nr);
5089
5090       SaveLevelSetup_SeriesInfo();
5091     }
5092   }
5093
5094   if (scores.last_added >= 0 && setup.show_scores_after_game)
5095   {
5096     SetGameStatus(GAME_MODE_SCORES);
5097
5098     DrawHallOfFame(last_level_nr);
5099   }
5100   else if (scores.continue_playing)
5101   {
5102     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5103   }
5104   else
5105   {
5106     SetGameStatus(GAME_MODE_MAIN);
5107
5108     DrawMainMenu();
5109   }
5110 }
5111
5112 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5113                          boolean one_score_entry_per_name)
5114 {
5115   int i;
5116
5117   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5118     return -1;
5119
5120   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5121   {
5122     struct ScoreEntry *entry = &list->entry[i];
5123     boolean score_is_better = (new_entry->score >  entry->score);
5124     boolean score_is_equal  = (new_entry->score == entry->score);
5125     boolean time_is_better  = (new_entry->time  <  entry->time);
5126     boolean time_is_equal   = (new_entry->time  == entry->time);
5127     boolean better_by_score = (score_is_better ||
5128                                (score_is_equal && time_is_better));
5129     boolean better_by_time  = (time_is_better ||
5130                                (time_is_equal && score_is_better));
5131     boolean is_better = (level.rate_time_over_score ? better_by_time :
5132                          better_by_score);
5133     boolean entry_is_empty = (entry->score == 0 &&
5134                               entry->time == 0);
5135
5136     // prevent adding server score entries if also existing in local score file
5137     // (special case: historic score entries have an empty tape basename entry)
5138     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5139         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5140     {
5141       // add fields from server score entry not stored in local score entry
5142       // (currently, this means setting platform, version and country fields;
5143       // in rare cases, this may also correct an invalid score value, as
5144       // historic scores might have been truncated to 16-bit values locally)
5145       *entry = *new_entry;
5146
5147       return -1;
5148     }
5149
5150     if (is_better || entry_is_empty)
5151     {
5152       // player has made it to the hall of fame
5153
5154       if (i < MAX_SCORE_ENTRIES - 1)
5155       {
5156         int m = MAX_SCORE_ENTRIES - 1;
5157         int l;
5158
5159         if (one_score_entry_per_name)
5160         {
5161           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5162             if (strEqual(list->entry[l].name, new_entry->name))
5163               m = l;
5164
5165           if (m == i)   // player's new highscore overwrites his old one
5166             goto put_into_list;
5167         }
5168
5169         for (l = m; l > i; l--)
5170           list->entry[l] = list->entry[l - 1];
5171       }
5172
5173       put_into_list:
5174
5175       *entry = *new_entry;
5176
5177       return i;
5178     }
5179     else if (one_score_entry_per_name &&
5180              strEqual(entry->name, new_entry->name))
5181     {
5182       // player already in high score list with better score or time
5183
5184       return -1;
5185     }
5186   }
5187
5188   // special case: new score is beyond the last high score list position
5189   return MAX_SCORE_ENTRIES;
5190 }
5191
5192 void NewHighScore(int level_nr, boolean tape_saved)
5193 {
5194   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5195   boolean one_per_name = FALSE;
5196
5197   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5198   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5199
5200   new_entry.score = game.score_final;
5201   new_entry.time = game.score_time_final;
5202
5203   LoadScore(level_nr);
5204
5205   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5206
5207   if (scores.last_added >= MAX_SCORE_ENTRIES)
5208   {
5209     scores.last_added = MAX_SCORE_ENTRIES - 1;
5210     scores.force_last_added = TRUE;
5211
5212     scores.entry[scores.last_added] = new_entry;
5213
5214     // store last added local score entry (before merging server scores)
5215     scores.last_added_local = scores.last_added;
5216
5217     return;
5218   }
5219
5220   if (scores.last_added < 0)
5221     return;
5222
5223   SaveScore(level_nr);
5224
5225   // store last added local score entry (before merging server scores)
5226   scores.last_added_local = scores.last_added;
5227
5228   if (!game.LevelSolved_SaveTape)
5229     return;
5230
5231   SaveScoreTape(level_nr);
5232
5233   if (setup.ask_for_using_api_server)
5234   {
5235     setup.use_api_server =
5236       Request("Upload your score and tape to the high score server?", REQ_ASK);
5237
5238     if (!setup.use_api_server)
5239       Request("Not using high score server! Use setup menu to enable again!",
5240               REQ_CONFIRM);
5241
5242     runtime.use_api_server = setup.use_api_server;
5243
5244     // after asking for using API server once, do not ask again
5245     setup.ask_for_using_api_server = FALSE;
5246
5247     SaveSetup_ServerSetup();
5248   }
5249
5250   SaveServerScore(level_nr, tape_saved);
5251 }
5252
5253 void MergeServerScore(void)
5254 {
5255   struct ScoreEntry last_added_entry;
5256   boolean one_per_name = FALSE;
5257   int i;
5258
5259   if (scores.last_added >= 0)
5260     last_added_entry = scores.entry[scores.last_added];
5261
5262   for (i = 0; i < server_scores.num_entries; i++)
5263   {
5264     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5265
5266     if (pos >= 0 && pos <= scores.last_added)
5267       scores.last_added++;
5268   }
5269
5270   if (scores.last_added >= MAX_SCORE_ENTRIES)
5271   {
5272     scores.last_added = MAX_SCORE_ENTRIES - 1;
5273     scores.force_last_added = TRUE;
5274
5275     scores.entry[scores.last_added] = last_added_entry;
5276   }
5277 }
5278
5279 static int getElementMoveStepsizeExt(int x, int y, int direction)
5280 {
5281   int element = Tile[x][y];
5282   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5283   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5284   int horiz_move = (dx != 0);
5285   int sign = (horiz_move ? dx : dy);
5286   int step = sign * element_info[element].move_stepsize;
5287
5288   // special values for move stepsize for spring and things on conveyor belt
5289   if (horiz_move)
5290   {
5291     if (CAN_FALL(element) &&
5292         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5293       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5294     else if (element == EL_SPRING)
5295       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5296   }
5297
5298   return step;
5299 }
5300
5301 static int getElementMoveStepsize(int x, int y)
5302 {
5303   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5304 }
5305
5306 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5307 {
5308   if (player->GfxAction != action || player->GfxDir != dir)
5309   {
5310     player->GfxAction = action;
5311     player->GfxDir = dir;
5312     player->Frame = 0;
5313     player->StepFrame = 0;
5314   }
5315 }
5316
5317 static void ResetGfxFrame(int x, int y)
5318 {
5319   // profiling showed that "autotest" spends 10~20% of its time in this function
5320   if (DrawingDeactivatedField())
5321     return;
5322
5323   int element = Tile[x][y];
5324   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5325
5326   if (graphic_info[graphic].anim_global_sync)
5327     GfxFrame[x][y] = FrameCounter;
5328   else if (graphic_info[graphic].anim_global_anim_sync)
5329     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5330   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5331     GfxFrame[x][y] = CustomValue[x][y];
5332   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5333     GfxFrame[x][y] = element_info[element].collect_score;
5334   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5335     GfxFrame[x][y] = ChangeDelay[x][y];
5336 }
5337
5338 static void ResetGfxAnimation(int x, int y)
5339 {
5340   GfxAction[x][y] = ACTION_DEFAULT;
5341   GfxDir[x][y] = MovDir[x][y];
5342   GfxFrame[x][y] = 0;
5343
5344   ResetGfxFrame(x, y);
5345 }
5346
5347 static void ResetRandomAnimationValue(int x, int y)
5348 {
5349   GfxRandom[x][y] = INIT_GFX_RANDOM();
5350 }
5351
5352 static void InitMovingField(int x, int y, int direction)
5353 {
5354   int element = Tile[x][y];
5355   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5356   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5357   int newx = x + dx;
5358   int newy = y + dy;
5359   boolean is_moving_before, is_moving_after;
5360
5361   // check if element was/is moving or being moved before/after mode change
5362   is_moving_before = (WasJustMoving[x][y] != 0);
5363   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5364
5365   // reset animation only for moving elements which change direction of moving
5366   // or which just started or stopped moving
5367   // (else CEs with property "can move" / "not moving" are reset each frame)
5368   if (is_moving_before != is_moving_after ||
5369       direction != MovDir[x][y])
5370     ResetGfxAnimation(x, y);
5371
5372   MovDir[x][y] = direction;
5373   GfxDir[x][y] = direction;
5374
5375   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5376                      direction == MV_DOWN && CAN_FALL(element) ?
5377                      ACTION_FALLING : ACTION_MOVING);
5378
5379   // this is needed for CEs with property "can move" / "not moving"
5380
5381   if (is_moving_after)
5382   {
5383     if (Tile[newx][newy] == EL_EMPTY)
5384       Tile[newx][newy] = EL_BLOCKED;
5385
5386     MovDir[newx][newy] = MovDir[x][y];
5387
5388     CustomValue[newx][newy] = CustomValue[x][y];
5389
5390     GfxFrame[newx][newy] = GfxFrame[x][y];
5391     GfxRandom[newx][newy] = GfxRandom[x][y];
5392     GfxAction[newx][newy] = GfxAction[x][y];
5393     GfxDir[newx][newy] = GfxDir[x][y];
5394   }
5395 }
5396
5397 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5398 {
5399   int direction = MovDir[x][y];
5400   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5401   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5402
5403   *goes_to_x = newx;
5404   *goes_to_y = newy;
5405 }
5406
5407 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5408 {
5409   int oldx = x, oldy = y;
5410   int direction = MovDir[x][y];
5411
5412   if (direction == MV_LEFT)
5413     oldx++;
5414   else if (direction == MV_RIGHT)
5415     oldx--;
5416   else if (direction == MV_UP)
5417     oldy++;
5418   else if (direction == MV_DOWN)
5419     oldy--;
5420
5421   *comes_from_x = oldx;
5422   *comes_from_y = oldy;
5423 }
5424
5425 static int MovingOrBlocked2Element(int x, int y)
5426 {
5427   int element = Tile[x][y];
5428
5429   if (element == EL_BLOCKED)
5430   {
5431     int oldx, oldy;
5432
5433     Blocked2Moving(x, y, &oldx, &oldy);
5434     return Tile[oldx][oldy];
5435   }
5436   else
5437     return element;
5438 }
5439
5440 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5441 {
5442   // like MovingOrBlocked2Element(), but if element is moving
5443   // and (x,y) is the field the moving element is just leaving,
5444   // return EL_BLOCKED instead of the element value
5445   int element = Tile[x][y];
5446
5447   if (IS_MOVING(x, y))
5448   {
5449     if (element == EL_BLOCKED)
5450     {
5451       int oldx, oldy;
5452
5453       Blocked2Moving(x, y, &oldx, &oldy);
5454       return Tile[oldx][oldy];
5455     }
5456     else
5457       return EL_BLOCKED;
5458   }
5459   else
5460     return element;
5461 }
5462
5463 static void RemoveField(int x, int y)
5464 {
5465   Tile[x][y] = EL_EMPTY;
5466
5467   MovPos[x][y] = 0;
5468   MovDir[x][y] = 0;
5469   MovDelay[x][y] = 0;
5470
5471   CustomValue[x][y] = 0;
5472
5473   AmoebaNr[x][y] = 0;
5474   ChangeDelay[x][y] = 0;
5475   ChangePage[x][y] = -1;
5476   Pushed[x][y] = FALSE;
5477
5478   GfxElement[x][y] = EL_UNDEFINED;
5479   GfxAction[x][y] = ACTION_DEFAULT;
5480   GfxDir[x][y] = MV_NONE;
5481 }
5482
5483 static void RemoveMovingField(int x, int y)
5484 {
5485   int oldx = x, oldy = y, newx = x, newy = y;
5486   int element = Tile[x][y];
5487   int next_element = EL_UNDEFINED;
5488
5489   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5490     return;
5491
5492   if (IS_MOVING(x, y))
5493   {
5494     Moving2Blocked(x, y, &newx, &newy);
5495
5496     if (Tile[newx][newy] != EL_BLOCKED)
5497     {
5498       // element is moving, but target field is not free (blocked), but
5499       // already occupied by something different (example: acid pool);
5500       // in this case, only remove the moving field, but not the target
5501
5502       RemoveField(oldx, oldy);
5503
5504       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5505
5506       TEST_DrawLevelField(oldx, oldy);
5507
5508       return;
5509     }
5510   }
5511   else if (element == EL_BLOCKED)
5512   {
5513     Blocked2Moving(x, y, &oldx, &oldy);
5514     if (!IS_MOVING(oldx, oldy))
5515       return;
5516   }
5517
5518   if (element == EL_BLOCKED &&
5519       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5520        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5521        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5522        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5523        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5524        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5525     next_element = get_next_element(Tile[oldx][oldy]);
5526
5527   RemoveField(oldx, oldy);
5528   RemoveField(newx, newy);
5529
5530   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5531
5532   if (next_element != EL_UNDEFINED)
5533     Tile[oldx][oldy] = next_element;
5534
5535   TEST_DrawLevelField(oldx, oldy);
5536   TEST_DrawLevelField(newx, newy);
5537 }
5538
5539 void DrawDynamite(int x, int y)
5540 {
5541   int sx = SCREENX(x), sy = SCREENY(y);
5542   int graphic = el2img(Tile[x][y]);
5543   int frame;
5544
5545   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5546     return;
5547
5548   if (IS_WALKABLE_INSIDE(Back[x][y]))
5549     return;
5550
5551   if (Back[x][y])
5552     DrawLevelElement(x, y, Back[x][y]);
5553   else if (Store[x][y])
5554     DrawLevelElement(x, y, Store[x][y]);
5555   else if (game.use_masked_elements)
5556     DrawLevelElement(x, y, EL_EMPTY);
5557
5558   frame = getGraphicAnimationFrameXY(graphic, x, y);
5559
5560   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5561     DrawGraphicThruMask(sx, sy, graphic, frame);
5562   else
5563     DrawGraphic(sx, sy, graphic, frame);
5564 }
5565
5566 static void CheckDynamite(int x, int y)
5567 {
5568   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5569   {
5570     MovDelay[x][y]--;
5571
5572     if (MovDelay[x][y] != 0)
5573     {
5574       DrawDynamite(x, y);
5575       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5576
5577       return;
5578     }
5579   }
5580
5581   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5582
5583   Bang(x, y);
5584 }
5585
5586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5587 {
5588   boolean num_checked_players = 0;
5589   int i;
5590
5591   for (i = 0; i < MAX_PLAYERS; i++)
5592   {
5593     if (stored_player[i].active)
5594     {
5595       int sx = stored_player[i].jx;
5596       int sy = stored_player[i].jy;
5597
5598       if (num_checked_players == 0)
5599       {
5600         *sx1 = *sx2 = sx;
5601         *sy1 = *sy2 = sy;
5602       }
5603       else
5604       {
5605         *sx1 = MIN(*sx1, sx);
5606         *sy1 = MIN(*sy1, sy);
5607         *sx2 = MAX(*sx2, sx);
5608         *sy2 = MAX(*sy2, sy);
5609       }
5610
5611       num_checked_players++;
5612     }
5613   }
5614 }
5615
5616 static boolean checkIfAllPlayersFitToScreen_RND(void)
5617 {
5618   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5619
5620   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5621
5622   return (sx2 - sx1 < SCR_FIELDX &&
5623           sy2 - sy1 < SCR_FIELDY);
5624 }
5625
5626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5627 {
5628   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5629
5630   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5631
5632   *sx = (sx1 + sx2) / 2;
5633   *sy = (sy1 + sy2) / 2;
5634 }
5635
5636 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5637                                boolean center_screen, boolean quick_relocation)
5638 {
5639   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5640   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5641   boolean no_delay = (tape.warp_forward);
5642   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5643   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5644   int new_scroll_x, new_scroll_y;
5645
5646   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5647   {
5648     // case 1: quick relocation inside visible screen (without scrolling)
5649
5650     RedrawPlayfield();
5651
5652     return;
5653   }
5654
5655   if (!level.shifted_relocation || center_screen)
5656   {
5657     // relocation _with_ centering of screen
5658
5659     new_scroll_x = SCROLL_POSITION_X(x);
5660     new_scroll_y = SCROLL_POSITION_Y(y);
5661   }
5662   else
5663   {
5664     // relocation _without_ centering of screen
5665
5666     int center_scroll_x = SCROLL_POSITION_X(old_x);
5667     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5668     int offset_x = x + (scroll_x - center_scroll_x);
5669     int offset_y = y + (scroll_y - center_scroll_y);
5670
5671     // for new screen position, apply previous offset to center position
5672     new_scroll_x = SCROLL_POSITION_X(offset_x);
5673     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5674   }
5675
5676   if (quick_relocation)
5677   {
5678     // case 2: quick relocation (redraw without visible scrolling)
5679
5680     scroll_x = new_scroll_x;
5681     scroll_y = new_scroll_y;
5682
5683     RedrawPlayfield();
5684
5685     return;
5686   }
5687
5688   // case 3: visible relocation (with scrolling to new position)
5689
5690   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5691
5692   SetVideoFrameDelay(wait_delay_value);
5693
5694   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5695   {
5696     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5697     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5698
5699     if (dx == 0 && dy == 0)             // no scrolling needed at all
5700       break;
5701
5702     scroll_x -= dx;
5703     scroll_y -= dy;
5704
5705     // set values for horizontal/vertical screen scrolling (half tile size)
5706     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5707     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5708     int pos_x = dx * TILEX / 2;
5709     int pos_y = dy * TILEY / 2;
5710     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5711     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5712
5713     ScrollLevel(dx, dy);
5714     DrawAllPlayers();
5715
5716     // scroll in two steps of half tile size to make things smoother
5717     BlitScreenToBitmapExt_RND(window, fx, fy);
5718
5719     // scroll second step to align at full tile size
5720     BlitScreenToBitmap(window);
5721   }
5722
5723   DrawAllPlayers();
5724   BackToFront();
5725
5726   SetVideoFrameDelay(frame_delay_value_old);
5727 }
5728
5729 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5730 {
5731   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5732   int player_nr = GET_PLAYER_NR(el_player);
5733   struct PlayerInfo *player = &stored_player[player_nr];
5734   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5735   boolean no_delay = (tape.warp_forward);
5736   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5737   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5738   int old_jx = player->jx;
5739   int old_jy = player->jy;
5740   int old_element = Tile[old_jx][old_jy];
5741   int element = Tile[jx][jy];
5742   boolean player_relocated = (old_jx != jx || old_jy != jy);
5743
5744   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5745   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5746   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5747   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5748   int leave_side_horiz = move_dir_horiz;
5749   int leave_side_vert  = move_dir_vert;
5750   int enter_side = enter_side_horiz | enter_side_vert;
5751   int leave_side = leave_side_horiz | leave_side_vert;
5752
5753   if (player->buried)           // do not reanimate dead player
5754     return;
5755
5756   if (!player_relocated)        // no need to relocate the player
5757     return;
5758
5759   if (IS_PLAYER(jx, jy))        // player already placed at new position
5760   {
5761     RemoveField(jx, jy);        // temporarily remove newly placed player
5762     DrawLevelField(jx, jy);
5763   }
5764
5765   if (player->present)
5766   {
5767     while (player->MovPos)
5768     {
5769       ScrollPlayer(player, SCROLL_GO_ON);
5770       ScrollScreen(NULL, SCROLL_GO_ON);
5771
5772       AdvanceFrameAndPlayerCounters(player->index_nr);
5773
5774       DrawPlayer(player);
5775
5776       BackToFront_WithFrameDelay(wait_delay_value);
5777     }
5778
5779     DrawPlayer(player);         // needed here only to cleanup last field
5780     DrawLevelField(player->jx, player->jy);     // remove player graphic
5781
5782     player->is_moving = FALSE;
5783   }
5784
5785   if (IS_CUSTOM_ELEMENT(old_element))
5786     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5787                                CE_LEFT_BY_PLAYER,
5788                                player->index_bit, leave_side);
5789
5790   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5791                                       CE_PLAYER_LEAVES_X,
5792                                       player->index_bit, leave_side);
5793
5794   Tile[jx][jy] = el_player;
5795   InitPlayerField(jx, jy, el_player, TRUE);
5796
5797   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5798      possible that the relocation target field did not contain a player element,
5799      but a walkable element, to which the new player was relocated -- in this
5800      case, restore that (already initialized!) element on the player field */
5801   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5802   {
5803     Tile[jx][jy] = element;     // restore previously existing element
5804   }
5805
5806   // only visually relocate centered player
5807   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5808                      FALSE, level.instant_relocation);
5809
5810   TestIfPlayerTouchesBadThing(jx, jy);
5811   TestIfPlayerTouchesCustomElement(jx, jy);
5812
5813   if (IS_CUSTOM_ELEMENT(element))
5814     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5815                                player->index_bit, enter_side);
5816
5817   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5818                                       player->index_bit, enter_side);
5819
5820   if (player->is_switching)
5821   {
5822     /* ensure that relocation while still switching an element does not cause
5823        a new element to be treated as also switched directly after relocation
5824        (this is important for teleporter switches that teleport the player to
5825        a place where another teleporter switch is in the same direction, which
5826        would then incorrectly be treated as immediately switched before the
5827        direction key that caused the switch was released) */
5828
5829     player->switch_x += jx - old_jx;
5830     player->switch_y += jy - old_jy;
5831   }
5832 }
5833
5834 static void Explode(int ex, int ey, int phase, int mode)
5835 {
5836   int x, y;
5837   int last_phase;
5838   int border_element;
5839
5840   // !!! eliminate this variable !!!
5841   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5842
5843   if (game.explosions_delayed)
5844   {
5845     ExplodeField[ex][ey] = mode;
5846     return;
5847   }
5848
5849   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5850   {
5851     int center_element = Tile[ex][ey];
5852     int artwork_element, explosion_element;     // set these values later
5853
5854     // remove things displayed in background while burning dynamite
5855     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5856       Back[ex][ey] = 0;
5857
5858     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5859     {
5860       // put moving element to center field (and let it explode there)
5861       center_element = MovingOrBlocked2Element(ex, ey);
5862       RemoveMovingField(ex, ey);
5863       Tile[ex][ey] = center_element;
5864     }
5865
5866     // now "center_element" is finally determined -- set related values now
5867     artwork_element = center_element;           // for custom player artwork
5868     explosion_element = center_element;         // for custom player artwork
5869
5870     if (IS_PLAYER(ex, ey))
5871     {
5872       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5873
5874       artwork_element = stored_player[player_nr].artwork_element;
5875
5876       if (level.use_explosion_element[player_nr])
5877       {
5878         explosion_element = level.explosion_element[player_nr];
5879         artwork_element = explosion_element;
5880       }
5881     }
5882
5883     if (mode == EX_TYPE_NORMAL ||
5884         mode == EX_TYPE_CENTER ||
5885         mode == EX_TYPE_CROSS)
5886       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5887
5888     last_phase = element_info[explosion_element].explosion_delay + 1;
5889
5890     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5891     {
5892       int xx = x - ex + 1;
5893       int yy = y - ey + 1;
5894       int element;
5895
5896       if (!IN_LEV_FIELD(x, y) ||
5897           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5898           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5899         continue;
5900
5901       element = Tile[x][y];
5902
5903       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5904       {
5905         element = MovingOrBlocked2Element(x, y);
5906
5907         if (!IS_EXPLOSION_PROOF(element))
5908           RemoveMovingField(x, y);
5909       }
5910
5911       // indestructible elements can only explode in center (but not flames)
5912       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5913                                            mode == EX_TYPE_BORDER)) ||
5914           element == EL_FLAMES)
5915         continue;
5916
5917       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5918          behaviour, for example when touching a yamyam that explodes to rocks
5919          with active deadly shield, a rock is created under the player !!! */
5920       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5921 #if 0
5922       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5923           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5924            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5925 #else
5926       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5927 #endif
5928       {
5929         if (IS_ACTIVE_BOMB(element))
5930         {
5931           // re-activate things under the bomb like gate or penguin
5932           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5933           Back[x][y] = 0;
5934         }
5935
5936         continue;
5937       }
5938
5939       // save walkable background elements while explosion on same tile
5940       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5941           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5942         Back[x][y] = element;
5943
5944       // ignite explodable elements reached by other explosion
5945       if (element == EL_EXPLOSION)
5946         element = Store2[x][y];
5947
5948       if (AmoebaNr[x][y] &&
5949           (element == EL_AMOEBA_FULL ||
5950            element == EL_BD_AMOEBA ||
5951            element == EL_AMOEBA_GROWING))
5952       {
5953         AmoebaCnt[AmoebaNr[x][y]]--;
5954         AmoebaCnt2[AmoebaNr[x][y]]--;
5955       }
5956
5957       RemoveField(x, y);
5958
5959       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5960       {
5961         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5962
5963         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5964
5965         if (PLAYERINFO(ex, ey)->use_murphy)
5966           Store[x][y] = EL_EMPTY;
5967       }
5968
5969       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5970       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5971       else if (IS_PLAYER_ELEMENT(center_element))
5972         Store[x][y] = EL_EMPTY;
5973       else if (center_element == EL_YAMYAM)
5974         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5975       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5976         Store[x][y] = element_info[center_element].content.e[xx][yy];
5977 #if 1
5978       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5979       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5980       // otherwise) -- FIX THIS !!!
5981       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5982         Store[x][y] = element_info[element].content.e[1][1];
5983 #else
5984       else if (!CAN_EXPLODE(element))
5985         Store[x][y] = element_info[element].content.e[1][1];
5986 #endif
5987       else
5988         Store[x][y] = EL_EMPTY;
5989
5990       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5991           center_element == EL_AMOEBA_TO_DIAMOND)
5992         Store2[x][y] = element;
5993
5994       Tile[x][y] = EL_EXPLOSION;
5995       GfxElement[x][y] = artwork_element;
5996
5997       ExplodePhase[x][y] = 1;
5998       ExplodeDelay[x][y] = last_phase;
5999
6000       Stop[x][y] = TRUE;
6001     }
6002
6003     if (center_element == EL_YAMYAM)
6004       game.yamyam_content_nr =
6005         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6006
6007     return;
6008   }
6009
6010   if (Stop[ex][ey])
6011     return;
6012
6013   x = ex;
6014   y = ey;
6015
6016   if (phase == 1)
6017     GfxFrame[x][y] = 0;         // restart explosion animation
6018
6019   last_phase = ExplodeDelay[x][y];
6020
6021   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6022
6023   // this can happen if the player leaves an explosion just in time
6024   if (GfxElement[x][y] == EL_UNDEFINED)
6025     GfxElement[x][y] = EL_EMPTY;
6026
6027   border_element = Store2[x][y];
6028   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6029     border_element = StorePlayer[x][y];
6030
6031   if (phase == element_info[border_element].ignition_delay ||
6032       phase == last_phase)
6033   {
6034     boolean border_explosion = FALSE;
6035
6036     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6037         !PLAYER_EXPLOSION_PROTECTED(x, y))
6038     {
6039       KillPlayerUnlessExplosionProtected(x, y);
6040       border_explosion = TRUE;
6041     }
6042     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6043     {
6044       Tile[x][y] = Store2[x][y];
6045       Store2[x][y] = 0;
6046       Bang(x, y);
6047       border_explosion = TRUE;
6048     }
6049     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6050     {
6051       AmoebaToDiamond(x, y);
6052       Store2[x][y] = 0;
6053       border_explosion = TRUE;
6054     }
6055
6056     // if an element just explodes due to another explosion (chain-reaction),
6057     // do not immediately end the new explosion when it was the last frame of
6058     // the explosion (as it would be done in the following "if"-statement!)
6059     if (border_explosion && phase == last_phase)
6060       return;
6061   }
6062
6063   // this can happen if the player was just killed by an explosion
6064   if (GfxElement[x][y] == EL_UNDEFINED)
6065     GfxElement[x][y] = EL_EMPTY;
6066
6067   if (phase == last_phase)
6068   {
6069     int element;
6070
6071     element = Tile[x][y] = Store[x][y];
6072     Store[x][y] = Store2[x][y] = 0;
6073     GfxElement[x][y] = EL_UNDEFINED;
6074
6075     // player can escape from explosions and might therefore be still alive
6076     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6077         element <= EL_PLAYER_IS_EXPLODING_4)
6078     {
6079       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6080       int explosion_element = EL_PLAYER_1 + player_nr;
6081       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6082       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6083
6084       if (level.use_explosion_element[player_nr])
6085         explosion_element = level.explosion_element[player_nr];
6086
6087       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6088                     element_info[explosion_element].content.e[xx][yy]);
6089     }
6090
6091     // restore probably existing indestructible background element
6092     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6093       element = Tile[x][y] = Back[x][y];
6094     Back[x][y] = 0;
6095
6096     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6097     GfxDir[x][y] = MV_NONE;
6098     ChangeDelay[x][y] = 0;
6099     ChangePage[x][y] = -1;
6100
6101     CustomValue[x][y] = 0;
6102
6103     InitField_WithBug2(x, y, FALSE);
6104
6105     TEST_DrawLevelField(x, y);
6106
6107     TestIfElementTouchesCustomElement(x, y);
6108
6109     if (GFX_CRUMBLED(element))
6110       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6111
6112     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6113       StorePlayer[x][y] = 0;
6114
6115     if (IS_PLAYER_ELEMENT(element))
6116       RelocatePlayer(x, y, element);
6117   }
6118   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6119   {
6120     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6121     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6122
6123     if (phase == delay)
6124       TEST_DrawLevelFieldCrumbled(x, y);
6125
6126     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6127     {
6128       DrawLevelElement(x, y, Back[x][y]);
6129       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6130     }
6131     else if (IS_WALKABLE_UNDER(Back[x][y]))
6132     {
6133       DrawLevelGraphic(x, y, graphic, frame);
6134       DrawLevelElementThruMask(x, y, Back[x][y]);
6135     }
6136     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6137       DrawLevelGraphic(x, y, graphic, frame);
6138   }
6139 }
6140
6141 static void DynaExplode(int ex, int ey)
6142 {
6143   int i, j;
6144   int dynabomb_element = Tile[ex][ey];
6145   int dynabomb_size = 1;
6146   boolean dynabomb_xl = FALSE;
6147   struct PlayerInfo *player;
6148   struct XY *xy = xy_topdown;
6149
6150   if (IS_ACTIVE_BOMB(dynabomb_element))
6151   {
6152     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6153     dynabomb_size = player->dynabomb_size;
6154     dynabomb_xl = player->dynabomb_xl;
6155     player->dynabombs_left++;
6156   }
6157
6158   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6159
6160   for (i = 0; i < NUM_DIRECTIONS; i++)
6161   {
6162     for (j = 1; j <= dynabomb_size; j++)
6163     {
6164       int x = ex + j * xy[i].x;
6165       int y = ey + j * xy[i].y;
6166       int element;
6167
6168       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6169         break;
6170
6171       element = Tile[x][y];
6172
6173       // do not restart explosions of fields with active bombs
6174       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6175         continue;
6176
6177       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6178
6179       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6180           !IS_DIGGABLE(element) && !dynabomb_xl)
6181         break;
6182     }
6183   }
6184 }
6185
6186 void Bang(int x, int y)
6187 {
6188   int element = MovingOrBlocked2Element(x, y);
6189   int explosion_type = EX_TYPE_NORMAL;
6190
6191   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6192   {
6193     struct PlayerInfo *player = PLAYERINFO(x, y);
6194
6195     element = Tile[x][y] = player->initial_element;
6196
6197     if (level.use_explosion_element[player->index_nr])
6198     {
6199       int explosion_element = level.explosion_element[player->index_nr];
6200
6201       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6202         explosion_type = EX_TYPE_CROSS;
6203       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6204         explosion_type = EX_TYPE_CENTER;
6205     }
6206   }
6207
6208   switch (element)
6209   {
6210     case EL_BUG:
6211     case EL_SPACESHIP:
6212     case EL_BD_BUTTERFLY:
6213     case EL_BD_FIREFLY:
6214     case EL_YAMYAM:
6215     case EL_DARK_YAMYAM:
6216     case EL_ROBOT:
6217     case EL_PACMAN:
6218     case EL_MOLE:
6219       RaiseScoreElement(element);
6220       break;
6221
6222     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6223     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6224     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6225     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6226     case EL_DYNABOMB_INCREASE_NUMBER:
6227     case EL_DYNABOMB_INCREASE_SIZE:
6228     case EL_DYNABOMB_INCREASE_POWER:
6229       explosion_type = EX_TYPE_DYNA;
6230       break;
6231
6232     case EL_DC_LANDMINE:
6233       explosion_type = EX_TYPE_CENTER;
6234       break;
6235
6236     case EL_PENGUIN:
6237     case EL_LAMP:
6238     case EL_LAMP_ACTIVE:
6239     case EL_AMOEBA_TO_DIAMOND:
6240       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6241         explosion_type = EX_TYPE_CENTER;
6242       break;
6243
6244     default:
6245       if (element_info[element].explosion_type == EXPLODES_CROSS)
6246         explosion_type = EX_TYPE_CROSS;
6247       else if (element_info[element].explosion_type == EXPLODES_1X1)
6248         explosion_type = EX_TYPE_CENTER;
6249       break;
6250   }
6251
6252   if (explosion_type == EX_TYPE_DYNA)
6253     DynaExplode(x, y);
6254   else
6255     Explode(x, y, EX_PHASE_START, explosion_type);
6256
6257   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6258 }
6259
6260 static void SplashAcid(int x, int y)
6261 {
6262   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6263       (!IN_LEV_FIELD(x - 1, y - 2) ||
6264        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6265     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6266
6267   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6268       (!IN_LEV_FIELD(x + 1, y - 2) ||
6269        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6270     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6271
6272   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6273 }
6274
6275 static void InitBeltMovement(void)
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
6292   int x, y, i, j;
6293
6294   // set frame order for belt animation graphic according to belt direction
6295   for (i = 0; i < NUM_BELTS; i++)
6296   {
6297     int belt_nr = i;
6298
6299     for (j = 0; j < NUM_BELT_PARTS; j++)
6300     {
6301       int element = belt_base_active_element[belt_nr] + j;
6302       int graphic_1 = el2img(element);
6303       int graphic_2 = el2panelimg(element);
6304
6305       if (game.belt_dir[i] == MV_LEFT)
6306       {
6307         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6308         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6309       }
6310       else
6311       {
6312         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6313         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6314       }
6315     }
6316   }
6317
6318   SCAN_PLAYFIELD(x, y)
6319   {
6320     int element = Tile[x][y];
6321
6322     for (i = 0; i < NUM_BELTS; i++)
6323     {
6324       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6325       {
6326         int e_belt_nr = getBeltNrFromBeltElement(element);
6327         int belt_nr = i;
6328
6329         if (e_belt_nr == belt_nr)
6330         {
6331           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6332
6333           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6334         }
6335       }
6336     }
6337   }
6338 }
6339
6340 static void ToggleBeltSwitch(int x, int y)
6341 {
6342   static int belt_base_element[4] =
6343   {
6344     EL_CONVEYOR_BELT_1_LEFT,
6345     EL_CONVEYOR_BELT_2_LEFT,
6346     EL_CONVEYOR_BELT_3_LEFT,
6347     EL_CONVEYOR_BELT_4_LEFT
6348   };
6349   static int belt_base_active_element[4] =
6350   {
6351     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6352     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6354     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6355   };
6356   static int belt_base_switch_element[4] =
6357   {
6358     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6359     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6360     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6361     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6362   };
6363   static int belt_move_dir[4] =
6364   {
6365     MV_LEFT,
6366     MV_NONE,
6367     MV_RIGHT,
6368     MV_NONE,
6369   };
6370
6371   int element = Tile[x][y];
6372   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6373   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6374   int belt_dir = belt_move_dir[belt_dir_nr];
6375   int xx, yy, i;
6376
6377   if (!IS_BELT_SWITCH(element))
6378     return;
6379
6380   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6381   game.belt_dir[belt_nr] = belt_dir;
6382
6383   if (belt_dir_nr == 3)
6384     belt_dir_nr = 1;
6385
6386   // set frame order for belt animation graphic according to belt direction
6387   for (i = 0; i < NUM_BELT_PARTS; i++)
6388   {
6389     int element = belt_base_active_element[belt_nr] + i;
6390     int graphic_1 = el2img(element);
6391     int graphic_2 = el2panelimg(element);
6392
6393     if (belt_dir == MV_LEFT)
6394     {
6395       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6396       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6397     }
6398     else
6399     {
6400       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6401       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6402     }
6403   }
6404
6405   SCAN_PLAYFIELD(xx, yy)
6406   {
6407     int element = Tile[xx][yy];
6408
6409     if (IS_BELT_SWITCH(element))
6410     {
6411       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6412
6413       if (e_belt_nr == belt_nr)
6414       {
6415         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6416         TEST_DrawLevelField(xx, yy);
6417       }
6418     }
6419     else if (IS_BELT(element) && belt_dir != MV_NONE)
6420     {
6421       int e_belt_nr = getBeltNrFromBeltElement(element);
6422
6423       if (e_belt_nr == belt_nr)
6424       {
6425         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6426
6427         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6428         TEST_DrawLevelField(xx, yy);
6429       }
6430     }
6431     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6432     {
6433       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6434
6435       if (e_belt_nr == belt_nr)
6436       {
6437         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6438
6439         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6440         TEST_DrawLevelField(xx, yy);
6441       }
6442     }
6443   }
6444 }
6445
6446 static void ToggleSwitchgateSwitch(void)
6447 {
6448   int xx, yy;
6449
6450   game.switchgate_pos = !game.switchgate_pos;
6451
6452   SCAN_PLAYFIELD(xx, yy)
6453   {
6454     int element = Tile[xx][yy];
6455
6456     if (element == EL_SWITCHGATE_SWITCH_UP)
6457     {
6458       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6459       TEST_DrawLevelField(xx, yy);
6460     }
6461     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6462     {
6463       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6464       TEST_DrawLevelField(xx, yy);
6465     }
6466     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6467     {
6468       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6469       TEST_DrawLevelField(xx, yy);
6470     }
6471     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6472     {
6473       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6474       TEST_DrawLevelField(xx, yy);
6475     }
6476     else if (element == EL_SWITCHGATE_OPEN ||
6477              element == EL_SWITCHGATE_OPENING)
6478     {
6479       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6480
6481       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6482     }
6483     else if (element == EL_SWITCHGATE_CLOSED ||
6484              element == EL_SWITCHGATE_CLOSING)
6485     {
6486       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6487
6488       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6489     }
6490   }
6491 }
6492
6493 static int getInvisibleActiveFromInvisibleElement(int element)
6494 {
6495   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6496           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6497           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6498           element);
6499 }
6500
6501 static int getInvisibleFromInvisibleActiveElement(int element)
6502 {
6503   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6504           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6505           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6506           element);
6507 }
6508
6509 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6510 {
6511   int x, y;
6512
6513   SCAN_PLAYFIELD(x, y)
6514   {
6515     int element = Tile[x][y];
6516
6517     if (element == EL_LIGHT_SWITCH &&
6518         game.light_time_left > 0)
6519     {
6520       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6521       TEST_DrawLevelField(x, y);
6522     }
6523     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6524              game.light_time_left == 0)
6525     {
6526       Tile[x][y] = EL_LIGHT_SWITCH;
6527       TEST_DrawLevelField(x, y);
6528     }
6529     else if (element == EL_EMC_DRIPPER &&
6530              game.light_time_left > 0)
6531     {
6532       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6533       TEST_DrawLevelField(x, y);
6534     }
6535     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6536              game.light_time_left == 0)
6537     {
6538       Tile[x][y] = EL_EMC_DRIPPER;
6539       TEST_DrawLevelField(x, y);
6540     }
6541     else if (element == EL_INVISIBLE_STEELWALL ||
6542              element == EL_INVISIBLE_WALL ||
6543              element == EL_INVISIBLE_SAND)
6544     {
6545       if (game.light_time_left > 0)
6546         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6547
6548       TEST_DrawLevelField(x, y);
6549
6550       // uncrumble neighbour fields, if needed
6551       if (element == EL_INVISIBLE_SAND)
6552         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6553     }
6554     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6555              element == EL_INVISIBLE_WALL_ACTIVE ||
6556              element == EL_INVISIBLE_SAND_ACTIVE)
6557     {
6558       if (game.light_time_left == 0)
6559         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6560
6561       TEST_DrawLevelField(x, y);
6562
6563       // re-crumble neighbour fields, if needed
6564       if (element == EL_INVISIBLE_SAND)
6565         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6566     }
6567   }
6568 }
6569
6570 static void RedrawAllInvisibleElementsForLenses(void)
6571 {
6572   int x, y;
6573
6574   SCAN_PLAYFIELD(x, y)
6575   {
6576     int element = Tile[x][y];
6577
6578     if (element == EL_EMC_DRIPPER &&
6579         game.lenses_time_left > 0)
6580     {
6581       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6582       TEST_DrawLevelField(x, y);
6583     }
6584     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6585              game.lenses_time_left == 0)
6586     {
6587       Tile[x][y] = EL_EMC_DRIPPER;
6588       TEST_DrawLevelField(x, y);
6589     }
6590     else if (element == EL_INVISIBLE_STEELWALL ||
6591              element == EL_INVISIBLE_WALL ||
6592              element == EL_INVISIBLE_SAND)
6593     {
6594       if (game.lenses_time_left > 0)
6595         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6596
6597       TEST_DrawLevelField(x, y);
6598
6599       // uncrumble neighbour fields, if needed
6600       if (element == EL_INVISIBLE_SAND)
6601         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6602     }
6603     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6604              element == EL_INVISIBLE_WALL_ACTIVE ||
6605              element == EL_INVISIBLE_SAND_ACTIVE)
6606     {
6607       if (game.lenses_time_left == 0)
6608         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6609
6610       TEST_DrawLevelField(x, y);
6611
6612       // re-crumble neighbour fields, if needed
6613       if (element == EL_INVISIBLE_SAND)
6614         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6615     }
6616   }
6617 }
6618
6619 static void RedrawAllInvisibleElementsForMagnifier(void)
6620 {
6621   int x, y;
6622
6623   SCAN_PLAYFIELD(x, y)
6624   {
6625     int element = Tile[x][y];
6626
6627     if (element == EL_EMC_FAKE_GRASS &&
6628         game.magnify_time_left > 0)
6629     {
6630       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6631       TEST_DrawLevelField(x, y);
6632     }
6633     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6634              game.magnify_time_left == 0)
6635     {
6636       Tile[x][y] = EL_EMC_FAKE_GRASS;
6637       TEST_DrawLevelField(x, y);
6638     }
6639     else if (IS_GATE_GRAY(element) &&
6640              game.magnify_time_left > 0)
6641     {
6642       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6643                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6644                     IS_EM_GATE_GRAY(element) ?
6645                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6646                     IS_EMC_GATE_GRAY(element) ?
6647                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6648                     IS_DC_GATE_GRAY(element) ?
6649                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6650                     element);
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (IS_GATE_GRAY_ACTIVE(element) &&
6654              game.magnify_time_left == 0)
6655     {
6656       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6657                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6658                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6659                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6660                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6661                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6662                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6663                     EL_DC_GATE_WHITE_GRAY :
6664                     element);
6665       TEST_DrawLevelField(x, y);
6666     }
6667   }
6668 }
6669
6670 static void ToggleLightSwitch(int x, int y)
6671 {
6672   int element = Tile[x][y];
6673
6674   game.light_time_left =
6675     (element == EL_LIGHT_SWITCH ?
6676      level.time_light * FRAMES_PER_SECOND : 0);
6677
6678   RedrawAllLightSwitchesAndInvisibleElements();
6679 }
6680
6681 static void ActivateTimegateSwitch(int x, int y)
6682 {
6683   int xx, yy;
6684
6685   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6686
6687   SCAN_PLAYFIELD(xx, yy)
6688   {
6689     int element = Tile[xx][yy];
6690
6691     if (element == EL_TIMEGATE_CLOSED ||
6692         element == EL_TIMEGATE_CLOSING)
6693     {
6694       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6695       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6696     }
6697
6698     /*
6699     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6700     {
6701       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6702       TEST_DrawLevelField(xx, yy);
6703     }
6704     */
6705
6706   }
6707
6708   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6709                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6710 }
6711
6712 static void Impact(int x, int y)
6713 {
6714   boolean last_line = (y == lev_fieldy - 1);
6715   boolean object_hit = FALSE;
6716   boolean impact = (last_line || object_hit);
6717   int element = Tile[x][y];
6718   int smashed = EL_STEELWALL;
6719
6720   if (!last_line)       // check if element below was hit
6721   {
6722     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6723       return;
6724
6725     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6726                                          MovDir[x][y + 1] != MV_DOWN ||
6727                                          MovPos[x][y + 1] <= TILEY / 2));
6728
6729     // do not smash moving elements that left the smashed field in time
6730     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6731         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6732       object_hit = FALSE;
6733
6734 #if USE_QUICKSAND_IMPACT_BUGFIX
6735     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6736     {
6737       RemoveMovingField(x, y + 1);
6738       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6739       Tile[x][y + 2] = EL_ROCK;
6740       TEST_DrawLevelField(x, y + 2);
6741
6742       object_hit = TRUE;
6743     }
6744
6745     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6746     {
6747       RemoveMovingField(x, y + 1);
6748       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6749       Tile[x][y + 2] = EL_ROCK;
6750       TEST_DrawLevelField(x, y + 2);
6751
6752       object_hit = TRUE;
6753     }
6754 #endif
6755
6756     if (object_hit)
6757       smashed = MovingOrBlocked2Element(x, y + 1);
6758
6759     impact = (last_line || object_hit);
6760   }
6761
6762   if (!last_line && smashed == EL_ACID) // element falls into acid
6763   {
6764     SplashAcid(x, y + 1);
6765     return;
6766   }
6767
6768   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6769   // only reset graphic animation if graphic really changes after impact
6770   if (impact &&
6771       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6772   {
6773     ResetGfxAnimation(x, y);
6774     TEST_DrawLevelField(x, y);
6775   }
6776
6777   if (impact && CAN_EXPLODE_IMPACT(element))
6778   {
6779     Bang(x, y);
6780     return;
6781   }
6782   else if (impact && element == EL_PEARL &&
6783            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6784   {
6785     ResetGfxAnimation(x, y);
6786
6787     Tile[x][y] = EL_PEARL_BREAKING;
6788     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6789     return;
6790   }
6791   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6792   {
6793     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6794
6795     return;
6796   }
6797
6798   if (impact && element == EL_AMOEBA_DROP)
6799   {
6800     if (object_hit && IS_PLAYER(x, y + 1))
6801       KillPlayerUnlessEnemyProtected(x, y + 1);
6802     else if (object_hit && smashed == EL_PENGUIN)
6803       Bang(x, y + 1);
6804     else
6805     {
6806       Tile[x][y] = EL_AMOEBA_GROWING;
6807       Store[x][y] = EL_AMOEBA_WET;
6808
6809       ResetRandomAnimationValue(x, y);
6810     }
6811     return;
6812   }
6813
6814   if (object_hit)               // check which object was hit
6815   {
6816     if ((CAN_PASS_MAGIC_WALL(element) && 
6817          (smashed == EL_MAGIC_WALL ||
6818           smashed == EL_BD_MAGIC_WALL)) ||
6819         (CAN_PASS_DC_MAGIC_WALL(element) &&
6820          smashed == EL_DC_MAGIC_WALL))
6821     {
6822       int xx, yy;
6823       int activated_magic_wall =
6824         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6825          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6826          EL_DC_MAGIC_WALL_ACTIVE);
6827
6828       // activate magic wall / mill
6829       SCAN_PLAYFIELD(xx, yy)
6830       {
6831         if (Tile[xx][yy] == smashed)
6832           Tile[xx][yy] = activated_magic_wall;
6833       }
6834
6835       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6836       game.magic_wall_active = TRUE;
6837
6838       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6839                             SND_MAGIC_WALL_ACTIVATING :
6840                             smashed == EL_BD_MAGIC_WALL ?
6841                             SND_BD_MAGIC_WALL_ACTIVATING :
6842                             SND_DC_MAGIC_WALL_ACTIVATING));
6843     }
6844
6845     if (IS_PLAYER(x, y + 1))
6846     {
6847       if (CAN_SMASH_PLAYER(element))
6848       {
6849         KillPlayerUnlessEnemyProtected(x, y + 1);
6850         return;
6851       }
6852     }
6853     else if (smashed == EL_PENGUIN)
6854     {
6855       if (CAN_SMASH_PLAYER(element))
6856       {
6857         Bang(x, y + 1);
6858         return;
6859       }
6860     }
6861     else if (element == EL_BD_DIAMOND)
6862     {
6863       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6864       {
6865         Bang(x, y + 1);
6866         return;
6867       }
6868     }
6869     else if (((element == EL_SP_INFOTRON ||
6870                element == EL_SP_ZONK) &&
6871               (smashed == EL_SP_SNIKSNAK ||
6872                smashed == EL_SP_ELECTRON ||
6873                smashed == EL_SP_DISK_ORANGE)) ||
6874              (element == EL_SP_INFOTRON &&
6875               smashed == EL_SP_DISK_YELLOW))
6876     {
6877       Bang(x, y + 1);
6878       return;
6879     }
6880     else if (CAN_SMASH_EVERYTHING(element))
6881     {
6882       if (IS_CLASSIC_ENEMY(smashed) ||
6883           CAN_EXPLODE_SMASHED(smashed))
6884       {
6885         Bang(x, y + 1);
6886         return;
6887       }
6888       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6889       {
6890         if (smashed == EL_LAMP ||
6891             smashed == EL_LAMP_ACTIVE)
6892         {
6893           Bang(x, y + 1);
6894           return;
6895         }
6896         else if (smashed == EL_NUT)
6897         {
6898           Tile[x][y + 1] = EL_NUT_BREAKING;
6899           PlayLevelSound(x, y, SND_NUT_BREAKING);
6900           RaiseScoreElement(EL_NUT);
6901           return;
6902         }
6903         else if (smashed == EL_PEARL)
6904         {
6905           ResetGfxAnimation(x, y);
6906
6907           Tile[x][y + 1] = EL_PEARL_BREAKING;
6908           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6909           return;
6910         }
6911         else if (smashed == EL_DIAMOND)
6912         {
6913           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6914           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6915           return;
6916         }
6917         else if (IS_BELT_SWITCH(smashed))
6918         {
6919           ToggleBeltSwitch(x, y + 1);
6920         }
6921         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6922                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6923                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6924                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6925         {
6926           ToggleSwitchgateSwitch();
6927         }
6928         else if (smashed == EL_LIGHT_SWITCH ||
6929                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6930         {
6931           ToggleLightSwitch(x, y + 1);
6932         }
6933         else
6934         {
6935           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6936
6937           CheckElementChangeBySide(x, y + 1, smashed, element,
6938                                    CE_SWITCHED, CH_SIDE_TOP);
6939           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6940                                             CH_SIDE_TOP);
6941         }
6942       }
6943       else
6944       {
6945         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6946       }
6947     }
6948   }
6949
6950   // play sound of magic wall / mill
6951   if (!last_line &&
6952       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6953        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6954        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6955   {
6956     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6957       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6958     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6959       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6960     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6961       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6962
6963     return;
6964   }
6965
6966   // play sound of object that hits the ground
6967   if (last_line || object_hit)
6968     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6969 }
6970
6971 static void TurnRoundExt(int x, int y)
6972 {
6973   static struct
6974   {
6975     int dx, dy;
6976   } move_xy[] =
6977   {
6978     {  0,  0 },
6979     { -1,  0 },
6980     { +1,  0 },
6981     {  0,  0 },
6982     {  0, -1 },
6983     {  0,  0 }, { 0, 0 }, { 0, 0 },
6984     {  0, +1 }
6985   };
6986   static struct
6987   {
6988     int left, right, back;
6989   } turn[] =
6990   {
6991     { 0,        0,              0        },
6992     { MV_DOWN,  MV_UP,          MV_RIGHT },
6993     { MV_UP,    MV_DOWN,        MV_LEFT  },
6994     { 0,        0,              0        },
6995     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6996     { 0,        0,              0        },
6997     { 0,        0,              0        },
6998     { 0,        0,              0        },
6999     { MV_RIGHT, MV_LEFT,        MV_UP    }
7000   };
7001
7002   int element = Tile[x][y];
7003   int move_pattern = element_info[element].move_pattern;
7004
7005   int old_move_dir = MovDir[x][y];
7006   int left_dir  = turn[old_move_dir].left;
7007   int right_dir = turn[old_move_dir].right;
7008   int back_dir  = turn[old_move_dir].back;
7009
7010   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7011   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7012   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7013   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7014
7015   int left_x  = x + left_dx,  left_y  = y + left_dy;
7016   int right_x = x + right_dx, right_y = y + right_dy;
7017   int move_x  = x + move_dx,  move_y  = y + move_dy;
7018
7019   int xx, yy;
7020
7021   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7022   {
7023     TestIfBadThingTouchesOtherBadThing(x, y);
7024
7025     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7026       MovDir[x][y] = right_dir;
7027     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7028       MovDir[x][y] = left_dir;
7029
7030     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7031       MovDelay[x][y] = 9;
7032     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7033       MovDelay[x][y] = 1;
7034   }
7035   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7036   {
7037     TestIfBadThingTouchesOtherBadThing(x, y);
7038
7039     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7040       MovDir[x][y] = left_dir;
7041     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7042       MovDir[x][y] = right_dir;
7043
7044     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7045       MovDelay[x][y] = 9;
7046     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7047       MovDelay[x][y] = 1;
7048   }
7049   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7050   {
7051     TestIfBadThingTouchesOtherBadThing(x, y);
7052
7053     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7054       MovDir[x][y] = left_dir;
7055     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7056       MovDir[x][y] = right_dir;
7057
7058     if (MovDir[x][y] != old_move_dir)
7059       MovDelay[x][y] = 9;
7060   }
7061   else if (element == EL_YAMYAM)
7062   {
7063     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7064     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7065
7066     if (can_turn_left && can_turn_right)
7067       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7068     else if (can_turn_left)
7069       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7070     else if (can_turn_right)
7071       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7072     else
7073       MovDir[x][y] = back_dir;
7074
7075     MovDelay[x][y] = 16 + 16 * RND(3);
7076   }
7077   else if (element == EL_DARK_YAMYAM)
7078   {
7079     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7080                                                          left_x, left_y);
7081     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7082                                                          right_x, right_y);
7083
7084     if (can_turn_left && can_turn_right)
7085       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7086     else if (can_turn_left)
7087       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7088     else if (can_turn_right)
7089       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7090     else
7091       MovDir[x][y] = back_dir;
7092
7093     MovDelay[x][y] = 16 + 16 * RND(3);
7094   }
7095   else if (element == EL_PACMAN)
7096   {
7097     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7098     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7099
7100     if (can_turn_left && can_turn_right)
7101       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7102     else if (can_turn_left)
7103       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7104     else if (can_turn_right)
7105       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7106     else
7107       MovDir[x][y] = back_dir;
7108
7109     MovDelay[x][y] = 6 + RND(40);
7110   }
7111   else if (element == EL_PIG)
7112   {
7113     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7114     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7115     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7116     boolean should_turn_left, should_turn_right, should_move_on;
7117     int rnd_value = 24;
7118     int rnd = RND(rnd_value);
7119
7120     should_turn_left = (can_turn_left &&
7121                         (!can_move_on ||
7122                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7123                                                    y + back_dy + left_dy)));
7124     should_turn_right = (can_turn_right &&
7125                          (!can_move_on ||
7126                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7127                                                     y + back_dy + right_dy)));
7128     should_move_on = (can_move_on &&
7129                       (!can_turn_left ||
7130                        !can_turn_right ||
7131                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7132                                                  y + move_dy + left_dy) ||
7133                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7134                                                  y + move_dy + right_dy)));
7135
7136     if (should_turn_left || should_turn_right || should_move_on)
7137     {
7138       if (should_turn_left && should_turn_right && should_move_on)
7139         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7140                         rnd < 2 * rnd_value / 3 ? right_dir :
7141                         old_move_dir);
7142       else if (should_turn_left && should_turn_right)
7143         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7144       else if (should_turn_left && should_move_on)
7145         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7146       else if (should_turn_right && should_move_on)
7147         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7148       else if (should_turn_left)
7149         MovDir[x][y] = left_dir;
7150       else if (should_turn_right)
7151         MovDir[x][y] = right_dir;
7152       else if (should_move_on)
7153         MovDir[x][y] = old_move_dir;
7154     }
7155     else if (can_move_on && rnd > rnd_value / 8)
7156       MovDir[x][y] = old_move_dir;
7157     else if (can_turn_left && can_turn_right)
7158       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7159     else if (can_turn_left && rnd > rnd_value / 8)
7160       MovDir[x][y] = left_dir;
7161     else if (can_turn_right && rnd > rnd_value/8)
7162       MovDir[x][y] = right_dir;
7163     else
7164       MovDir[x][y] = back_dir;
7165
7166     xx = x + move_xy[MovDir[x][y]].dx;
7167     yy = y + move_xy[MovDir[x][y]].dy;
7168
7169     if (!IN_LEV_FIELD(xx, yy) ||
7170         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7171       MovDir[x][y] = old_move_dir;
7172
7173     MovDelay[x][y] = 0;
7174   }
7175   else if (element == EL_DRAGON)
7176   {
7177     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7178     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7179     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7180     int rnd_value = 24;
7181     int rnd = RND(rnd_value);
7182
7183     if (can_move_on && rnd > rnd_value / 8)
7184       MovDir[x][y] = old_move_dir;
7185     else if (can_turn_left && can_turn_right)
7186       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7187     else if (can_turn_left && rnd > rnd_value / 8)
7188       MovDir[x][y] = left_dir;
7189     else if (can_turn_right && rnd > rnd_value / 8)
7190       MovDir[x][y] = right_dir;
7191     else
7192       MovDir[x][y] = back_dir;
7193
7194     xx = x + move_xy[MovDir[x][y]].dx;
7195     yy = y + move_xy[MovDir[x][y]].dy;
7196
7197     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7198       MovDir[x][y] = old_move_dir;
7199
7200     MovDelay[x][y] = 0;
7201   }
7202   else if (element == EL_MOLE)
7203   {
7204     boolean can_move_on =
7205       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7206                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7207                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7208     if (!can_move_on)
7209     {
7210       boolean can_turn_left =
7211         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7212                               IS_AMOEBOID(Tile[left_x][left_y])));
7213
7214       boolean can_turn_right =
7215         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7216                               IS_AMOEBOID(Tile[right_x][right_y])));
7217
7218       if (can_turn_left && can_turn_right)
7219         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7220       else if (can_turn_left)
7221         MovDir[x][y] = left_dir;
7222       else
7223         MovDir[x][y] = right_dir;
7224     }
7225
7226     if (MovDir[x][y] != old_move_dir)
7227       MovDelay[x][y] = 9;
7228   }
7229   else if (element == EL_BALLOON)
7230   {
7231     MovDir[x][y] = game.wind_direction;
7232     MovDelay[x][y] = 0;
7233   }
7234   else if (element == EL_SPRING)
7235   {
7236     if (MovDir[x][y] & MV_HORIZONTAL)
7237     {
7238       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7239           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7240       {
7241         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7242         ResetGfxAnimation(move_x, move_y);
7243         TEST_DrawLevelField(move_x, move_y);
7244
7245         MovDir[x][y] = back_dir;
7246       }
7247       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7248                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7249         MovDir[x][y] = MV_NONE;
7250     }
7251
7252     MovDelay[x][y] = 0;
7253   }
7254   else if (element == EL_ROBOT ||
7255            element == EL_SATELLITE ||
7256            element == EL_PENGUIN ||
7257            element == EL_EMC_ANDROID)
7258   {
7259     int attr_x = -1, attr_y = -1;
7260
7261     if (game.all_players_gone)
7262     {
7263       attr_x = game.exit_x;
7264       attr_y = game.exit_y;
7265     }
7266     else
7267     {
7268       int i;
7269
7270       for (i = 0; i < MAX_PLAYERS; i++)
7271       {
7272         struct PlayerInfo *player = &stored_player[i];
7273         int jx = player->jx, jy = player->jy;
7274
7275         if (!player->active)
7276           continue;
7277
7278         if (attr_x == -1 ||
7279             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7280         {
7281           attr_x = jx;
7282           attr_y = jy;
7283         }
7284       }
7285     }
7286
7287     if (element == EL_ROBOT &&
7288         game.robot_wheel_x >= 0 &&
7289         game.robot_wheel_y >= 0 &&
7290         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7291          game.engine_version < VERSION_IDENT(3,1,0,0)))
7292     {
7293       attr_x = game.robot_wheel_x;
7294       attr_y = game.robot_wheel_y;
7295     }
7296
7297     if (element == EL_PENGUIN)
7298     {
7299       int i;
7300       struct XY *xy = xy_topdown;
7301
7302       for (i = 0; i < NUM_DIRECTIONS; i++)
7303       {
7304         int ex = x + xy[i].x;
7305         int ey = y + xy[i].y;
7306
7307         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7308                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7309                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7310                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7311         {
7312           attr_x = ex;
7313           attr_y = ey;
7314           break;
7315         }
7316       }
7317     }
7318
7319     MovDir[x][y] = MV_NONE;
7320     if (attr_x < x)
7321       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7322     else if (attr_x > x)
7323       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7324     if (attr_y < y)
7325       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7326     else if (attr_y > y)
7327       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7328
7329     if (element == EL_ROBOT)
7330     {
7331       int newx, newy;
7332
7333       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7334         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7335       Moving2Blocked(x, y, &newx, &newy);
7336
7337       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7338         MovDelay[x][y] = 8 + 8 * !RND(3);
7339       else
7340         MovDelay[x][y] = 16;
7341     }
7342     else if (element == EL_PENGUIN)
7343     {
7344       int newx, newy;
7345
7346       MovDelay[x][y] = 1;
7347
7348       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7349       {
7350         boolean first_horiz = RND(2);
7351         int new_move_dir = MovDir[x][y];
7352
7353         MovDir[x][y] =
7354           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7355         Moving2Blocked(x, y, &newx, &newy);
7356
7357         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7358           return;
7359
7360         MovDir[x][y] =
7361           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7362         Moving2Blocked(x, y, &newx, &newy);
7363
7364         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7365           return;
7366
7367         MovDir[x][y] = old_move_dir;
7368         return;
7369       }
7370     }
7371     else if (element == EL_SATELLITE)
7372     {
7373       int newx, newy;
7374
7375       MovDelay[x][y] = 1;
7376
7377       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7378       {
7379         boolean first_horiz = RND(2);
7380         int new_move_dir = MovDir[x][y];
7381
7382         MovDir[x][y] =
7383           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7384         Moving2Blocked(x, y, &newx, &newy);
7385
7386         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7387           return;
7388
7389         MovDir[x][y] =
7390           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391         Moving2Blocked(x, y, &newx, &newy);
7392
7393         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7394           return;
7395
7396         MovDir[x][y] = old_move_dir;
7397         return;
7398       }
7399     }
7400     else if (element == EL_EMC_ANDROID)
7401     {
7402       static int check_pos[16] =
7403       {
7404         -1,             //  0 => (invalid)
7405         7,              //  1 => MV_LEFT
7406         3,              //  2 => MV_RIGHT
7407         -1,             //  3 => (invalid)
7408         1,              //  4 =>            MV_UP
7409         0,              //  5 => MV_LEFT  | MV_UP
7410         2,              //  6 => MV_RIGHT | MV_UP
7411         -1,             //  7 => (invalid)
7412         5,              //  8 =>            MV_DOWN
7413         6,              //  9 => MV_LEFT  | MV_DOWN
7414         4,              // 10 => MV_RIGHT | MV_DOWN
7415         -1,             // 11 => (invalid)
7416         -1,             // 12 => (invalid)
7417         -1,             // 13 => (invalid)
7418         -1,             // 14 => (invalid)
7419         -1,             // 15 => (invalid)
7420       };
7421       static struct
7422       {
7423         int dx, dy;
7424         int dir;
7425       } check_xy[8] =
7426       {
7427         { -1, -1,       MV_LEFT  | MV_UP   },
7428         {  0, -1,                  MV_UP   },
7429         { +1, -1,       MV_RIGHT | MV_UP   },
7430         { +1,  0,       MV_RIGHT           },
7431         { +1, +1,       MV_RIGHT | MV_DOWN },
7432         {  0, +1,                  MV_DOWN },
7433         { -1, +1,       MV_LEFT  | MV_DOWN },
7434         { -1,  0,       MV_LEFT            },
7435       };
7436       int start_pos, check_order;
7437       boolean can_clone = FALSE;
7438       int i;
7439
7440       // check if there is any free field around current position
7441       for (i = 0; i < 8; i++)
7442       {
7443         int newx = x + check_xy[i].dx;
7444         int newy = y + check_xy[i].dy;
7445
7446         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7447         {
7448           can_clone = TRUE;
7449
7450           break;
7451         }
7452       }
7453
7454       if (can_clone)            // randomly find an element to clone
7455       {
7456         can_clone = FALSE;
7457
7458         start_pos = check_pos[RND(8)];
7459         check_order = (RND(2) ? -1 : +1);
7460
7461         for (i = 0; i < 8; i++)
7462         {
7463           int pos_raw = start_pos + i * check_order;
7464           int pos = (pos_raw + 8) % 8;
7465           int newx = x + check_xy[pos].dx;
7466           int newy = y + check_xy[pos].dy;
7467
7468           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7469           {
7470             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7471             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7472
7473             Store[x][y] = Tile[newx][newy];
7474
7475             can_clone = TRUE;
7476
7477             break;
7478           }
7479         }
7480       }
7481
7482       if (can_clone)            // randomly find a direction to move
7483       {
7484         can_clone = FALSE;
7485
7486         start_pos = check_pos[RND(8)];
7487         check_order = (RND(2) ? -1 : +1);
7488
7489         for (i = 0; i < 8; i++)
7490         {
7491           int pos_raw = start_pos + i * check_order;
7492           int pos = (pos_raw + 8) % 8;
7493           int newx = x + check_xy[pos].dx;
7494           int newy = y + check_xy[pos].dy;
7495           int new_move_dir = check_xy[pos].dir;
7496
7497           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7498           {
7499             MovDir[x][y] = new_move_dir;
7500             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7501
7502             can_clone = TRUE;
7503
7504             break;
7505           }
7506         }
7507       }
7508
7509       if (can_clone)            // cloning and moving successful
7510         return;
7511
7512       // cannot clone -- try to move towards player
7513
7514       start_pos = check_pos[MovDir[x][y] & 0x0f];
7515       check_order = (RND(2) ? -1 : +1);
7516
7517       for (i = 0; i < 3; i++)
7518       {
7519         // first check start_pos, then previous/next or (next/previous) pos
7520         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7521         int pos = (pos_raw + 8) % 8;
7522         int newx = x + check_xy[pos].dx;
7523         int newy = y + check_xy[pos].dy;
7524         int new_move_dir = check_xy[pos].dir;
7525
7526         if (IS_PLAYER(newx, newy))
7527           break;
7528
7529         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7530         {
7531           MovDir[x][y] = new_move_dir;
7532           MovDelay[x][y] = level.android_move_time * 8 + 1;
7533
7534           break;
7535         }
7536       }
7537     }
7538   }
7539   else if (move_pattern == MV_TURNING_LEFT ||
7540            move_pattern == MV_TURNING_RIGHT ||
7541            move_pattern == MV_TURNING_LEFT_RIGHT ||
7542            move_pattern == MV_TURNING_RIGHT_LEFT ||
7543            move_pattern == MV_TURNING_RANDOM ||
7544            move_pattern == MV_ALL_DIRECTIONS)
7545   {
7546     boolean can_turn_left =
7547       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7548     boolean can_turn_right =
7549       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7550
7551     if (element_info[element].move_stepsize == 0)       // "not moving"
7552       return;
7553
7554     if (move_pattern == MV_TURNING_LEFT)
7555       MovDir[x][y] = left_dir;
7556     else if (move_pattern == MV_TURNING_RIGHT)
7557       MovDir[x][y] = right_dir;
7558     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7559       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7560     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7561       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7562     else if (move_pattern == MV_TURNING_RANDOM)
7563       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7564                       can_turn_right && !can_turn_left ? right_dir :
7565                       RND(2) ? left_dir : right_dir);
7566     else if (can_turn_left && can_turn_right)
7567       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7568     else if (can_turn_left)
7569       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7570     else if (can_turn_right)
7571       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7572     else
7573       MovDir[x][y] = back_dir;
7574
7575     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576   }
7577   else if (move_pattern == MV_HORIZONTAL ||
7578            move_pattern == MV_VERTICAL)
7579   {
7580     if (move_pattern & old_move_dir)
7581       MovDir[x][y] = back_dir;
7582     else if (move_pattern == MV_HORIZONTAL)
7583       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7584     else if (move_pattern == MV_VERTICAL)
7585       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7586
7587     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7588   }
7589   else if (move_pattern & MV_ANY_DIRECTION)
7590   {
7591     MovDir[x][y] = move_pattern;
7592     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7593   }
7594   else if (move_pattern & MV_WIND_DIRECTION)
7595   {
7596     MovDir[x][y] = game.wind_direction;
7597     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7598   }
7599   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7600   {
7601     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7602       MovDir[x][y] = left_dir;
7603     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7604       MovDir[x][y] = right_dir;
7605
7606     if (MovDir[x][y] != old_move_dir)
7607       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7608   }
7609   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7610   {
7611     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7612       MovDir[x][y] = right_dir;
7613     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7614       MovDir[x][y] = left_dir;
7615
7616     if (MovDir[x][y] != old_move_dir)
7617       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7618   }
7619   else if (move_pattern == MV_TOWARDS_PLAYER ||
7620            move_pattern == MV_AWAY_FROM_PLAYER)
7621   {
7622     int attr_x = -1, attr_y = -1;
7623     int newx, newy;
7624     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7625
7626     if (game.all_players_gone)
7627     {
7628       attr_x = game.exit_x;
7629       attr_y = game.exit_y;
7630     }
7631     else
7632     {
7633       int i;
7634
7635       for (i = 0; i < MAX_PLAYERS; i++)
7636       {
7637         struct PlayerInfo *player = &stored_player[i];
7638         int jx = player->jx, jy = player->jy;
7639
7640         if (!player->active)
7641           continue;
7642
7643         if (attr_x == -1 ||
7644             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7645         {
7646           attr_x = jx;
7647           attr_y = jy;
7648         }
7649       }
7650     }
7651
7652     MovDir[x][y] = MV_NONE;
7653     if (attr_x < x)
7654       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7655     else if (attr_x > x)
7656       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7657     if (attr_y < y)
7658       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7659     else if (attr_y > y)
7660       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7661
7662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7663
7664     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7665     {
7666       boolean first_horiz = RND(2);
7667       int new_move_dir = MovDir[x][y];
7668
7669       if (element_info[element].move_stepsize == 0)     // "not moving"
7670       {
7671         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7672         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7673
7674         return;
7675       }
7676
7677       MovDir[x][y] =
7678         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7679       Moving2Blocked(x, y, &newx, &newy);
7680
7681       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7682         return;
7683
7684       MovDir[x][y] =
7685         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7686       Moving2Blocked(x, y, &newx, &newy);
7687
7688       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7689         return;
7690
7691       MovDir[x][y] = old_move_dir;
7692     }
7693   }
7694   else if (move_pattern == MV_WHEN_PUSHED ||
7695            move_pattern == MV_WHEN_DROPPED)
7696   {
7697     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7698       MovDir[x][y] = MV_NONE;
7699
7700     MovDelay[x][y] = 0;
7701   }
7702   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7703   {
7704     struct XY *test_xy = xy_topdown;
7705     static int test_dir[4] =
7706     {
7707       MV_UP,
7708       MV_LEFT,
7709       MV_RIGHT,
7710       MV_DOWN
7711     };
7712     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7713     int move_preference = -1000000;     // start with very low preference
7714     int new_move_dir = MV_NONE;
7715     int start_test = RND(4);
7716     int i;
7717
7718     for (i = 0; i < NUM_DIRECTIONS; i++)
7719     {
7720       int j = (start_test + i) % 4;
7721       int move_dir = test_dir[j];
7722       int move_dir_preference;
7723
7724       xx = x + test_xy[j].x;
7725       yy = y + test_xy[j].y;
7726
7727       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7728           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7729       {
7730         new_move_dir = move_dir;
7731
7732         break;
7733       }
7734
7735       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7736         continue;
7737
7738       move_dir_preference = -1 * RunnerVisit[xx][yy];
7739       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7740         move_dir_preference = PlayerVisit[xx][yy];
7741
7742       if (move_dir_preference > move_preference)
7743       {
7744         // prefer field that has not been visited for the longest time
7745         move_preference = move_dir_preference;
7746         new_move_dir = move_dir;
7747       }
7748       else if (move_dir_preference == move_preference &&
7749                move_dir == old_move_dir)
7750       {
7751         // prefer last direction when all directions are preferred equally
7752         move_preference = move_dir_preference;
7753         new_move_dir = move_dir;
7754       }
7755     }
7756
7757     MovDir[x][y] = new_move_dir;
7758     if (old_move_dir != new_move_dir)
7759       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7760   }
7761 }
7762
7763 static void TurnRound(int x, int y)
7764 {
7765   int direction = MovDir[x][y];
7766
7767   TurnRoundExt(x, y);
7768
7769   GfxDir[x][y] = MovDir[x][y];
7770
7771   if (direction != MovDir[x][y])
7772     GfxFrame[x][y] = 0;
7773
7774   if (MovDelay[x][y])
7775     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7776
7777   ResetGfxFrame(x, y);
7778 }
7779
7780 static boolean JustBeingPushed(int x, int y)
7781 {
7782   int i;
7783
7784   for (i = 0; i < MAX_PLAYERS; i++)
7785   {
7786     struct PlayerInfo *player = &stored_player[i];
7787
7788     if (player->active && player->is_pushing && player->MovPos)
7789     {
7790       int next_jx = player->jx + (player->jx - player->last_jx);
7791       int next_jy = player->jy + (player->jy - player->last_jy);
7792
7793       if (x == next_jx && y == next_jy)
7794         return TRUE;
7795     }
7796   }
7797
7798   return FALSE;
7799 }
7800
7801 static void StartMoving(int x, int y)
7802 {
7803   boolean started_moving = FALSE;       // some elements can fall _and_ move
7804   int element = Tile[x][y];
7805
7806   if (Stop[x][y])
7807     return;
7808
7809   if (MovDelay[x][y] == 0)
7810     GfxAction[x][y] = ACTION_DEFAULT;
7811
7812   if (CAN_FALL(element) && y < lev_fieldy - 1)
7813   {
7814     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7815         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7816       if (JustBeingPushed(x, y))
7817         return;
7818
7819     if (element == EL_QUICKSAND_FULL)
7820     {
7821       if (IS_FREE(x, y + 1))
7822       {
7823         InitMovingField(x, y, MV_DOWN);
7824         started_moving = TRUE;
7825
7826         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7827 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7828         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7829           Store[x][y] = EL_ROCK;
7830 #else
7831         Store[x][y] = EL_ROCK;
7832 #endif
7833
7834         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7835       }
7836       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7837       {
7838         if (!MovDelay[x][y])
7839         {
7840           MovDelay[x][y] = TILEY + 1;
7841
7842           ResetGfxAnimation(x, y);
7843           ResetGfxAnimation(x, y + 1);
7844         }
7845
7846         if (MovDelay[x][y])
7847         {
7848           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7849           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7850
7851           MovDelay[x][y]--;
7852           if (MovDelay[x][y])
7853             return;
7854         }
7855
7856         Tile[x][y] = EL_QUICKSAND_EMPTY;
7857         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7858         Store[x][y + 1] = Store[x][y];
7859         Store[x][y] = 0;
7860
7861         PlayLevelSoundAction(x, y, ACTION_FILLING);
7862       }
7863       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7864       {
7865         if (!MovDelay[x][y])
7866         {
7867           MovDelay[x][y] = TILEY + 1;
7868
7869           ResetGfxAnimation(x, y);
7870           ResetGfxAnimation(x, y + 1);
7871         }
7872
7873         if (MovDelay[x][y])
7874         {
7875           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7876           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7877
7878           MovDelay[x][y]--;
7879           if (MovDelay[x][y])
7880             return;
7881         }
7882
7883         Tile[x][y] = EL_QUICKSAND_EMPTY;
7884         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7885         Store[x][y + 1] = Store[x][y];
7886         Store[x][y] = 0;
7887
7888         PlayLevelSoundAction(x, y, ACTION_FILLING);
7889       }
7890     }
7891     else if (element == EL_QUICKSAND_FAST_FULL)
7892     {
7893       if (IS_FREE(x, y + 1))
7894       {
7895         InitMovingField(x, y, MV_DOWN);
7896         started_moving = TRUE;
7897
7898         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7899 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7900         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7901           Store[x][y] = EL_ROCK;
7902 #else
7903         Store[x][y] = EL_ROCK;
7904 #endif
7905
7906         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7907       }
7908       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7909       {
7910         if (!MovDelay[x][y])
7911         {
7912           MovDelay[x][y] = TILEY + 1;
7913
7914           ResetGfxAnimation(x, y);
7915           ResetGfxAnimation(x, y + 1);
7916         }
7917
7918         if (MovDelay[x][y])
7919         {
7920           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7921           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7922
7923           MovDelay[x][y]--;
7924           if (MovDelay[x][y])
7925             return;
7926         }
7927
7928         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7929         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7930         Store[x][y + 1] = Store[x][y];
7931         Store[x][y] = 0;
7932
7933         PlayLevelSoundAction(x, y, ACTION_FILLING);
7934       }
7935       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7936       {
7937         if (!MovDelay[x][y])
7938         {
7939           MovDelay[x][y] = TILEY + 1;
7940
7941           ResetGfxAnimation(x, y);
7942           ResetGfxAnimation(x, y + 1);
7943         }
7944
7945         if (MovDelay[x][y])
7946         {
7947           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7948           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7949
7950           MovDelay[x][y]--;
7951           if (MovDelay[x][y])
7952             return;
7953         }
7954
7955         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7956         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7957         Store[x][y + 1] = Store[x][y];
7958         Store[x][y] = 0;
7959
7960         PlayLevelSoundAction(x, y, ACTION_FILLING);
7961       }
7962     }
7963     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7964              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7965     {
7966       InitMovingField(x, y, MV_DOWN);
7967       started_moving = TRUE;
7968
7969       Tile[x][y] = EL_QUICKSAND_FILLING;
7970       Store[x][y] = element;
7971
7972       PlayLevelSoundAction(x, y, ACTION_FILLING);
7973     }
7974     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7975              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7976     {
7977       InitMovingField(x, y, MV_DOWN);
7978       started_moving = TRUE;
7979
7980       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7981       Store[x][y] = element;
7982
7983       PlayLevelSoundAction(x, y, ACTION_FILLING);
7984     }
7985     else if (element == EL_MAGIC_WALL_FULL)
7986     {
7987       if (IS_FREE(x, y + 1))
7988       {
7989         InitMovingField(x, y, MV_DOWN);
7990         started_moving = TRUE;
7991
7992         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7993         Store[x][y] = EL_CHANGED(Store[x][y]);
7994       }
7995       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7996       {
7997         if (!MovDelay[x][y])
7998           MovDelay[x][y] = TILEY / 4 + 1;
7999
8000         if (MovDelay[x][y])
8001         {
8002           MovDelay[x][y]--;
8003           if (MovDelay[x][y])
8004             return;
8005         }
8006
8007         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8008         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8009         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8010         Store[x][y] = 0;
8011       }
8012     }
8013     else if (element == EL_BD_MAGIC_WALL_FULL)
8014     {
8015       if (IS_FREE(x, y + 1))
8016       {
8017         InitMovingField(x, y, MV_DOWN);
8018         started_moving = TRUE;
8019
8020         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8021         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8022       }
8023       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8024       {
8025         if (!MovDelay[x][y])
8026           MovDelay[x][y] = TILEY / 4 + 1;
8027
8028         if (MovDelay[x][y])
8029         {
8030           MovDelay[x][y]--;
8031           if (MovDelay[x][y])
8032             return;
8033         }
8034
8035         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8036         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8037         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8038         Store[x][y] = 0;
8039       }
8040     }
8041     else if (element == EL_DC_MAGIC_WALL_FULL)
8042     {
8043       if (IS_FREE(x, y + 1))
8044       {
8045         InitMovingField(x, y, MV_DOWN);
8046         started_moving = TRUE;
8047
8048         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8049         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8050       }
8051       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8052       {
8053         if (!MovDelay[x][y])
8054           MovDelay[x][y] = TILEY / 4 + 1;
8055
8056         if (MovDelay[x][y])
8057         {
8058           MovDelay[x][y]--;
8059           if (MovDelay[x][y])
8060             return;
8061         }
8062
8063         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8064         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8065         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8066         Store[x][y] = 0;
8067       }
8068     }
8069     else if ((CAN_PASS_MAGIC_WALL(element) &&
8070               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8071                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8072              (CAN_PASS_DC_MAGIC_WALL(element) &&
8073               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8074
8075     {
8076       InitMovingField(x, y, MV_DOWN);
8077       started_moving = TRUE;
8078
8079       Tile[x][y] =
8080         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8081          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8082          EL_DC_MAGIC_WALL_FILLING);
8083       Store[x][y] = element;
8084     }
8085     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8086     {
8087       SplashAcid(x, y + 1);
8088
8089       InitMovingField(x, y, MV_DOWN);
8090       started_moving = TRUE;
8091
8092       Store[x][y] = EL_ACID;
8093     }
8094     else if (
8095              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8096               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8097              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8098               CAN_FALL(element) && WasJustFalling[x][y] &&
8099               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8100
8101              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8102               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8103               (Tile[x][y + 1] == EL_BLOCKED)))
8104     {
8105       /* this is needed for a special case not covered by calling "Impact()"
8106          from "ContinueMoving()": if an element moves to a tile directly below
8107          another element which was just falling on that tile (which was empty
8108          in the previous frame), the falling element above would just stop
8109          instead of smashing the element below (in previous version, the above
8110          element was just checked for "moving" instead of "falling", resulting
8111          in incorrect smashes caused by horizontal movement of the above
8112          element; also, the case of the player being the element to smash was
8113          simply not covered here... :-/ ) */
8114
8115       CheckCollision[x][y] = 0;
8116       CheckImpact[x][y] = 0;
8117
8118       Impact(x, y);
8119     }
8120     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8121     {
8122       if (MovDir[x][y] == MV_NONE)
8123       {
8124         InitMovingField(x, y, MV_DOWN);
8125         started_moving = TRUE;
8126       }
8127     }
8128     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8129     {
8130       if (WasJustFalling[x][y]) // prevent animation from being restarted
8131         MovDir[x][y] = MV_DOWN;
8132
8133       InitMovingField(x, y, MV_DOWN);
8134       started_moving = TRUE;
8135     }
8136     else if (element == EL_AMOEBA_DROP)
8137     {
8138       Tile[x][y] = EL_AMOEBA_GROWING;
8139       Store[x][y] = EL_AMOEBA_WET;
8140     }
8141     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8142               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8143              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8144              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8145     {
8146       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8147                                 (IS_FREE(x - 1, y + 1) ||
8148                                  Tile[x - 1][y + 1] == EL_ACID));
8149       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8150                                 (IS_FREE(x + 1, y + 1) ||
8151                                  Tile[x + 1][y + 1] == EL_ACID));
8152       boolean can_fall_any  = (can_fall_left || can_fall_right);
8153       boolean can_fall_both = (can_fall_left && can_fall_right);
8154       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8155
8156       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8157       {
8158         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8159           can_fall_right = FALSE;
8160         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8161           can_fall_left = FALSE;
8162         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8163           can_fall_right = FALSE;
8164         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8165           can_fall_left = FALSE;
8166
8167         can_fall_any  = (can_fall_left || can_fall_right);
8168         can_fall_both = FALSE;
8169       }
8170
8171       if (can_fall_both)
8172       {
8173         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8174           can_fall_right = FALSE;       // slip down on left side
8175         else
8176           can_fall_left = !(can_fall_right = RND(2));
8177
8178         can_fall_both = FALSE;
8179       }
8180
8181       if (can_fall_any)
8182       {
8183         // if not determined otherwise, prefer left side for slipping down
8184         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8185         started_moving = TRUE;
8186       }
8187     }
8188     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8189     {
8190       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8191       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8192       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8193       int belt_dir = game.belt_dir[belt_nr];
8194
8195       if ((belt_dir == MV_LEFT  && left_is_free) ||
8196           (belt_dir == MV_RIGHT && right_is_free))
8197       {
8198         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8199
8200         InitMovingField(x, y, belt_dir);
8201         started_moving = TRUE;
8202
8203         Pushed[x][y] = TRUE;
8204         Pushed[nextx][y] = TRUE;
8205
8206         GfxAction[x][y] = ACTION_DEFAULT;
8207       }
8208       else
8209       {
8210         MovDir[x][y] = 0;       // if element was moving, stop it
8211       }
8212     }
8213   }
8214
8215   // not "else if" because of elements that can fall and move (EL_SPRING)
8216   if (CAN_MOVE(element) && !started_moving)
8217   {
8218     int move_pattern = element_info[element].move_pattern;
8219     int newx, newy;
8220
8221     Moving2Blocked(x, y, &newx, &newy);
8222
8223     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8224       return;
8225
8226     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8227         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8228     {
8229       WasJustMoving[x][y] = 0;
8230       CheckCollision[x][y] = 0;
8231
8232       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8233
8234       if (Tile[x][y] != element)        // element has changed
8235         return;
8236     }
8237
8238     if (!MovDelay[x][y])        // start new movement phase
8239     {
8240       // all objects that can change their move direction after each step
8241       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8242
8243       if (element != EL_YAMYAM &&
8244           element != EL_DARK_YAMYAM &&
8245           element != EL_PACMAN &&
8246           !(move_pattern & MV_ANY_DIRECTION) &&
8247           move_pattern != MV_TURNING_LEFT &&
8248           move_pattern != MV_TURNING_RIGHT &&
8249           move_pattern != MV_TURNING_LEFT_RIGHT &&
8250           move_pattern != MV_TURNING_RIGHT_LEFT &&
8251           move_pattern != MV_TURNING_RANDOM)
8252       {
8253         TurnRound(x, y);
8254
8255         if (MovDelay[x][y] && (element == EL_BUG ||
8256                                element == EL_SPACESHIP ||
8257                                element == EL_SP_SNIKSNAK ||
8258                                element == EL_SP_ELECTRON ||
8259                                element == EL_MOLE))
8260           TEST_DrawLevelField(x, y);
8261       }
8262     }
8263
8264     if (MovDelay[x][y])         // wait some time before next movement
8265     {
8266       MovDelay[x][y]--;
8267
8268       if (element == EL_ROBOT ||
8269           element == EL_YAMYAM ||
8270           element == EL_DARK_YAMYAM)
8271       {
8272         DrawLevelElementAnimationIfNeeded(x, y, element);
8273         PlayLevelSoundAction(x, y, ACTION_WAITING);
8274       }
8275       else if (element == EL_SP_ELECTRON)
8276         DrawLevelElementAnimationIfNeeded(x, y, element);
8277       else if (element == EL_DRAGON)
8278       {
8279         int i;
8280         int dir = MovDir[x][y];
8281         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8282         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8283         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8284                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8285                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8286                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8287         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8288
8289         GfxAction[x][y] = ACTION_ATTACKING;
8290
8291         if (IS_PLAYER(x, y))
8292           DrawPlayerField(x, y);
8293         else
8294           TEST_DrawLevelField(x, y);
8295
8296         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8297
8298         for (i = 1; i <= 3; i++)
8299         {
8300           int xx = x + i * dx;
8301           int yy = y + i * dy;
8302           int sx = SCREENX(xx);
8303           int sy = SCREENY(yy);
8304           int flame_graphic = graphic + (i - 1);
8305
8306           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8307             break;
8308
8309           if (MovDelay[x][y])
8310           {
8311             int flamed = MovingOrBlocked2Element(xx, yy);
8312
8313             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8314               Bang(xx, yy);
8315             else
8316               RemoveMovingField(xx, yy);
8317
8318             ChangeDelay[xx][yy] = 0;
8319
8320             Tile[xx][yy] = EL_FLAMES;
8321
8322             if (IN_SCR_FIELD(sx, sy))
8323             {
8324               TEST_DrawLevelFieldCrumbled(xx, yy);
8325               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8326             }
8327           }
8328           else
8329           {
8330             if (Tile[xx][yy] == EL_FLAMES)
8331               Tile[xx][yy] = EL_EMPTY;
8332             TEST_DrawLevelField(xx, yy);
8333           }
8334         }
8335       }
8336
8337       if (MovDelay[x][y])       // element still has to wait some time
8338       {
8339         PlayLevelSoundAction(x, y, ACTION_WAITING);
8340
8341         return;
8342       }
8343     }
8344
8345     // now make next step
8346
8347     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8348
8349     if (DONT_COLLIDE_WITH(element) &&
8350         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8351         !PLAYER_ENEMY_PROTECTED(newx, newy))
8352     {
8353       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8354
8355       return;
8356     }
8357
8358     else if (CAN_MOVE_INTO_ACID(element) &&
8359              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8360              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8361              (MovDir[x][y] == MV_DOWN ||
8362               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8363     {
8364       SplashAcid(newx, newy);
8365       Store[x][y] = EL_ACID;
8366     }
8367     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8368     {
8369       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8370           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8371           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8372           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8373       {
8374         RemoveField(x, y);
8375         TEST_DrawLevelField(x, y);
8376
8377         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8378         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8379           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8380
8381         game.friends_still_needed--;
8382         if (!game.friends_still_needed &&
8383             !game.GameOver &&
8384             game.all_players_gone)
8385           LevelSolved();
8386
8387         return;
8388       }
8389       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8390       {
8391         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8392           TEST_DrawLevelField(newx, newy);
8393         else
8394           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8395       }
8396       else if (!IS_FREE(newx, newy))
8397       {
8398         GfxAction[x][y] = ACTION_WAITING;
8399
8400         if (IS_PLAYER(x, y))
8401           DrawPlayerField(x, y);
8402         else
8403           TEST_DrawLevelField(x, y);
8404
8405         return;
8406       }
8407     }
8408     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8409     {
8410       if (IS_FOOD_PIG(Tile[newx][newy]))
8411       {
8412         if (IS_MOVING(newx, newy))
8413           RemoveMovingField(newx, newy);
8414         else
8415         {
8416           Tile[newx][newy] = EL_EMPTY;
8417           TEST_DrawLevelField(newx, newy);
8418         }
8419
8420         PlayLevelSound(x, y, SND_PIG_DIGGING);
8421       }
8422       else if (!IS_FREE(newx, newy))
8423       {
8424         if (IS_PLAYER(x, y))
8425           DrawPlayerField(x, y);
8426         else
8427           TEST_DrawLevelField(x, y);
8428
8429         return;
8430       }
8431     }
8432     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8433     {
8434       if (Store[x][y] != EL_EMPTY)
8435       {
8436         boolean can_clone = FALSE;
8437         int xx, yy;
8438
8439         // check if element to clone is still there
8440         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8441         {
8442           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8443           {
8444             can_clone = TRUE;
8445
8446             break;
8447           }
8448         }
8449
8450         // cannot clone or target field not free anymore -- do not clone
8451         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8452           Store[x][y] = EL_EMPTY;
8453       }
8454
8455       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8456       {
8457         if (IS_MV_DIAGONAL(MovDir[x][y]))
8458         {
8459           int diagonal_move_dir = MovDir[x][y];
8460           int stored = Store[x][y];
8461           int change_delay = 8;
8462           int graphic;
8463
8464           // android is moving diagonally
8465
8466           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8467
8468           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8469           GfxElement[x][y] = EL_EMC_ANDROID;
8470           GfxAction[x][y] = ACTION_SHRINKING;
8471           GfxDir[x][y] = diagonal_move_dir;
8472           ChangeDelay[x][y] = change_delay;
8473
8474           if (Store[x][y] == EL_EMPTY)
8475             Store[x][y] = GfxElementEmpty[x][y];
8476
8477           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8478                                    GfxDir[x][y]);
8479
8480           DrawLevelGraphicAnimation(x, y, graphic);
8481           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8482
8483           if (Tile[newx][newy] == EL_ACID)
8484           {
8485             SplashAcid(newx, newy);
8486
8487             return;
8488           }
8489
8490           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8491
8492           Store[newx][newy] = EL_EMC_ANDROID;
8493           GfxElement[newx][newy] = EL_EMC_ANDROID;
8494           GfxAction[newx][newy] = ACTION_GROWING;
8495           GfxDir[newx][newy] = diagonal_move_dir;
8496           ChangeDelay[newx][newy] = change_delay;
8497
8498           graphic = el_act_dir2img(GfxElement[newx][newy],
8499                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8500
8501           DrawLevelGraphicAnimation(newx, newy, graphic);
8502           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8503
8504           return;
8505         }
8506         else
8507         {
8508           Tile[newx][newy] = EL_EMPTY;
8509           TEST_DrawLevelField(newx, newy);
8510
8511           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8512         }
8513       }
8514       else if (!IS_FREE(newx, newy))
8515       {
8516         return;
8517       }
8518     }
8519     else if (IS_CUSTOM_ELEMENT(element) &&
8520              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8521     {
8522       if (!DigFieldByCE(newx, newy, element))
8523         return;
8524
8525       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8526       {
8527         RunnerVisit[x][y] = FrameCounter;
8528         PlayerVisit[x][y] /= 8;         // expire player visit path
8529       }
8530     }
8531     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8532     {
8533       if (!IS_FREE(newx, newy))
8534       {
8535         if (IS_PLAYER(x, y))
8536           DrawPlayerField(x, y);
8537         else
8538           TEST_DrawLevelField(x, y);
8539
8540         return;
8541       }
8542       else
8543       {
8544         boolean wanna_flame = !RND(10);
8545         int dx = newx - x, dy = newy - y;
8546         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8547         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8548         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8549                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8550         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8551                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8552
8553         if ((wanna_flame ||
8554              IS_CLASSIC_ENEMY(element1) ||
8555              IS_CLASSIC_ENEMY(element2)) &&
8556             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8557             element1 != EL_FLAMES && element2 != EL_FLAMES)
8558         {
8559           ResetGfxAnimation(x, y);
8560           GfxAction[x][y] = ACTION_ATTACKING;
8561
8562           if (IS_PLAYER(x, y))
8563             DrawPlayerField(x, y);
8564           else
8565             TEST_DrawLevelField(x, y);
8566
8567           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8568
8569           MovDelay[x][y] = 50;
8570
8571           Tile[newx][newy] = EL_FLAMES;
8572           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8573             Tile[newx1][newy1] = EL_FLAMES;
8574           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8575             Tile[newx2][newy2] = EL_FLAMES;
8576
8577           return;
8578         }
8579       }
8580     }
8581     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8582              Tile[newx][newy] == EL_DIAMOND)
8583     {
8584       if (IS_MOVING(newx, newy))
8585         RemoveMovingField(newx, newy);
8586       else
8587       {
8588         Tile[newx][newy] = EL_EMPTY;
8589         TEST_DrawLevelField(newx, newy);
8590       }
8591
8592       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8593     }
8594     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8595              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8596     {
8597       if (AmoebaNr[newx][newy])
8598       {
8599         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8600         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8601             Tile[newx][newy] == EL_BD_AMOEBA)
8602           AmoebaCnt[AmoebaNr[newx][newy]]--;
8603       }
8604
8605       if (IS_MOVING(newx, newy))
8606       {
8607         RemoveMovingField(newx, newy);
8608       }
8609       else
8610       {
8611         Tile[newx][newy] = EL_EMPTY;
8612         TEST_DrawLevelField(newx, newy);
8613       }
8614
8615       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8616     }
8617     else if ((element == EL_PACMAN || element == EL_MOLE)
8618              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8619     {
8620       if (AmoebaNr[newx][newy])
8621       {
8622         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8623         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8624             Tile[newx][newy] == EL_BD_AMOEBA)
8625           AmoebaCnt[AmoebaNr[newx][newy]]--;
8626       }
8627
8628       if (element == EL_MOLE)
8629       {
8630         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8631         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8632
8633         ResetGfxAnimation(x, y);
8634         GfxAction[x][y] = ACTION_DIGGING;
8635         TEST_DrawLevelField(x, y);
8636
8637         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8638
8639         return;                         // wait for shrinking amoeba
8640       }
8641       else      // element == EL_PACMAN
8642       {
8643         Tile[newx][newy] = EL_EMPTY;
8644         TEST_DrawLevelField(newx, newy);
8645         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8646       }
8647     }
8648     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8649              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8650               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8651     {
8652       // wait for shrinking amoeba to completely disappear
8653       return;
8654     }
8655     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8656     {
8657       // object was running against a wall
8658
8659       TurnRound(x, y);
8660
8661       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8662         DrawLevelElementAnimation(x, y, element);
8663
8664       if (DONT_TOUCH(element))
8665         TestIfBadThingTouchesPlayer(x, y);
8666
8667       return;
8668     }
8669
8670     InitMovingField(x, y, MovDir[x][y]);
8671
8672     PlayLevelSoundAction(x, y, ACTION_MOVING);
8673   }
8674
8675   if (MovDir[x][y])
8676     ContinueMoving(x, y);
8677 }
8678
8679 void ContinueMoving(int x, int y)
8680 {
8681   int element = Tile[x][y];
8682   struct ElementInfo *ei = &element_info[element];
8683   int direction = MovDir[x][y];
8684   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8685   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8686   int newx = x + dx, newy = y + dy;
8687   int stored = Store[x][y];
8688   int stored_new = Store[newx][newy];
8689   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8690   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8691   boolean last_line = (newy == lev_fieldy - 1);
8692   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8693
8694   if (pushed_by_player)         // special case: moving object pushed by player
8695   {
8696     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8697   }
8698   else if (use_step_delay)      // special case: moving object has step delay
8699   {
8700     if (!MovDelay[x][y])
8701       MovPos[x][y] += getElementMoveStepsize(x, y);
8702
8703     if (MovDelay[x][y])
8704       MovDelay[x][y]--;
8705     else
8706       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8707
8708     if (MovDelay[x][y])
8709     {
8710       TEST_DrawLevelField(x, y);
8711
8712       return;   // element is still waiting
8713     }
8714   }
8715   else                          // normal case: generically moving object
8716   {
8717     MovPos[x][y] += getElementMoveStepsize(x, y);
8718   }
8719
8720   if (ABS(MovPos[x][y]) < TILEX)
8721   {
8722     TEST_DrawLevelField(x, y);
8723
8724     return;     // element is still moving
8725   }
8726
8727   // element reached destination field
8728
8729   Tile[x][y] = EL_EMPTY;
8730   Tile[newx][newy] = element;
8731   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8732
8733   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8734   {
8735     element = Tile[newx][newy] = EL_ACID;
8736   }
8737   else if (element == EL_MOLE)
8738   {
8739     Tile[x][y] = EL_SAND;
8740
8741     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8742   }
8743   else if (element == EL_QUICKSAND_FILLING)
8744   {
8745     element = Tile[newx][newy] = get_next_element(element);
8746     Store[newx][newy] = Store[x][y];
8747   }
8748   else if (element == EL_QUICKSAND_EMPTYING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     element = Tile[newx][newy] = Store[x][y];
8752   }
8753   else if (element == EL_QUICKSAND_FAST_FILLING)
8754   {
8755     element = Tile[newx][newy] = get_next_element(element);
8756     Store[newx][newy] = Store[x][y];
8757   }
8758   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8759   {
8760     Tile[x][y] = get_next_element(element);
8761     element = Tile[newx][newy] = Store[x][y];
8762   }
8763   else if (element == EL_MAGIC_WALL_FILLING)
8764   {
8765     element = Tile[newx][newy] = get_next_element(element);
8766     if (!game.magic_wall_active)
8767       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8768     Store[newx][newy] = Store[x][y];
8769   }
8770   else if (element == EL_MAGIC_WALL_EMPTYING)
8771   {
8772     Tile[x][y] = get_next_element(element);
8773     if (!game.magic_wall_active)
8774       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8775     element = Tile[newx][newy] = Store[x][y];
8776
8777     InitField(newx, newy, FALSE);
8778   }
8779   else if (element == EL_BD_MAGIC_WALL_FILLING)
8780   {
8781     element = Tile[newx][newy] = get_next_element(element);
8782     if (!game.magic_wall_active)
8783       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8784     Store[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8787   {
8788     Tile[x][y] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8791     element = Tile[newx][newy] = Store[x][y];
8792
8793     InitField(newx, newy, FALSE);
8794   }
8795   else if (element == EL_DC_MAGIC_WALL_FILLING)
8796   {
8797     element = Tile[newx][newy] = get_next_element(element);
8798     if (!game.magic_wall_active)
8799       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8800     Store[newx][newy] = Store[x][y];
8801   }
8802   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8803   {
8804     Tile[x][y] = get_next_element(element);
8805     if (!game.magic_wall_active)
8806       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8807     element = Tile[newx][newy] = Store[x][y];
8808
8809     InitField(newx, newy, FALSE);
8810   }
8811   else if (element == EL_AMOEBA_DROPPING)
8812   {
8813     Tile[x][y] = get_next_element(element);
8814     element = Tile[newx][newy] = Store[x][y];
8815   }
8816   else if (element == EL_SOKOBAN_OBJECT)
8817   {
8818     if (Back[x][y])
8819       Tile[x][y] = Back[x][y];
8820
8821     if (Back[newx][newy])
8822       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8823
8824     Back[x][y] = Back[newx][newy] = 0;
8825   }
8826
8827   Store[x][y] = EL_EMPTY;
8828   MovPos[x][y] = 0;
8829   MovDir[x][y] = 0;
8830   MovDelay[x][y] = 0;
8831
8832   MovDelay[newx][newy] = 0;
8833
8834   if (CAN_CHANGE_OR_HAS_ACTION(element))
8835   {
8836     // copy element change control values to new field
8837     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8838     ChangePage[newx][newy]  = ChangePage[x][y];
8839     ChangeCount[newx][newy] = ChangeCount[x][y];
8840     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8841   }
8842
8843   CustomValue[newx][newy] = CustomValue[x][y];
8844
8845   ChangeDelay[x][y] = 0;
8846   ChangePage[x][y] = -1;
8847   ChangeCount[x][y] = 0;
8848   ChangeEvent[x][y] = -1;
8849
8850   CustomValue[x][y] = 0;
8851
8852   // copy animation control values to new field
8853   GfxFrame[newx][newy]  = GfxFrame[x][y];
8854   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8855   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8856   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8857
8858   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8859
8860   // some elements can leave other elements behind after moving
8861   if (ei->move_leave_element != EL_EMPTY &&
8862       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8863       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8864   {
8865     int move_leave_element = ei->move_leave_element;
8866
8867     // this makes it possible to leave the removed element again
8868     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8869       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8870
8871     Tile[x][y] = move_leave_element;
8872
8873     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8874       MovDir[x][y] = direction;
8875
8876     InitField(x, y, FALSE);
8877
8878     if (GFX_CRUMBLED(Tile[x][y]))
8879       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8880
8881     if (IS_PLAYER_ELEMENT(move_leave_element))
8882       RelocatePlayer(x, y, move_leave_element);
8883   }
8884
8885   // do this after checking for left-behind element
8886   ResetGfxAnimation(x, y);      // reset animation values for old field
8887
8888   if (!CAN_MOVE(element) ||
8889       (CAN_FALL(element) && direction == MV_DOWN &&
8890        (element == EL_SPRING ||
8891         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8892         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8893     GfxDir[x][y] = MovDir[newx][newy] = 0;
8894
8895   TEST_DrawLevelField(x, y);
8896   TEST_DrawLevelField(newx, newy);
8897
8898   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8899
8900   // prevent pushed element from moving on in pushed direction
8901   if (pushed_by_player && CAN_MOVE(element) &&
8902       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8903       !(element_info[element].move_pattern & direction))
8904     TurnRound(newx, newy);
8905
8906   // prevent elements on conveyor belt from moving on in last direction
8907   if (pushed_by_conveyor && CAN_FALL(element) &&
8908       direction & MV_HORIZONTAL)
8909     MovDir[newx][newy] = 0;
8910
8911   if (!pushed_by_player)
8912   {
8913     int nextx = newx + dx, nexty = newy + dy;
8914     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8915
8916     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8917
8918     if (CAN_FALL(element) && direction == MV_DOWN)
8919       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8920
8921     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8922       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8923
8924     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8925       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8926   }
8927
8928   if (DONT_TOUCH(element))      // object may be nasty to player or others
8929   {
8930     TestIfBadThingTouchesPlayer(newx, newy);
8931     TestIfBadThingTouchesFriend(newx, newy);
8932
8933     if (!IS_CUSTOM_ELEMENT(element))
8934       TestIfBadThingTouchesOtherBadThing(newx, newy);
8935   }
8936   else if (element == EL_PENGUIN)
8937     TestIfFriendTouchesBadThing(newx, newy);
8938
8939   if (DONT_GET_HIT_BY(element))
8940   {
8941     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8942   }
8943
8944   // give the player one last chance (one more frame) to move away
8945   if (CAN_FALL(element) && direction == MV_DOWN &&
8946       (last_line || (!IS_FREE(x, newy + 1) &&
8947                      (!IS_PLAYER(x, newy + 1) ||
8948                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8949     Impact(x, newy);
8950
8951   if (pushed_by_player && !game.use_change_when_pushing_bug)
8952   {
8953     int push_side = MV_DIR_OPPOSITE(direction);
8954     struct PlayerInfo *player = PLAYERINFO(x, y);
8955
8956     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8957                                player->index_bit, push_side);
8958     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8959                                         player->index_bit, push_side);
8960   }
8961
8962   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8963     MovDelay[newx][newy] = 1;
8964
8965   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8966
8967   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8968   TestIfElementHitsCustomElement(newx, newy, direction);
8969   TestIfPlayerTouchesCustomElement(newx, newy);
8970   TestIfElementTouchesCustomElement(newx, newy);
8971
8972   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8973       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8974     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8975                              MV_DIR_OPPOSITE(direction));
8976 }
8977
8978 int AmoebaNeighbourNr(int ax, int ay)
8979 {
8980   int i;
8981   int element = Tile[ax][ay];
8982   int group_nr = 0;
8983   struct XY *xy = xy_topdown;
8984
8985   for (i = 0; i < NUM_DIRECTIONS; i++)
8986   {
8987     int x = ax + xy[i].x;
8988     int y = ay + xy[i].y;
8989
8990     if (!IN_LEV_FIELD(x, y))
8991       continue;
8992
8993     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8994       group_nr = AmoebaNr[x][y];
8995   }
8996
8997   return group_nr;
8998 }
8999
9000 static void AmoebaMerge(int ax, int ay)
9001 {
9002   int i, x, y, xx, yy;
9003   int new_group_nr = AmoebaNr[ax][ay];
9004   struct XY *xy = xy_topdown;
9005
9006   if (new_group_nr == 0)
9007     return;
9008
9009   for (i = 0; i < NUM_DIRECTIONS; i++)
9010   {
9011     x = ax + xy[i].x;
9012     y = ay + xy[i].y;
9013
9014     if (!IN_LEV_FIELD(x, y))
9015       continue;
9016
9017     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9018          Tile[x][y] == EL_BD_AMOEBA ||
9019          Tile[x][y] == EL_AMOEBA_DEAD) &&
9020         AmoebaNr[x][y] != new_group_nr)
9021     {
9022       int old_group_nr = AmoebaNr[x][y];
9023
9024       if (old_group_nr == 0)
9025         return;
9026
9027       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9028       AmoebaCnt[old_group_nr] = 0;
9029       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9030       AmoebaCnt2[old_group_nr] = 0;
9031
9032       SCAN_PLAYFIELD(xx, yy)
9033       {
9034         if (AmoebaNr[xx][yy] == old_group_nr)
9035           AmoebaNr[xx][yy] = new_group_nr;
9036       }
9037     }
9038   }
9039 }
9040
9041 void AmoebaToDiamond(int ax, int ay)
9042 {
9043   int i, x, y;
9044
9045   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9046   {
9047     int group_nr = AmoebaNr[ax][ay];
9048
9049 #ifdef DEBUG
9050     if (group_nr == 0)
9051     {
9052       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9053       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9054
9055       return;
9056     }
9057 #endif
9058
9059     SCAN_PLAYFIELD(x, y)
9060     {
9061       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9062       {
9063         AmoebaNr[x][y] = 0;
9064         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9065       }
9066     }
9067
9068     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9069                             SND_AMOEBA_TURNING_TO_GEM :
9070                             SND_AMOEBA_TURNING_TO_ROCK));
9071     Bang(ax, ay);
9072   }
9073   else
9074   {
9075     struct XY *xy = xy_topdown;
9076
9077     for (i = 0; i < NUM_DIRECTIONS; i++)
9078     {
9079       x = ax + xy[i].x;
9080       y = ay + xy[i].y;
9081
9082       if (!IN_LEV_FIELD(x, y))
9083         continue;
9084
9085       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9086       {
9087         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9088                               SND_AMOEBA_TURNING_TO_GEM :
9089                               SND_AMOEBA_TURNING_TO_ROCK));
9090         Bang(x, y);
9091       }
9092     }
9093   }
9094 }
9095
9096 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9097 {
9098   int x, y;
9099   int group_nr = AmoebaNr[ax][ay];
9100   boolean done = FALSE;
9101
9102 #ifdef DEBUG
9103   if (group_nr == 0)
9104   {
9105     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9106     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9107
9108     return;
9109   }
9110 #endif
9111
9112   SCAN_PLAYFIELD(x, y)
9113   {
9114     if (AmoebaNr[x][y] == group_nr &&
9115         (Tile[x][y] == EL_AMOEBA_DEAD ||
9116          Tile[x][y] == EL_BD_AMOEBA ||
9117          Tile[x][y] == EL_AMOEBA_GROWING))
9118     {
9119       AmoebaNr[x][y] = 0;
9120       Tile[x][y] = new_element;
9121       InitField(x, y, FALSE);
9122       TEST_DrawLevelField(x, y);
9123       done = TRUE;
9124     }
9125   }
9126
9127   if (done)
9128     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9129                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9130                             SND_BD_AMOEBA_TURNING_TO_GEM));
9131 }
9132
9133 static void AmoebaGrowing(int x, int y)
9134 {
9135   static DelayCounter sound_delay = { 0 };
9136
9137   if (!MovDelay[x][y])          // start new growing cycle
9138   {
9139     MovDelay[x][y] = 7;
9140
9141     if (DelayReached(&sound_delay))
9142     {
9143       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9144       sound_delay.value = 30;
9145     }
9146   }
9147
9148   if (MovDelay[x][y])           // wait some time before growing bigger
9149   {
9150     MovDelay[x][y]--;
9151     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9152     {
9153       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9154                                            6 - MovDelay[x][y]);
9155
9156       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9157     }
9158
9159     if (!MovDelay[x][y])
9160     {
9161       Tile[x][y] = Store[x][y];
9162       Store[x][y] = 0;
9163       TEST_DrawLevelField(x, y);
9164     }
9165   }
9166 }
9167
9168 static void AmoebaShrinking(int x, int y)
9169 {
9170   static DelayCounter sound_delay = { 0 };
9171
9172   if (!MovDelay[x][y])          // start new shrinking cycle
9173   {
9174     MovDelay[x][y] = 7;
9175
9176     if (DelayReached(&sound_delay))
9177       sound_delay.value = 30;
9178   }
9179
9180   if (MovDelay[x][y])           // wait some time before shrinking
9181   {
9182     MovDelay[x][y]--;
9183     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9184     {
9185       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9186                                            6 - MovDelay[x][y]);
9187
9188       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9189     }
9190
9191     if (!MovDelay[x][y])
9192     {
9193       Tile[x][y] = EL_EMPTY;
9194       TEST_DrawLevelField(x, y);
9195
9196       // don't let mole enter this field in this cycle;
9197       // (give priority to objects falling to this field from above)
9198       Stop[x][y] = TRUE;
9199     }
9200   }
9201 }
9202
9203 static void AmoebaReproduce(int ax, int ay)
9204 {
9205   int i;
9206   int element = Tile[ax][ay];
9207   int graphic = el2img(element);
9208   int newax = ax, neway = ay;
9209   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9210   struct XY *xy = xy_topdown;
9211
9212   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9213   {
9214     Tile[ax][ay] = EL_AMOEBA_DEAD;
9215     TEST_DrawLevelField(ax, ay);
9216     return;
9217   }
9218
9219   if (IS_ANIMATED(graphic))
9220     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9221
9222   if (!MovDelay[ax][ay])        // start making new amoeba field
9223     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9224
9225   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9226   {
9227     MovDelay[ax][ay]--;
9228     if (MovDelay[ax][ay])
9229       return;
9230   }
9231
9232   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9233   {
9234     int start = RND(4);
9235     int x = ax + xy[start].x;
9236     int y = ay + xy[start].y;
9237
9238     if (!IN_LEV_FIELD(x, y))
9239       return;
9240
9241     if (IS_FREE(x, y) ||
9242         CAN_GROW_INTO(Tile[x][y]) ||
9243         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9244         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9245     {
9246       newax = x;
9247       neway = y;
9248     }
9249
9250     if (newax == ax && neway == ay)
9251       return;
9252   }
9253   else                          // normal or "filled" (BD style) amoeba
9254   {
9255     int start = RND(4);
9256     boolean waiting_for_player = FALSE;
9257
9258     for (i = 0; i < NUM_DIRECTIONS; i++)
9259     {
9260       int j = (start + i) % 4;
9261       int x = ax + xy[j].x;
9262       int y = ay + xy[j].y;
9263
9264       if (!IN_LEV_FIELD(x, y))
9265         continue;
9266
9267       if (IS_FREE(x, y) ||
9268           CAN_GROW_INTO(Tile[x][y]) ||
9269           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9270           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9271       {
9272         newax = x;
9273         neway = y;
9274         break;
9275       }
9276       else if (IS_PLAYER(x, y))
9277         waiting_for_player = TRUE;
9278     }
9279
9280     if (newax == ax && neway == ay)             // amoeba cannot grow
9281     {
9282       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9283       {
9284         Tile[ax][ay] = EL_AMOEBA_DEAD;
9285         TEST_DrawLevelField(ax, ay);
9286         AmoebaCnt[AmoebaNr[ax][ay]]--;
9287
9288         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9289         {
9290           if (element == EL_AMOEBA_FULL)
9291             AmoebaToDiamond(ax, ay);
9292           else if (element == EL_BD_AMOEBA)
9293             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9294         }
9295       }
9296       return;
9297     }
9298     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9299     {
9300       // amoeba gets larger by growing in some direction
9301
9302       int new_group_nr = AmoebaNr[ax][ay];
9303
9304 #ifdef DEBUG
9305   if (new_group_nr == 0)
9306   {
9307     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9308           newax, neway);
9309     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9310
9311     return;
9312   }
9313 #endif
9314
9315       AmoebaNr[newax][neway] = new_group_nr;
9316       AmoebaCnt[new_group_nr]++;
9317       AmoebaCnt2[new_group_nr]++;
9318
9319       // if amoeba touches other amoeba(s) after growing, unify them
9320       AmoebaMerge(newax, neway);
9321
9322       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9323       {
9324         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9325         return;
9326       }
9327     }
9328   }
9329
9330   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9331       (neway == lev_fieldy - 1 && newax != ax))
9332   {
9333     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9334     Store[newax][neway] = element;
9335   }
9336   else if (neway == ay || element == EL_EMC_DRIPPER)
9337   {
9338     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9339
9340     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9341   }
9342   else
9343   {
9344     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9345     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9346     Store[ax][ay] = EL_AMOEBA_DROP;
9347     ContinueMoving(ax, ay);
9348     return;
9349   }
9350
9351   TEST_DrawLevelField(newax, neway);
9352 }
9353
9354 static void Life(int ax, int ay)
9355 {
9356   int x1, y1, x2, y2;
9357   int life_time = 40;
9358   int element = Tile[ax][ay];
9359   int graphic = el2img(element);
9360   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9361                          level.biomaze);
9362   boolean changed = FALSE;
9363
9364   if (IS_ANIMATED(graphic))
9365     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9366
9367   if (Stop[ax][ay])
9368     return;
9369
9370   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9371     MovDelay[ax][ay] = life_time;
9372
9373   if (MovDelay[ax][ay])         // wait some time before next cycle
9374   {
9375     MovDelay[ax][ay]--;
9376     if (MovDelay[ax][ay])
9377       return;
9378   }
9379
9380   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9381   {
9382     int xx = ax+x1, yy = ay+y1;
9383     int old_element = Tile[xx][yy];
9384     int num_neighbours = 0;
9385
9386     if (!IN_LEV_FIELD(xx, yy))
9387       continue;
9388
9389     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9390     {
9391       int x = xx+x2, y = yy+y2;
9392
9393       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9394         continue;
9395
9396       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9397       boolean is_neighbour = FALSE;
9398
9399       if (level.use_life_bugs)
9400         is_neighbour =
9401           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9402            (IS_FREE(x, y)                             &&  Stop[x][y]));
9403       else
9404         is_neighbour =
9405           (Last[x][y] == element || is_player_cell);
9406
9407       if (is_neighbour)
9408         num_neighbours++;
9409     }
9410
9411     boolean is_free = FALSE;
9412
9413     if (level.use_life_bugs)
9414       is_free = (IS_FREE(xx, yy));
9415     else
9416       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9417
9418     if (xx == ax && yy == ay)           // field in the middle
9419     {
9420       if (num_neighbours < life_parameter[0] ||
9421           num_neighbours > life_parameter[1])
9422       {
9423         Tile[xx][yy] = EL_EMPTY;
9424         if (Tile[xx][yy] != old_element)
9425           TEST_DrawLevelField(xx, yy);
9426         Stop[xx][yy] = TRUE;
9427         changed = TRUE;
9428       }
9429     }
9430     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9431     {                                   // free border field
9432       if (num_neighbours >= life_parameter[2] &&
9433           num_neighbours <= life_parameter[3])
9434       {
9435         Tile[xx][yy] = element;
9436         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9437         if (Tile[xx][yy] != old_element)
9438           TEST_DrawLevelField(xx, yy);
9439         Stop[xx][yy] = TRUE;
9440         changed = TRUE;
9441       }
9442     }
9443   }
9444
9445   if (changed)
9446     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9447                    SND_GAME_OF_LIFE_GROWING);
9448 }
9449
9450 static void InitRobotWheel(int x, int y)
9451 {
9452   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9453 }
9454
9455 static void RunRobotWheel(int x, int y)
9456 {
9457   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9458 }
9459
9460 static void StopRobotWheel(int x, int y)
9461 {
9462   if (game.robot_wheel_x == x &&
9463       game.robot_wheel_y == y)
9464   {
9465     game.robot_wheel_x = -1;
9466     game.robot_wheel_y = -1;
9467     game.robot_wheel_active = FALSE;
9468   }
9469 }
9470
9471 static void InitTimegateWheel(int x, int y)
9472 {
9473   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9474 }
9475
9476 static void RunTimegateWheel(int x, int y)
9477 {
9478   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9479 }
9480
9481 static void InitMagicBallDelay(int x, int y)
9482 {
9483   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9484 }
9485
9486 static void ActivateMagicBall(int bx, int by)
9487 {
9488   int x, y;
9489
9490   if (level.ball_random)
9491   {
9492     int pos_border = RND(8);    // select one of the eight border elements
9493     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9494     int xx = pos_content % 3;
9495     int yy = pos_content / 3;
9496
9497     x = bx - 1 + xx;
9498     y = by - 1 + yy;
9499
9500     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9501       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9502   }
9503   else
9504   {
9505     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9506     {
9507       int xx = x - bx + 1;
9508       int yy = y - by + 1;
9509
9510       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9511         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9512     }
9513   }
9514
9515   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9516 }
9517
9518 static void CheckExit(int x, int y)
9519 {
9520   if (game.gems_still_needed > 0 ||
9521       game.sokoban_fields_still_needed > 0 ||
9522       game.sokoban_objects_still_needed > 0 ||
9523       game.lights_still_needed > 0)
9524   {
9525     int element = Tile[x][y];
9526     int graphic = el2img(element);
9527
9528     if (IS_ANIMATED(graphic))
9529       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9530
9531     return;
9532   }
9533
9534   // do not re-open exit door closed after last player
9535   if (game.all_players_gone)
9536     return;
9537
9538   Tile[x][y] = EL_EXIT_OPENING;
9539
9540   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9541 }
9542
9543 static void CheckExitEM(int x, int y)
9544 {
9545   if (game.gems_still_needed > 0 ||
9546       game.sokoban_fields_still_needed > 0 ||
9547       game.sokoban_objects_still_needed > 0 ||
9548       game.lights_still_needed > 0)
9549   {
9550     int element = Tile[x][y];
9551     int graphic = el2img(element);
9552
9553     if (IS_ANIMATED(graphic))
9554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555
9556     return;
9557   }
9558
9559   // do not re-open exit door closed after last player
9560   if (game.all_players_gone)
9561     return;
9562
9563   Tile[x][y] = EL_EM_EXIT_OPENING;
9564
9565   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9566 }
9567
9568 static void CheckExitSteel(int x, int y)
9569 {
9570   if (game.gems_still_needed > 0 ||
9571       game.sokoban_fields_still_needed > 0 ||
9572       game.sokoban_objects_still_needed > 0 ||
9573       game.lights_still_needed > 0)
9574   {
9575     int element = Tile[x][y];
9576     int graphic = el2img(element);
9577
9578     if (IS_ANIMATED(graphic))
9579       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9580
9581     return;
9582   }
9583
9584   // do not re-open exit door closed after last player
9585   if (game.all_players_gone)
9586     return;
9587
9588   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9589
9590   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9591 }
9592
9593 static void CheckExitSteelEM(int x, int y)
9594 {
9595   if (game.gems_still_needed > 0 ||
9596       game.sokoban_fields_still_needed > 0 ||
9597       game.sokoban_objects_still_needed > 0 ||
9598       game.lights_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_EM_STEEL_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9616 }
9617
9618 static void CheckExitSP(int x, int y)
9619 {
9620   if (game.gems_still_needed > 0)
9621   {
9622     int element = Tile[x][y];
9623     int graphic = el2img(element);
9624
9625     if (IS_ANIMATED(graphic))
9626       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9627
9628     return;
9629   }
9630
9631   // do not re-open exit door closed after last player
9632   if (game.all_players_gone)
9633     return;
9634
9635   Tile[x][y] = EL_SP_EXIT_OPENING;
9636
9637   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9638 }
9639
9640 static void CloseAllOpenTimegates(void)
9641 {
9642   int x, y;
9643
9644   SCAN_PLAYFIELD(x, y)
9645   {
9646     int element = Tile[x][y];
9647
9648     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9649     {
9650       Tile[x][y] = EL_TIMEGATE_CLOSING;
9651
9652       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9653     }
9654   }
9655 }
9656
9657 static void DrawTwinkleOnField(int x, int y)
9658 {
9659   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9660     return;
9661
9662   if (Tile[x][y] == EL_BD_DIAMOND)
9663     return;
9664
9665   if (MovDelay[x][y] == 0)      // next animation frame
9666     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9667
9668   if (MovDelay[x][y] != 0)      // wait some time before next frame
9669   {
9670     MovDelay[x][y]--;
9671
9672     DrawLevelElementAnimation(x, y, Tile[x][y]);
9673
9674     if (MovDelay[x][y] != 0)
9675     {
9676       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9677                                            10 - MovDelay[x][y]);
9678
9679       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9680     }
9681   }
9682 }
9683
9684 static void WallGrowing(int x, int y)
9685 {
9686   int delay = 6;
9687
9688   if (!MovDelay[x][y])          // next animation frame
9689     MovDelay[x][y] = 3 * delay;
9690
9691   if (MovDelay[x][y])           // wait some time before next frame
9692   {
9693     MovDelay[x][y]--;
9694
9695     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9696     {
9697       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9698       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9699
9700       DrawLevelGraphic(x, y, graphic, frame);
9701     }
9702
9703     if (!MovDelay[x][y])
9704     {
9705       if (MovDir[x][y] == MV_LEFT)
9706       {
9707         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9708           TEST_DrawLevelField(x - 1, y);
9709       }
9710       else if (MovDir[x][y] == MV_RIGHT)
9711       {
9712         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9713           TEST_DrawLevelField(x + 1, y);
9714       }
9715       else if (MovDir[x][y] == MV_UP)
9716       {
9717         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9718           TEST_DrawLevelField(x, y - 1);
9719       }
9720       else
9721       {
9722         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9723           TEST_DrawLevelField(x, y + 1);
9724       }
9725
9726       Tile[x][y] = Store[x][y];
9727       Store[x][y] = 0;
9728       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9729       TEST_DrawLevelField(x, y);
9730     }
9731   }
9732 }
9733
9734 static void CheckWallGrowing(int ax, int ay)
9735 {
9736   int element = Tile[ax][ay];
9737   int graphic = el2img(element);
9738   boolean free_top    = FALSE;
9739   boolean free_bottom = FALSE;
9740   boolean free_left   = FALSE;
9741   boolean free_right  = FALSE;
9742   boolean stop_top    = FALSE;
9743   boolean stop_bottom = FALSE;
9744   boolean stop_left   = FALSE;
9745   boolean stop_right  = FALSE;
9746   boolean new_wall    = FALSE;
9747
9748   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9749                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9750                            element == EL_EXPANDABLE_STEELWALL_ANY);
9751
9752   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9753                              element == EL_EXPANDABLE_WALL_ANY ||
9754                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9755                              element == EL_EXPANDABLE_STEELWALL_ANY);
9756
9757   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9758                              element == EL_EXPANDABLE_WALL_ANY ||
9759                              element == EL_EXPANDABLE_WALL ||
9760                              element == EL_BD_EXPANDABLE_WALL ||
9761                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9762                              element == EL_EXPANDABLE_STEELWALL_ANY);
9763
9764   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9765                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9766
9767   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9768                              element == EL_EXPANDABLE_WALL ||
9769                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9770
9771   int wall_growing = (is_steelwall ?
9772                       EL_EXPANDABLE_STEELWALL_GROWING :
9773                       EL_EXPANDABLE_WALL_GROWING);
9774
9775   int gfx_wall_growing_up    = (is_steelwall ?
9776                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9777                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9778   int gfx_wall_growing_down  = (is_steelwall ?
9779                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9780                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9781   int gfx_wall_growing_left  = (is_steelwall ?
9782                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9783                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9784   int gfx_wall_growing_right = (is_steelwall ?
9785                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9786                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9787
9788   if (IS_ANIMATED(graphic))
9789     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9790
9791   if (!MovDelay[ax][ay])        // start building new wall
9792     MovDelay[ax][ay] = 6;
9793
9794   if (MovDelay[ax][ay])         // wait some time before building new wall
9795   {
9796     MovDelay[ax][ay]--;
9797     if (MovDelay[ax][ay])
9798       return;
9799   }
9800
9801   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9802     free_top = TRUE;
9803   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9804     free_bottom = TRUE;
9805   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9806     free_left = TRUE;
9807   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9808     free_right = TRUE;
9809
9810   if (grow_vertical)
9811   {
9812     if (free_top)
9813     {
9814       Tile[ax][ay - 1] = wall_growing;
9815       Store[ax][ay - 1] = element;
9816       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9817
9818       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9819         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9820
9821       new_wall = TRUE;
9822     }
9823
9824     if (free_bottom)
9825     {
9826       Tile[ax][ay + 1] = wall_growing;
9827       Store[ax][ay + 1] = element;
9828       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9829
9830       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9831         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9832
9833       new_wall = TRUE;
9834     }
9835   }
9836
9837   if (grow_horizontal)
9838   {
9839     if (free_left)
9840     {
9841       Tile[ax - 1][ay] = wall_growing;
9842       Store[ax - 1][ay] = element;
9843       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9844
9845       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9846         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9847
9848       new_wall = TRUE;
9849     }
9850
9851     if (free_right)
9852     {
9853       Tile[ax + 1][ay] = wall_growing;
9854       Store[ax + 1][ay] = element;
9855       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9856
9857       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9858         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9859
9860       new_wall = TRUE;
9861     }
9862   }
9863
9864   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9865     TEST_DrawLevelField(ax, ay);
9866
9867   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9868     stop_top = TRUE;
9869   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9870     stop_bottom = TRUE;
9871   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9872     stop_left = TRUE;
9873   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9874     stop_right = TRUE;
9875
9876   if (((stop_top && stop_bottom) || stop_horizontal) &&
9877       ((stop_left && stop_right) || stop_vertical))
9878     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9879
9880   if (new_wall)
9881     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9882 }
9883
9884 static void CheckForDragon(int x, int y)
9885 {
9886   int i, j;
9887   boolean dragon_found = FALSE;
9888   struct XY *xy = xy_topdown;
9889
9890   for (i = 0; i < NUM_DIRECTIONS; i++)
9891   {
9892     for (j = 0; j < 4; j++)
9893     {
9894       int xx = x + j * xy[i].x;
9895       int yy = y + j * xy[i].y;
9896
9897       if (IN_LEV_FIELD(xx, yy) &&
9898           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9899       {
9900         if (Tile[xx][yy] == EL_DRAGON)
9901           dragon_found = TRUE;
9902       }
9903       else
9904         break;
9905     }
9906   }
9907
9908   if (!dragon_found)
9909   {
9910     for (i = 0; i < NUM_DIRECTIONS; i++)
9911     {
9912       for (j = 0; j < 3; j++)
9913       {
9914         int xx = x + j * xy[i].x;
9915         int yy = y + j * xy[i].y;
9916
9917         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9918         {
9919           Tile[xx][yy] = EL_EMPTY;
9920           TEST_DrawLevelField(xx, yy);
9921         }
9922         else
9923           break;
9924       }
9925     }
9926   }
9927 }
9928
9929 static void InitBuggyBase(int x, int y)
9930 {
9931   int element = Tile[x][y];
9932   int activating_delay = FRAMES_PER_SECOND / 4;
9933
9934   ChangeDelay[x][y] =
9935     (element == EL_SP_BUGGY_BASE ?
9936      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9937      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9938      activating_delay :
9939      element == EL_SP_BUGGY_BASE_ACTIVE ?
9940      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9941 }
9942
9943 static void WarnBuggyBase(int x, int y)
9944 {
9945   int i;
9946   struct XY *xy = xy_topdown;
9947
9948   for (i = 0; i < NUM_DIRECTIONS; i++)
9949   {
9950     int xx = x + xy[i].x;
9951     int yy = y + xy[i].y;
9952
9953     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9954     {
9955       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9956
9957       break;
9958     }
9959   }
9960 }
9961
9962 static void InitTrap(int x, int y)
9963 {
9964   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9965 }
9966
9967 static void ActivateTrap(int x, int y)
9968 {
9969   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9970 }
9971
9972 static void ChangeActiveTrap(int x, int y)
9973 {
9974   int graphic = IMG_TRAP_ACTIVE;
9975
9976   // if new animation frame was drawn, correct crumbled sand border
9977   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9978     TEST_DrawLevelFieldCrumbled(x, y);
9979 }
9980
9981 static int getSpecialActionElement(int element, int number, int base_element)
9982 {
9983   return (element != EL_EMPTY ? element :
9984           number != -1 ? base_element + number - 1 :
9985           EL_EMPTY);
9986 }
9987
9988 static int getModifiedActionNumber(int value_old, int operator, int operand,
9989                                    int value_min, int value_max)
9990 {
9991   int value_new = (operator == CA_MODE_SET      ? operand :
9992                    operator == CA_MODE_ADD      ? value_old + operand :
9993                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9994                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9995                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9996                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9997                    value_old);
9998
9999   return (value_new < value_min ? value_min :
10000           value_new > value_max ? value_max :
10001           value_new);
10002 }
10003
10004 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10005 {
10006   struct ElementInfo *ei = &element_info[element];
10007   struct ElementChangeInfo *change = &ei->change_page[page];
10008   int target_element = change->target_element;
10009   int action_type = change->action_type;
10010   int action_mode = change->action_mode;
10011   int action_arg = change->action_arg;
10012   int action_element = change->action_element;
10013   int i;
10014
10015   if (!change->has_action)
10016     return;
10017
10018   // ---------- determine action paramater values -----------------------------
10019
10020   int level_time_value =
10021     (level.time > 0 ? TimeLeft :
10022      TimePlayed);
10023
10024   int action_arg_element_raw =
10025     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10026      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10027      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10028      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10029      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10030      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10031      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10032      EL_EMPTY);
10033   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10034
10035   int action_arg_direction =
10036     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10037      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10038      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10039      change->actual_trigger_side :
10040      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10041      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10042      MV_NONE);
10043
10044   int action_arg_number_min =
10045     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10046      CA_ARG_MIN);
10047
10048   int action_arg_number_max =
10049     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10050      action_type == CA_SET_LEVEL_GEMS ? 999 :
10051      action_type == CA_SET_LEVEL_TIME ? 9999 :
10052      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10053      action_type == CA_SET_CE_VALUE ? 9999 :
10054      action_type == CA_SET_CE_SCORE ? 9999 :
10055      CA_ARG_MAX);
10056
10057   int action_arg_number_reset =
10058     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10059      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10060      action_type == CA_SET_LEVEL_TIME ? level.time :
10061      action_type == CA_SET_LEVEL_SCORE ? 0 :
10062      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10063      action_type == CA_SET_CE_SCORE ? 0 :
10064      0);
10065
10066   int action_arg_number =
10067     (action_arg <= CA_ARG_MAX ? action_arg :
10068      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10069      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10070      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10071      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10072      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10073      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10074      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10075      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10076      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10077      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10078      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10079      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10080      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10081      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10082      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10083      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10084      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10085      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10086      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10087      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10088      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10089      -1);
10090
10091   int action_arg_number_old =
10092     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10093      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10094      action_type == CA_SET_LEVEL_SCORE ? game.score :
10095      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10096      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10097      0);
10098
10099   int action_arg_number_new =
10100     getModifiedActionNumber(action_arg_number_old,
10101                             action_mode, action_arg_number,
10102                             action_arg_number_min, action_arg_number_max);
10103
10104   int trigger_player_bits =
10105     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10106      change->actual_trigger_player_bits : change->trigger_player);
10107
10108   int action_arg_player_bits =
10109     (action_arg >= CA_ARG_PLAYER_1 &&
10110      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10111      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10112      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10113      PLAYER_BITS_ANY);
10114
10115   // ---------- execute action  -----------------------------------------------
10116
10117   switch (action_type)
10118   {
10119     case CA_NO_ACTION:
10120     {
10121       return;
10122     }
10123
10124     // ---------- level actions  ----------------------------------------------
10125
10126     case CA_RESTART_LEVEL:
10127     {
10128       game.restart_level = TRUE;
10129
10130       break;
10131     }
10132
10133     case CA_SHOW_ENVELOPE:
10134     {
10135       int element = getSpecialActionElement(action_arg_element,
10136                                             action_arg_number, EL_ENVELOPE_1);
10137
10138       if (IS_ENVELOPE(element))
10139         local_player->show_envelope = element;
10140
10141       break;
10142     }
10143
10144     case CA_SET_LEVEL_TIME:
10145     {
10146       if (level.time > 0)       // only modify limited time value
10147       {
10148         TimeLeft = action_arg_number_new;
10149
10150         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10151
10152         DisplayGameControlValues();
10153
10154         if (!TimeLeft && game.time_limit)
10155           for (i = 0; i < MAX_PLAYERS; i++)
10156             KillPlayer(&stored_player[i]);
10157       }
10158
10159       break;
10160     }
10161
10162     case CA_SET_LEVEL_SCORE:
10163     {
10164       game.score = action_arg_number_new;
10165
10166       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10167
10168       DisplayGameControlValues();
10169
10170       break;
10171     }
10172
10173     case CA_SET_LEVEL_GEMS:
10174     {
10175       game.gems_still_needed = action_arg_number_new;
10176
10177       game.snapshot.collected_item = TRUE;
10178
10179       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10180
10181       DisplayGameControlValues();
10182
10183       break;
10184     }
10185
10186     case CA_SET_LEVEL_WIND:
10187     {
10188       game.wind_direction = action_arg_direction;
10189
10190       break;
10191     }
10192
10193     case CA_SET_LEVEL_RANDOM_SEED:
10194     {
10195       // ensure that setting a new random seed while playing is predictable
10196       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10197
10198       break;
10199     }
10200
10201     // ---------- player actions  ---------------------------------------------
10202
10203     case CA_MOVE_PLAYER:
10204     case CA_MOVE_PLAYER_NEW:
10205     {
10206       // automatically move to the next field in specified direction
10207       for (i = 0; i < MAX_PLAYERS; i++)
10208         if (trigger_player_bits & (1 << i))
10209           if (action_type == CA_MOVE_PLAYER ||
10210               stored_player[i].MovPos == 0)
10211             stored_player[i].programmed_action = action_arg_direction;
10212
10213       break;
10214     }
10215
10216     case CA_EXIT_PLAYER:
10217     {
10218       for (i = 0; i < MAX_PLAYERS; i++)
10219         if (action_arg_player_bits & (1 << i))
10220           ExitPlayer(&stored_player[i]);
10221
10222       if (game.players_still_needed == 0)
10223         LevelSolved();
10224
10225       break;
10226     }
10227
10228     case CA_KILL_PLAYER:
10229     {
10230       for (i = 0; i < MAX_PLAYERS; i++)
10231         if (action_arg_player_bits & (1 << i))
10232           KillPlayer(&stored_player[i]);
10233
10234       break;
10235     }
10236
10237     case CA_SET_PLAYER_KEYS:
10238     {
10239       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10240       int element = getSpecialActionElement(action_arg_element,
10241                                             action_arg_number, EL_KEY_1);
10242
10243       if (IS_KEY(element))
10244       {
10245         for (i = 0; i < MAX_PLAYERS; i++)
10246         {
10247           if (trigger_player_bits & (1 << i))
10248           {
10249             stored_player[i].key[KEY_NR(element)] = key_state;
10250
10251             DrawGameDoorValues();
10252           }
10253         }
10254       }
10255
10256       break;
10257     }
10258
10259     case CA_SET_PLAYER_SPEED:
10260     {
10261       for (i = 0; i < MAX_PLAYERS; i++)
10262       {
10263         if (trigger_player_bits & (1 << i))
10264         {
10265           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10266
10267           if (action_arg == CA_ARG_SPEED_FASTER &&
10268               stored_player[i].cannot_move)
10269           {
10270             action_arg_number = STEPSIZE_VERY_SLOW;
10271           }
10272           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10273                    action_arg == CA_ARG_SPEED_FASTER)
10274           {
10275             action_arg_number = 2;
10276             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10277                            CA_MODE_MULTIPLY);
10278           }
10279           else if (action_arg == CA_ARG_NUMBER_RESET)
10280           {
10281             action_arg_number = level.initial_player_stepsize[i];
10282           }
10283
10284           move_stepsize =
10285             getModifiedActionNumber(move_stepsize,
10286                                     action_mode,
10287                                     action_arg_number,
10288                                     action_arg_number_min,
10289                                     action_arg_number_max);
10290
10291           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10292         }
10293       }
10294
10295       break;
10296     }
10297
10298     case CA_SET_PLAYER_SHIELD:
10299     {
10300       for (i = 0; i < MAX_PLAYERS; i++)
10301       {
10302         if (trigger_player_bits & (1 << i))
10303         {
10304           if (action_arg == CA_ARG_SHIELD_OFF)
10305           {
10306             stored_player[i].shield_normal_time_left = 0;
10307             stored_player[i].shield_deadly_time_left = 0;
10308           }
10309           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10310           {
10311             stored_player[i].shield_normal_time_left = 999999;
10312           }
10313           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10314           {
10315             stored_player[i].shield_normal_time_left = 999999;
10316             stored_player[i].shield_deadly_time_left = 999999;
10317           }
10318         }
10319       }
10320
10321       break;
10322     }
10323
10324     case CA_SET_PLAYER_GRAVITY:
10325     {
10326       for (i = 0; i < MAX_PLAYERS; i++)
10327       {
10328         if (trigger_player_bits & (1 << i))
10329         {
10330           stored_player[i].gravity =
10331             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10332              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10333              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10334              stored_player[i].gravity);
10335         }
10336       }
10337
10338       break;
10339     }
10340
10341     case CA_SET_PLAYER_ARTWORK:
10342     {
10343       for (i = 0; i < MAX_PLAYERS; i++)
10344       {
10345         if (trigger_player_bits & (1 << i))
10346         {
10347           int artwork_element = action_arg_element;
10348
10349           if (action_arg == CA_ARG_ELEMENT_RESET)
10350             artwork_element =
10351               (level.use_artwork_element[i] ? level.artwork_element[i] :
10352                stored_player[i].element_nr);
10353
10354           if (stored_player[i].artwork_element != artwork_element)
10355             stored_player[i].Frame = 0;
10356
10357           stored_player[i].artwork_element = artwork_element;
10358
10359           SetPlayerWaiting(&stored_player[i], FALSE);
10360
10361           // set number of special actions for bored and sleeping animation
10362           stored_player[i].num_special_action_bored =
10363             get_num_special_action(artwork_element,
10364                                    ACTION_BORING_1, ACTION_BORING_LAST);
10365           stored_player[i].num_special_action_sleeping =
10366             get_num_special_action(artwork_element,
10367                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_PLAYER_INVENTORY:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377       {
10378         struct PlayerInfo *player = &stored_player[i];
10379         int j, k;
10380
10381         if (trigger_player_bits & (1 << i))
10382         {
10383           int inventory_element = action_arg_element;
10384
10385           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10386               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10387               action_arg == CA_ARG_ELEMENT_ACTION)
10388           {
10389             int element = inventory_element;
10390             int collect_count = element_info[element].collect_count_initial;
10391
10392             if (!IS_CUSTOM_ELEMENT(element))
10393               collect_count = 1;
10394
10395             if (collect_count == 0)
10396               player->inventory_infinite_element = element;
10397             else
10398               for (k = 0; k < collect_count; k++)
10399                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10400                   player->inventory_element[player->inventory_size++] =
10401                     element;
10402           }
10403           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10404                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10405                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10406           {
10407             if (player->inventory_infinite_element != EL_UNDEFINED &&
10408                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10409                                      action_arg_element_raw))
10410               player->inventory_infinite_element = EL_UNDEFINED;
10411
10412             for (k = 0, j = 0; j < player->inventory_size; j++)
10413             {
10414               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10415                                         action_arg_element_raw))
10416                 player->inventory_element[k++] = player->inventory_element[j];
10417             }
10418
10419             player->inventory_size = k;
10420           }
10421           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10422           {
10423             if (player->inventory_size > 0)
10424             {
10425               for (j = 0; j < player->inventory_size - 1; j++)
10426                 player->inventory_element[j] = player->inventory_element[j + 1];
10427
10428               player->inventory_size--;
10429             }
10430           }
10431           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10432           {
10433             if (player->inventory_size > 0)
10434               player->inventory_size--;
10435           }
10436           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10437           {
10438             player->inventory_infinite_element = EL_UNDEFINED;
10439             player->inventory_size = 0;
10440           }
10441           else if (action_arg == CA_ARG_INVENTORY_RESET)
10442           {
10443             player->inventory_infinite_element = EL_UNDEFINED;
10444             player->inventory_size = 0;
10445
10446             if (level.use_initial_inventory[i])
10447             {
10448               for (j = 0; j < level.initial_inventory_size[i]; j++)
10449               {
10450                 int element = level.initial_inventory_content[i][j];
10451                 int collect_count = element_info[element].collect_count_initial;
10452
10453                 if (!IS_CUSTOM_ELEMENT(element))
10454                   collect_count = 1;
10455
10456                 if (collect_count == 0)
10457                   player->inventory_infinite_element = element;
10458                 else
10459                   for (k = 0; k < collect_count; k++)
10460                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10461                       player->inventory_element[player->inventory_size++] =
10462                         element;
10463               }
10464             }
10465           }
10466         }
10467       }
10468
10469       break;
10470     }
10471
10472     // ---------- CE actions  -------------------------------------------------
10473
10474     case CA_SET_CE_VALUE:
10475     {
10476       int last_ce_value = CustomValue[x][y];
10477
10478       CustomValue[x][y] = action_arg_number_new;
10479
10480       if (CustomValue[x][y] != last_ce_value)
10481       {
10482         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10483         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10484
10485         if (CustomValue[x][y] == 0)
10486         {
10487           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10488           ChangeCount[x][y] = 0;        // allow at least one more change
10489
10490           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10491           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10492         }
10493       }
10494
10495       break;
10496     }
10497
10498     case CA_SET_CE_SCORE:
10499     {
10500       int last_ce_score = ei->collect_score;
10501
10502       ei->collect_score = action_arg_number_new;
10503
10504       if (ei->collect_score != last_ce_score)
10505       {
10506         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10507         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10508
10509         if (ei->collect_score == 0)
10510         {
10511           int xx, yy;
10512
10513           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10514           ChangeCount[x][y] = 0;        // allow at least one more change
10515
10516           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10517           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10518
10519           /*
10520             This is a very special case that seems to be a mixture between
10521             CheckElementChange() and CheckTriggeredElementChange(): while
10522             the first one only affects single elements that are triggered
10523             directly, the second one affects multiple elements in the playfield
10524             that are triggered indirectly by another element. This is a third
10525             case: Changing the CE score always affects multiple identical CEs,
10526             so every affected CE must be checked, not only the single CE for
10527             which the CE score was changed in the first place (as every instance
10528             of that CE shares the same CE score, and therefore also can change)!
10529           */
10530           SCAN_PLAYFIELD(xx, yy)
10531           {
10532             if (Tile[xx][yy] == element)
10533               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10534                                  CE_SCORE_GETS_ZERO);
10535           }
10536         }
10537       }
10538
10539       break;
10540     }
10541
10542     case CA_SET_CE_ARTWORK:
10543     {
10544       int artwork_element = action_arg_element;
10545       boolean reset_frame = FALSE;
10546       int xx, yy;
10547
10548       if (action_arg == CA_ARG_ELEMENT_RESET)
10549         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10550                            element);
10551
10552       if (ei->gfx_element != artwork_element)
10553         reset_frame = TRUE;
10554
10555       ei->gfx_element = artwork_element;
10556
10557       SCAN_PLAYFIELD(xx, yy)
10558       {
10559         if (Tile[xx][yy] == element)
10560         {
10561           if (reset_frame)
10562           {
10563             ResetGfxAnimation(xx, yy);
10564             ResetRandomAnimationValue(xx, yy);
10565           }
10566
10567           TEST_DrawLevelField(xx, yy);
10568         }
10569       }
10570
10571       break;
10572     }
10573
10574     // ---------- engine actions  ---------------------------------------------
10575
10576     case CA_SET_ENGINE_SCAN_MODE:
10577     {
10578       InitPlayfieldScanMode(action_arg);
10579
10580       break;
10581     }
10582
10583     default:
10584       break;
10585   }
10586 }
10587
10588 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10589 {
10590   int old_element = Tile[x][y];
10591   int new_element = GetElementFromGroupElement(element);
10592   int previous_move_direction = MovDir[x][y];
10593   int last_ce_value = CustomValue[x][y];
10594   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10595   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10596   boolean add_player_onto_element = (new_element_is_player &&
10597                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10598                                      IS_WALKABLE(old_element));
10599
10600   if (!add_player_onto_element)
10601   {
10602     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10603       RemoveMovingField(x, y);
10604     else
10605       RemoveField(x, y);
10606
10607     Tile[x][y] = new_element;
10608
10609     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10610       MovDir[x][y] = previous_move_direction;
10611
10612     if (element_info[new_element].use_last_ce_value)
10613       CustomValue[x][y] = last_ce_value;
10614
10615     InitField_WithBug1(x, y, FALSE);
10616
10617     new_element = Tile[x][y];   // element may have changed
10618
10619     ResetGfxAnimation(x, y);
10620     ResetRandomAnimationValue(x, y);
10621
10622     TEST_DrawLevelField(x, y);
10623
10624     if (GFX_CRUMBLED(new_element))
10625       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10626   }
10627
10628   // check if element under the player changes from accessible to unaccessible
10629   // (needed for special case of dropping element which then changes)
10630   // (must be checked after creating new element for walkable group elements)
10631   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10632       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10633   {
10634     Bang(x, y);
10635
10636     return;
10637   }
10638
10639   // "ChangeCount" not set yet to allow "entered by player" change one time
10640   if (new_element_is_player)
10641     RelocatePlayer(x, y, new_element);
10642
10643   if (is_change)
10644     ChangeCount[x][y]++;        // count number of changes in the same frame
10645
10646   TestIfBadThingTouchesPlayer(x, y);
10647   TestIfPlayerTouchesCustomElement(x, y);
10648   TestIfElementTouchesCustomElement(x, y);
10649 }
10650
10651 static void CreateField(int x, int y, int element)
10652 {
10653   CreateFieldExt(x, y, element, FALSE);
10654 }
10655
10656 static void CreateElementFromChange(int x, int y, int element)
10657 {
10658   element = GET_VALID_RUNTIME_ELEMENT(element);
10659
10660   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10661   {
10662     int old_element = Tile[x][y];
10663
10664     // prevent changed element from moving in same engine frame
10665     // unless both old and new element can either fall or move
10666     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10667         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10668       Stop[x][y] = TRUE;
10669   }
10670
10671   CreateFieldExt(x, y, element, TRUE);
10672 }
10673
10674 static boolean ChangeElement(int x, int y, int element, int page)
10675 {
10676   struct ElementInfo *ei = &element_info[element];
10677   struct ElementChangeInfo *change = &ei->change_page[page];
10678   int ce_value = CustomValue[x][y];
10679   int ce_score = ei->collect_score;
10680   int target_element;
10681   int old_element = Tile[x][y];
10682
10683   // always use default change event to prevent running into a loop
10684   if (ChangeEvent[x][y] == -1)
10685     ChangeEvent[x][y] = CE_DELAY;
10686
10687   if (ChangeEvent[x][y] == CE_DELAY)
10688   {
10689     // reset actual trigger element, trigger player and action element
10690     change->actual_trigger_element = EL_EMPTY;
10691     change->actual_trigger_player = EL_EMPTY;
10692     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10693     change->actual_trigger_side = CH_SIDE_NONE;
10694     change->actual_trigger_ce_value = 0;
10695     change->actual_trigger_ce_score = 0;
10696   }
10697
10698   // do not change elements more than a specified maximum number of changes
10699   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10700     return FALSE;
10701
10702   ChangeCount[x][y]++;          // count number of changes in the same frame
10703
10704   if (change->explode)
10705   {
10706     Bang(x, y);
10707
10708     return TRUE;
10709   }
10710
10711   if (change->use_target_content)
10712   {
10713     boolean complete_replace = TRUE;
10714     boolean can_replace[3][3];
10715     int xx, yy;
10716
10717     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10718     {
10719       boolean is_empty;
10720       boolean is_walkable;
10721       boolean is_diggable;
10722       boolean is_collectible;
10723       boolean is_removable;
10724       boolean is_destructible;
10725       int ex = x + xx - 1;
10726       int ey = y + yy - 1;
10727       int content_element = change->target_content.e[xx][yy];
10728       int e;
10729
10730       can_replace[xx][yy] = TRUE;
10731
10732       if (ex == x && ey == y)   // do not check changing element itself
10733         continue;
10734
10735       if (content_element == EL_EMPTY_SPACE)
10736       {
10737         can_replace[xx][yy] = FALSE;    // do not replace border with space
10738
10739         continue;
10740       }
10741
10742       if (!IN_LEV_FIELD(ex, ey))
10743       {
10744         can_replace[xx][yy] = FALSE;
10745         complete_replace = FALSE;
10746
10747         continue;
10748       }
10749
10750       e = Tile[ex][ey];
10751
10752       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10753         e = MovingOrBlocked2Element(ex, ey);
10754
10755       is_empty = (IS_FREE(ex, ey) ||
10756                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10757
10758       is_walkable     = (is_empty || IS_WALKABLE(e));
10759       is_diggable     = (is_empty || IS_DIGGABLE(e));
10760       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10761       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10762       is_removable    = (is_diggable || is_collectible);
10763
10764       can_replace[xx][yy] =
10765         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10766           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10767           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10768           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10769           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10770           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10771          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10772
10773       if (!can_replace[xx][yy])
10774         complete_replace = FALSE;
10775     }
10776
10777     if (!change->only_if_complete || complete_replace)
10778     {
10779       boolean something_has_changed = FALSE;
10780
10781       if (change->only_if_complete && change->use_random_replace &&
10782           RND(100) < change->random_percentage)
10783         return FALSE;
10784
10785       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10786       {
10787         int ex = x + xx - 1;
10788         int ey = y + yy - 1;
10789         int content_element;
10790
10791         if (can_replace[xx][yy] && (!change->use_random_replace ||
10792                                     RND(100) < change->random_percentage))
10793         {
10794           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10795             RemoveMovingField(ex, ey);
10796
10797           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10798
10799           content_element = change->target_content.e[xx][yy];
10800           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10801                                               ce_value, ce_score);
10802
10803           CreateElementFromChange(ex, ey, target_element);
10804
10805           something_has_changed = TRUE;
10806
10807           // for symmetry reasons, freeze newly created border elements
10808           if (ex != x || ey != y)
10809             Stop[ex][ey] = TRUE;        // no more moving in this frame
10810         }
10811       }
10812
10813       if (something_has_changed)
10814       {
10815         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10816         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10817       }
10818     }
10819   }
10820   else
10821   {
10822     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10823                                         ce_value, ce_score);
10824
10825     if (element == EL_DIAGONAL_GROWING ||
10826         element == EL_DIAGONAL_SHRINKING)
10827     {
10828       target_element = Store[x][y];
10829
10830       Store[x][y] = EL_EMPTY;
10831     }
10832
10833     // special case: element changes to player (and may be kept if walkable)
10834     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10835       CreateElementFromChange(x, y, EL_EMPTY);
10836
10837     CreateElementFromChange(x, y, target_element);
10838
10839     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10840     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10841   }
10842
10843   // this uses direct change before indirect change
10844   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10845
10846   return TRUE;
10847 }
10848
10849 static void HandleElementChange(int x, int y, int page)
10850 {
10851   int element = MovingOrBlocked2Element(x, y);
10852   struct ElementInfo *ei = &element_info[element];
10853   struct ElementChangeInfo *change = &ei->change_page[page];
10854   boolean handle_action_before_change = FALSE;
10855
10856 #ifdef DEBUG
10857   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10858       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10859   {
10860     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10861           x, y, element, element_info[element].token_name);
10862     Debug("game:playing:HandleElementChange", "This should never happen!");
10863   }
10864 #endif
10865
10866   // this can happen with classic bombs on walkable, changing elements
10867   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10868   {
10869     return;
10870   }
10871
10872   if (ChangeDelay[x][y] == 0)           // initialize element change
10873   {
10874     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10875
10876     if (change->can_change)
10877     {
10878       // !!! not clear why graphic animation should be reset at all here !!!
10879       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10880       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10881
10882       /*
10883         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10884
10885         When using an animation frame delay of 1 (this only happens with
10886         "sp_zonk.moving.left/right" in the classic graphics), the default
10887         (non-moving) animation shows wrong animation frames (while the
10888         moving animation, like "sp_zonk.moving.left/right", is correct,
10889         so this graphical bug never shows up with the classic graphics).
10890         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10891         be drawn instead of the correct frames 0,1,2,3. This is caused by
10892         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10893         an element change: First when the change delay ("ChangeDelay[][]")
10894         counter has reached zero after decrementing, then a second time in
10895         the next frame (after "GfxFrame[][]" was already incremented) when
10896         "ChangeDelay[][]" is reset to the initial delay value again.
10897
10898         This causes frame 0 to be drawn twice, while the last frame won't
10899         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10900
10901         As some animations may already be cleverly designed around this bug
10902         (at least the "Snake Bite" snake tail animation does this), it cannot
10903         simply be fixed here without breaking such existing animations.
10904         Unfortunately, it cannot easily be detected if a graphics set was
10905         designed "before" or "after" the bug was fixed. As a workaround,
10906         a new graphics set option "game.graphics_engine_version" was added
10907         to be able to specify the game's major release version for which the
10908         graphics set was designed, which can then be used to decide if the
10909         bugfix should be used (version 4 and above) or not (version 3 or
10910         below, or if no version was specified at all, as with old sets).
10911
10912         (The wrong/fixed animation frames can be tested with the test level set
10913         "test_gfxframe" and level "000", which contains a specially prepared
10914         custom element at level position (x/y) == (11/9) which uses the zonk
10915         animation mentioned above. Using "game.graphics_engine_version: 4"
10916         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10917         This can also be seen from the debug output for this test element.)
10918       */
10919
10920       // when a custom element is about to change (for example by change delay),
10921       // do not reset graphic animation when the custom element is moving
10922       if (game.graphics_engine_version < 4 &&
10923           !IS_MOVING(x, y))
10924       {
10925         ResetGfxAnimation(x, y);
10926         ResetRandomAnimationValue(x, y);
10927       }
10928
10929       if (change->pre_change_function)
10930         change->pre_change_function(x, y);
10931     }
10932   }
10933
10934   ChangeDelay[x][y]--;
10935
10936   if (ChangeDelay[x][y] != 0)           // continue element change
10937   {
10938     if (change->can_change)
10939     {
10940       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10941
10942       if (IS_ANIMATED(graphic))
10943         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10944
10945       if (change->change_function)
10946         change->change_function(x, y);
10947     }
10948   }
10949   else                                  // finish element change
10950   {
10951     if (ChangePage[x][y] != -1)         // remember page from delayed change
10952     {
10953       page = ChangePage[x][y];
10954       ChangePage[x][y] = -1;
10955
10956       change = &ei->change_page[page];
10957     }
10958
10959     if (IS_MOVING(x, y))                // never change a running system ;-)
10960     {
10961       ChangeDelay[x][y] = 1;            // try change after next move step
10962       ChangePage[x][y] = page;          // remember page to use for change
10963
10964       return;
10965     }
10966
10967     // special case: set new level random seed before changing element
10968     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10969       handle_action_before_change = TRUE;
10970
10971     if (change->has_action && handle_action_before_change)
10972       ExecuteCustomElementAction(x, y, element, page);
10973
10974     if (change->can_change)
10975     {
10976       if (ChangeElement(x, y, element, page))
10977       {
10978         if (change->post_change_function)
10979           change->post_change_function(x, y);
10980       }
10981     }
10982
10983     if (change->has_action && !handle_action_before_change)
10984       ExecuteCustomElementAction(x, y, element, page);
10985   }
10986 }
10987
10988 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10989                                               int trigger_element,
10990                                               int trigger_event,
10991                                               int trigger_player,
10992                                               int trigger_side,
10993                                               int trigger_page)
10994 {
10995   boolean change_done_any = FALSE;
10996   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10997   int i;
10998
10999   if (!(trigger_events[trigger_element][trigger_event]))
11000     return FALSE;
11001
11002   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11003
11004   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11005   {
11006     int element = EL_CUSTOM_START + i;
11007     boolean change_done = FALSE;
11008     int p;
11009
11010     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11011         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11012       continue;
11013
11014     for (p = 0; p < element_info[element].num_change_pages; p++)
11015     {
11016       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11017
11018       if (change->can_change_or_has_action &&
11019           change->has_event[trigger_event] &&
11020           change->trigger_side & trigger_side &&
11021           change->trigger_player & trigger_player &&
11022           change->trigger_page & trigger_page_bits &&
11023           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11024       {
11025         change->actual_trigger_element = trigger_element;
11026         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11027         change->actual_trigger_player_bits = trigger_player;
11028         change->actual_trigger_side = trigger_side;
11029         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11030         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11031
11032         if ((change->can_change && !change_done) || change->has_action)
11033         {
11034           int x, y;
11035
11036           SCAN_PLAYFIELD(x, y)
11037           {
11038             if (Tile[x][y] == element)
11039             {
11040               if (change->can_change && !change_done)
11041               {
11042                 // if element already changed in this frame, not only prevent
11043                 // another element change (checked in ChangeElement()), but
11044                 // also prevent additional element actions for this element
11045
11046                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11047                     !level.use_action_after_change_bug)
11048                   continue;
11049
11050                 ChangeDelay[x][y] = 1;
11051                 ChangeEvent[x][y] = trigger_event;
11052
11053                 HandleElementChange(x, y, p);
11054               }
11055               else if (change->has_action)
11056               {
11057                 // if element already changed in this frame, not only prevent
11058                 // another element change (checked in ChangeElement()), but
11059                 // also prevent additional element actions for this element
11060
11061                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11062                     !level.use_action_after_change_bug)
11063                   continue;
11064
11065                 ExecuteCustomElementAction(x, y, element, p);
11066                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11067               }
11068             }
11069           }
11070
11071           if (change->can_change)
11072           {
11073             change_done = TRUE;
11074             change_done_any = TRUE;
11075           }
11076         }
11077       }
11078     }
11079   }
11080
11081   RECURSION_LOOP_DETECTION_END();
11082
11083   return change_done_any;
11084 }
11085
11086 static boolean CheckElementChangeExt(int x, int y,
11087                                      int element,
11088                                      int trigger_element,
11089                                      int trigger_event,
11090                                      int trigger_player,
11091                                      int trigger_side)
11092 {
11093   boolean change_done = FALSE;
11094   int p;
11095
11096   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11097       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11098     return FALSE;
11099
11100   if (Tile[x][y] == EL_BLOCKED)
11101   {
11102     Blocked2Moving(x, y, &x, &y);
11103     element = Tile[x][y];
11104   }
11105
11106   // check if element has already changed or is about to change after moving
11107   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11108        Tile[x][y] != element) ||
11109
11110       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11111        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11112         ChangePage[x][y] != -1)))
11113     return FALSE;
11114
11115   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11116
11117   for (p = 0; p < element_info[element].num_change_pages; p++)
11118   {
11119     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11120
11121     /* check trigger element for all events where the element that is checked
11122        for changing interacts with a directly adjacent element -- this is
11123        different to element changes that affect other elements to change on the
11124        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11125     boolean check_trigger_element =
11126       (trigger_event == CE_NEXT_TO_X ||
11127        trigger_event == CE_TOUCHING_X ||
11128        trigger_event == CE_HITTING_X ||
11129        trigger_event == CE_HIT_BY_X ||
11130        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11131
11132     if (change->can_change_or_has_action &&
11133         change->has_event[trigger_event] &&
11134         change->trigger_side & trigger_side &&
11135         change->trigger_player & trigger_player &&
11136         (!check_trigger_element ||
11137          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11138     {
11139       change->actual_trigger_element = trigger_element;
11140       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11141       change->actual_trigger_player_bits = trigger_player;
11142       change->actual_trigger_side = trigger_side;
11143       change->actual_trigger_ce_value = CustomValue[x][y];
11144       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11145
11146       // special case: trigger element not at (x,y) position for some events
11147       if (check_trigger_element)
11148       {
11149         static struct
11150         {
11151           int dx, dy;
11152         } move_xy[] =
11153           {
11154             {  0,  0 },
11155             { -1,  0 },
11156             { +1,  0 },
11157             {  0,  0 },
11158             {  0, -1 },
11159             {  0,  0 }, { 0, 0 }, { 0, 0 },
11160             {  0, +1 }
11161           };
11162
11163         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11164         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11165
11166         change->actual_trigger_ce_value = CustomValue[xx][yy];
11167         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11168       }
11169
11170       if (change->can_change && !change_done)
11171       {
11172         ChangeDelay[x][y] = 1;
11173         ChangeEvent[x][y] = trigger_event;
11174
11175         HandleElementChange(x, y, p);
11176
11177         change_done = TRUE;
11178       }
11179       else if (change->has_action)
11180       {
11181         ExecuteCustomElementAction(x, y, element, p);
11182         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11183       }
11184     }
11185   }
11186
11187   RECURSION_LOOP_DETECTION_END();
11188
11189   return change_done;
11190 }
11191
11192 static void PlayPlayerSound(struct PlayerInfo *player)
11193 {
11194   int jx = player->jx, jy = player->jy;
11195   int sound_element = player->artwork_element;
11196   int last_action = player->last_action_waiting;
11197   int action = player->action_waiting;
11198
11199   if (player->is_waiting)
11200   {
11201     if (action != last_action)
11202       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11203     else
11204       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11205   }
11206   else
11207   {
11208     if (action != last_action)
11209       StopSound(element_info[sound_element].sound[last_action]);
11210
11211     if (last_action == ACTION_SLEEPING)
11212       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11213   }
11214 }
11215
11216 static void PlayAllPlayersSound(void)
11217 {
11218   int i;
11219
11220   for (i = 0; i < MAX_PLAYERS; i++)
11221     if (stored_player[i].active)
11222       PlayPlayerSound(&stored_player[i]);
11223 }
11224
11225 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11226 {
11227   boolean last_waiting = player->is_waiting;
11228   int move_dir = player->MovDir;
11229
11230   player->dir_waiting = move_dir;
11231   player->last_action_waiting = player->action_waiting;
11232
11233   if (is_waiting)
11234   {
11235     if (!last_waiting)          // not waiting -> waiting
11236     {
11237       player->is_waiting = TRUE;
11238
11239       player->frame_counter_bored =
11240         FrameCounter +
11241         game.player_boring_delay_fixed +
11242         GetSimpleRandom(game.player_boring_delay_random);
11243       player->frame_counter_sleeping =
11244         FrameCounter +
11245         game.player_sleeping_delay_fixed +
11246         GetSimpleRandom(game.player_sleeping_delay_random);
11247
11248       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11249     }
11250
11251     if (game.player_sleeping_delay_fixed +
11252         game.player_sleeping_delay_random > 0 &&
11253         player->anim_delay_counter == 0 &&
11254         player->post_delay_counter == 0 &&
11255         FrameCounter >= player->frame_counter_sleeping)
11256       player->is_sleeping = TRUE;
11257     else if (game.player_boring_delay_fixed +
11258              game.player_boring_delay_random > 0 &&
11259              FrameCounter >= player->frame_counter_bored)
11260       player->is_bored = TRUE;
11261
11262     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11263                               player->is_bored ? ACTION_BORING :
11264                               ACTION_WAITING);
11265
11266     if (player->is_sleeping && player->use_murphy)
11267     {
11268       // special case for sleeping Murphy when leaning against non-free tile
11269
11270       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11271           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11272            !IS_MOVING(player->jx - 1, player->jy)))
11273         move_dir = MV_LEFT;
11274       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11275                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11276                 !IS_MOVING(player->jx + 1, player->jy)))
11277         move_dir = MV_RIGHT;
11278       else
11279         player->is_sleeping = FALSE;
11280
11281       player->dir_waiting = move_dir;
11282     }
11283
11284     if (player->is_sleeping)
11285     {
11286       if (player->num_special_action_sleeping > 0)
11287       {
11288         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11289         {
11290           int last_special_action = player->special_action_sleeping;
11291           int num_special_action = player->num_special_action_sleeping;
11292           int special_action =
11293             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11294              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11295              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11296              last_special_action + 1 : ACTION_SLEEPING);
11297           int special_graphic =
11298             el_act_dir2img(player->artwork_element, special_action, move_dir);
11299
11300           player->anim_delay_counter =
11301             graphic_info[special_graphic].anim_delay_fixed +
11302             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11303           player->post_delay_counter =
11304             graphic_info[special_graphic].post_delay_fixed +
11305             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11306
11307           player->special_action_sleeping = special_action;
11308         }
11309
11310         if (player->anim_delay_counter > 0)
11311         {
11312           player->action_waiting = player->special_action_sleeping;
11313           player->anim_delay_counter--;
11314         }
11315         else if (player->post_delay_counter > 0)
11316         {
11317           player->post_delay_counter--;
11318         }
11319       }
11320     }
11321     else if (player->is_bored)
11322     {
11323       if (player->num_special_action_bored > 0)
11324       {
11325         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11326         {
11327           int special_action =
11328             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11329           int special_graphic =
11330             el_act_dir2img(player->artwork_element, special_action, move_dir);
11331
11332           player->anim_delay_counter =
11333             graphic_info[special_graphic].anim_delay_fixed +
11334             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11335           player->post_delay_counter =
11336             graphic_info[special_graphic].post_delay_fixed +
11337             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11338
11339           player->special_action_bored = special_action;
11340         }
11341
11342         if (player->anim_delay_counter > 0)
11343         {
11344           player->action_waiting = player->special_action_bored;
11345           player->anim_delay_counter--;
11346         }
11347         else if (player->post_delay_counter > 0)
11348         {
11349           player->post_delay_counter--;
11350         }
11351       }
11352     }
11353   }
11354   else if (last_waiting)        // waiting -> not waiting
11355   {
11356     player->is_waiting = FALSE;
11357     player->is_bored = FALSE;
11358     player->is_sleeping = FALSE;
11359
11360     player->frame_counter_bored = -1;
11361     player->frame_counter_sleeping = -1;
11362
11363     player->anim_delay_counter = 0;
11364     player->post_delay_counter = 0;
11365
11366     player->dir_waiting = player->MovDir;
11367     player->action_waiting = ACTION_DEFAULT;
11368
11369     player->special_action_bored = ACTION_DEFAULT;
11370     player->special_action_sleeping = ACTION_DEFAULT;
11371   }
11372 }
11373
11374 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11375 {
11376   if ((!player->is_moving  && player->was_moving) ||
11377       (player->MovPos == 0 && player->was_moving) ||
11378       (player->is_snapping && !player->was_snapping) ||
11379       (player->is_dropping && !player->was_dropping))
11380   {
11381     if (!CheckSaveEngineSnapshotToList())
11382       return;
11383
11384     player->was_moving = FALSE;
11385     player->was_snapping = TRUE;
11386     player->was_dropping = TRUE;
11387   }
11388   else
11389   {
11390     if (player->is_moving)
11391       player->was_moving = TRUE;
11392
11393     if (!player->is_snapping)
11394       player->was_snapping = FALSE;
11395
11396     if (!player->is_dropping)
11397       player->was_dropping = FALSE;
11398   }
11399
11400   static struct MouseActionInfo mouse_action_last = { 0 };
11401   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11402   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11403
11404   if (new_released)
11405     CheckSaveEngineSnapshotToList();
11406
11407   mouse_action_last = mouse_action;
11408 }
11409
11410 static void CheckSingleStepMode(struct PlayerInfo *player)
11411 {
11412   if (tape.single_step && tape.recording && !tape.pausing)
11413   {
11414     // as it is called "single step mode", just return to pause mode when the
11415     // player stopped moving after one tile (or never starts moving at all)
11416     // (reverse logic needed here in case single step mode used in team mode)
11417     if (player->is_moving ||
11418         player->is_pushing ||
11419         player->is_dropping_pressed ||
11420         player->effective_mouse_action.button)
11421       game.enter_single_step_mode = FALSE;
11422   }
11423
11424   CheckSaveEngineSnapshot(player);
11425 }
11426
11427 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11428 {
11429   int left      = player_action & JOY_LEFT;
11430   int right     = player_action & JOY_RIGHT;
11431   int up        = player_action & JOY_UP;
11432   int down      = player_action & JOY_DOWN;
11433   int button1   = player_action & JOY_BUTTON_1;
11434   int button2   = player_action & JOY_BUTTON_2;
11435   int dx        = (left ? -1 : right ? 1 : 0);
11436   int dy        = (up   ? -1 : down  ? 1 : 0);
11437
11438   if (!player->active || tape.pausing)
11439     return 0;
11440
11441   if (player_action)
11442   {
11443     if (button1)
11444       SnapField(player, dx, dy);
11445     else
11446     {
11447       if (button2)
11448         DropElement(player);
11449
11450       MovePlayer(player, dx, dy);
11451     }
11452
11453     CheckSingleStepMode(player);
11454
11455     SetPlayerWaiting(player, FALSE);
11456
11457     return player_action;
11458   }
11459   else
11460   {
11461     // no actions for this player (no input at player's configured device)
11462
11463     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11464     SnapField(player, 0, 0);
11465     CheckGravityMovementWhenNotMoving(player);
11466
11467     if (player->MovPos == 0)
11468       SetPlayerWaiting(player, TRUE);
11469
11470     if (player->MovPos == 0)    // needed for tape.playing
11471       player->is_moving = FALSE;
11472
11473     player->is_dropping = FALSE;
11474     player->is_dropping_pressed = FALSE;
11475     player->drop_pressed_delay = 0;
11476
11477     CheckSingleStepMode(player);
11478
11479     return 0;
11480   }
11481 }
11482
11483 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11484                                          byte *tape_action)
11485 {
11486   if (!tape.use_mouse_actions)
11487     return;
11488
11489   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11490   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11491   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11492 }
11493
11494 static void SetTapeActionFromMouseAction(byte *tape_action,
11495                                          struct MouseActionInfo *mouse_action)
11496 {
11497   if (!tape.use_mouse_actions)
11498     return;
11499
11500   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11501   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11502   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11503 }
11504
11505 static void CheckLevelSolved(void)
11506 {
11507   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11508   {
11509     if (game_em.level_solved &&
11510         !game_em.game_over)                             // game won
11511     {
11512       LevelSolved();
11513
11514       game_em.game_over = TRUE;
11515
11516       game.all_players_gone = TRUE;
11517     }
11518
11519     if (game_em.game_over)                              // game lost
11520       game.all_players_gone = TRUE;
11521   }
11522   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11523   {
11524     if (game_sp.level_solved &&
11525         !game_sp.game_over)                             // game won
11526     {
11527       LevelSolved();
11528
11529       game_sp.game_over = TRUE;
11530
11531       game.all_players_gone = TRUE;
11532     }
11533
11534     if (game_sp.game_over)                              // game lost
11535       game.all_players_gone = TRUE;
11536   }
11537   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11538   {
11539     if (game_mm.level_solved &&
11540         !game_mm.game_over)                             // game won
11541     {
11542       LevelSolved();
11543
11544       game_mm.game_over = TRUE;
11545
11546       game.all_players_gone = TRUE;
11547     }
11548
11549     if (game_mm.game_over)                              // game lost
11550       game.all_players_gone = TRUE;
11551   }
11552 }
11553
11554 static void CheckLevelTime_StepCounter(void)
11555 {
11556   int i;
11557
11558   TimePlayed++;
11559
11560   if (TimeLeft > 0)
11561   {
11562     TimeLeft--;
11563
11564     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11565       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11566
11567     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11568
11569     DisplayGameControlValues();
11570
11571     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11572       for (i = 0; i < MAX_PLAYERS; i++)
11573         KillPlayer(&stored_player[i]);
11574   }
11575   else if (game.no_level_time_limit && !game.all_players_gone)
11576   {
11577     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11578
11579     DisplayGameControlValues();
11580   }
11581 }
11582
11583 static void CheckLevelTime(void)
11584 {
11585   int i;
11586
11587   if (TimeFrames >= FRAMES_PER_SECOND)
11588   {
11589     TimeFrames = 0;
11590     TapeTime++;
11591
11592     for (i = 0; i < MAX_PLAYERS; i++)
11593     {
11594       struct PlayerInfo *player = &stored_player[i];
11595
11596       if (SHIELD_ON(player))
11597       {
11598         player->shield_normal_time_left--;
11599
11600         if (player->shield_deadly_time_left > 0)
11601           player->shield_deadly_time_left--;
11602       }
11603     }
11604
11605     if (!game.LevelSolved && !level.use_step_counter)
11606     {
11607       TimePlayed++;
11608
11609       if (TimeLeft > 0)
11610       {
11611         TimeLeft--;
11612
11613         if (TimeLeft <= 10 && game.time_limit)
11614           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11615
11616         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11617            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11618
11619         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11620
11621         if (!TimeLeft && game.time_limit)
11622         {
11623           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11624             game_em.lev->killed_out_of_time = TRUE;
11625           else
11626             for (i = 0; i < MAX_PLAYERS; i++)
11627               KillPlayer(&stored_player[i]);
11628         }
11629       }
11630       else if (game.no_level_time_limit && !game.all_players_gone)
11631       {
11632         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11633       }
11634
11635       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11636     }
11637
11638     if (tape.recording || tape.playing)
11639       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11640   }
11641
11642   if (tape.recording || tape.playing)
11643     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11644
11645   UpdateAndDisplayGameControlValues();
11646 }
11647
11648 void AdvanceFrameAndPlayerCounters(int player_nr)
11649 {
11650   int i;
11651
11652   // advance frame counters (global frame counter and time frame counter)
11653   FrameCounter++;
11654   TimeFrames++;
11655
11656   // advance player counters (counters for move delay, move animation etc.)
11657   for (i = 0; i < MAX_PLAYERS; i++)
11658   {
11659     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11660     int move_delay_value = stored_player[i].move_delay_value;
11661     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11662
11663     if (!advance_player_counters)       // not all players may be affected
11664       continue;
11665
11666     if (move_frames == 0)       // less than one move per game frame
11667     {
11668       int stepsize = TILEX / move_delay_value;
11669       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11670       int count = (stored_player[i].is_moving ?
11671                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11672
11673       if (count % delay == 0)
11674         move_frames = 1;
11675     }
11676
11677     stored_player[i].Frame += move_frames;
11678
11679     if (stored_player[i].MovPos != 0)
11680       stored_player[i].StepFrame += move_frames;
11681
11682     if (stored_player[i].move_delay > 0)
11683       stored_player[i].move_delay--;
11684
11685     // due to bugs in previous versions, counter must count up, not down
11686     if (stored_player[i].push_delay != -1)
11687       stored_player[i].push_delay++;
11688
11689     if (stored_player[i].drop_delay > 0)
11690       stored_player[i].drop_delay--;
11691
11692     if (stored_player[i].is_dropping_pressed)
11693       stored_player[i].drop_pressed_delay++;
11694   }
11695 }
11696
11697 void AdvanceFrameCounter(void)
11698 {
11699   FrameCounter++;
11700 }
11701
11702 void AdvanceGfxFrame(void)
11703 {
11704   int x, y;
11705
11706   SCAN_PLAYFIELD(x, y)
11707   {
11708     GfxFrame[x][y]++;
11709   }
11710 }
11711
11712 void StartGameActions(boolean init_network_game, boolean record_tape,
11713                       int random_seed)
11714 {
11715   unsigned int new_random_seed = InitRND(random_seed);
11716
11717   if (record_tape)
11718     TapeStartRecording(new_random_seed);
11719
11720   if (setup.auto_pause_on_start && !tape.pausing)
11721     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11722
11723   if (init_network_game)
11724   {
11725     SendToServer_LevelFile();
11726     SendToServer_StartPlaying();
11727
11728     return;
11729   }
11730
11731   InitGame();
11732 }
11733
11734 static void GameActionsExt(void)
11735 {
11736 #if 0
11737   static unsigned int game_frame_delay = 0;
11738 #endif
11739   unsigned int game_frame_delay_value;
11740   byte *recorded_player_action;
11741   byte summarized_player_action = 0;
11742   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11743   int i;
11744
11745   // detect endless loops, caused by custom element programming
11746   if (recursion_loop_detected && recursion_loop_depth == 0)
11747   {
11748     char *message = getStringCat3("Internal Error! Element ",
11749                                   EL_NAME(recursion_loop_element),
11750                                   " caused endless loop! Quit the game?");
11751
11752     Warn("element '%s' caused endless loop in game engine",
11753          EL_NAME(recursion_loop_element));
11754
11755     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11756
11757     recursion_loop_detected = FALSE;    // if game should be continued
11758
11759     free(message);
11760
11761     return;
11762   }
11763
11764   if (game.restart_level)
11765     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11766
11767   CheckLevelSolved();
11768
11769   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11770     GameWon();
11771
11772   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11773     TapeStop();
11774
11775   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11776     return;
11777
11778   game_frame_delay_value =
11779     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11780
11781   if (tape.playing && tape.warp_forward && !tape.pausing)
11782     game_frame_delay_value = 0;
11783
11784   SetVideoFrameDelay(game_frame_delay_value);
11785
11786   // (de)activate virtual buttons depending on current game status
11787   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11788   {
11789     if (game.all_players_gone)  // if no players there to be controlled anymore
11790       SetOverlayActive(FALSE);
11791     else if (!tape.playing)     // if game continues after tape stopped playing
11792       SetOverlayActive(TRUE);
11793   }
11794
11795 #if 0
11796 #if 0
11797   // ---------- main game synchronization point ----------
11798
11799   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11800
11801   Debug("game:playing:skip", "skip == %d", skip);
11802
11803 #else
11804   // ---------- main game synchronization point ----------
11805
11806   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11807 #endif
11808 #endif
11809
11810   if (network_playing && !network_player_action_received)
11811   {
11812     // try to get network player actions in time
11813
11814     // last chance to get network player actions without main loop delay
11815     HandleNetworking();
11816
11817     // game was quit by network peer
11818     if (game_status != GAME_MODE_PLAYING)
11819       return;
11820
11821     // check if network player actions still missing and game still running
11822     if (!network_player_action_received && !checkGameEnded())
11823       return;           // failed to get network player actions in time
11824
11825     // do not yet reset "network_player_action_received" (for tape.pausing)
11826   }
11827
11828   if (tape.pausing)
11829     return;
11830
11831   // at this point we know that we really continue executing the game
11832
11833   network_player_action_received = FALSE;
11834
11835   // when playing tape, read previously recorded player input from tape data
11836   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11837
11838   local_player->effective_mouse_action = local_player->mouse_action;
11839
11840   if (recorded_player_action != NULL)
11841     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11842                                  recorded_player_action);
11843
11844   // TapePlayAction() may return NULL when toggling to "pause before death"
11845   if (tape.pausing)
11846     return;
11847
11848   if (tape.set_centered_player)
11849   {
11850     game.centered_player_nr_next = tape.centered_player_nr_next;
11851     game.set_centered_player = TRUE;
11852   }
11853
11854   for (i = 0; i < MAX_PLAYERS; i++)
11855   {
11856     summarized_player_action |= stored_player[i].action;
11857
11858     if (!network_playing && (game.team_mode || tape.playing))
11859       stored_player[i].effective_action = stored_player[i].action;
11860   }
11861
11862   if (network_playing && !checkGameEnded())
11863     SendToServer_MovePlayer(summarized_player_action);
11864
11865   // summarize all actions at local players mapped input device position
11866   // (this allows using different input devices in single player mode)
11867   if (!network.enabled && !game.team_mode)
11868     stored_player[map_player_action[local_player->index_nr]].effective_action =
11869       summarized_player_action;
11870
11871   // summarize all actions at centered player in local team mode
11872   if (tape.recording &&
11873       setup.team_mode && !network.enabled &&
11874       setup.input_on_focus &&
11875       game.centered_player_nr != -1)
11876   {
11877     for (i = 0; i < MAX_PLAYERS; i++)
11878       stored_player[map_player_action[i]].effective_action =
11879         (i == game.centered_player_nr ? summarized_player_action : 0);
11880   }
11881
11882   if (recorded_player_action != NULL)
11883     for (i = 0; i < MAX_PLAYERS; i++)
11884       stored_player[i].effective_action = recorded_player_action[i];
11885
11886   for (i = 0; i < MAX_PLAYERS; i++)
11887   {
11888     tape_action[i] = stored_player[i].effective_action;
11889
11890     /* (this may happen in the RND game engine if a player was not present on
11891        the playfield on level start, but appeared later from a custom element */
11892     if (setup.team_mode &&
11893         tape.recording &&
11894         tape_action[i] &&
11895         !tape.player_participates[i])
11896       tape.player_participates[i] = TRUE;
11897   }
11898
11899   SetTapeActionFromMouseAction(tape_action,
11900                                &local_player->effective_mouse_action);
11901
11902   // only record actions from input devices, but not programmed actions
11903   if (tape.recording)
11904     TapeRecordAction(tape_action);
11905
11906   // remember if game was played (especially after tape stopped playing)
11907   if (!tape.playing && summarized_player_action && !checkGameFailed())
11908     game.GamePlayed = TRUE;
11909
11910 #if USE_NEW_PLAYER_ASSIGNMENTS
11911   // !!! also map player actions in single player mode !!!
11912   // if (game.team_mode)
11913   if (1)
11914   {
11915     byte mapped_action[MAX_PLAYERS];
11916
11917 #if DEBUG_PLAYER_ACTIONS
11918     for (i = 0; i < MAX_PLAYERS; i++)
11919       DebugContinued("", "%d, ", stored_player[i].effective_action);
11920 #endif
11921
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11924
11925     for (i = 0; i < MAX_PLAYERS; i++)
11926       stored_player[i].effective_action = mapped_action[i];
11927
11928 #if DEBUG_PLAYER_ACTIONS
11929     DebugContinued("", "=> ");
11930     for (i = 0; i < MAX_PLAYERS; i++)
11931       DebugContinued("", "%d, ", stored_player[i].effective_action);
11932     DebugContinued("game:playing:player", "\n");
11933 #endif
11934   }
11935 #if DEBUG_PLAYER_ACTIONS
11936   else
11937   {
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       DebugContinued("", "%d, ", stored_player[i].effective_action);
11940     DebugContinued("game:playing:player", "\n");
11941   }
11942 #endif
11943 #endif
11944
11945   for (i = 0; i < MAX_PLAYERS; i++)
11946   {
11947     // allow engine snapshot in case of changed movement attempt
11948     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11949         (stored_player[i].effective_action & KEY_MOTION))
11950       game.snapshot.changed_action = TRUE;
11951
11952     // allow engine snapshot in case of snapping/dropping attempt
11953     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11954         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11955       game.snapshot.changed_action = TRUE;
11956
11957     game.snapshot.last_action[i] = stored_player[i].effective_action;
11958   }
11959
11960   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11961   {
11962     GameActions_EM_Main();
11963   }
11964   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11965   {
11966     GameActions_SP_Main();
11967   }
11968   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11969   {
11970     GameActions_MM_Main();
11971   }
11972   else
11973   {
11974     GameActions_RND_Main();
11975   }
11976
11977   BlitScreenToBitmap(backbuffer);
11978
11979   CheckLevelSolved();
11980   CheckLevelTime();
11981
11982   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11983
11984   if (global.show_frames_per_second)
11985   {
11986     static unsigned int fps_counter = 0;
11987     static int fps_frames = 0;
11988     unsigned int fps_delay_ms = Counter() - fps_counter;
11989
11990     fps_frames++;
11991
11992     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11993     {
11994       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11995
11996       fps_frames = 0;
11997       fps_counter = Counter();
11998
11999       // always draw FPS to screen after FPS value was updated
12000       redraw_mask |= REDRAW_FPS;
12001     }
12002
12003     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12004     if (GetDrawDeactivationMask() == REDRAW_NONE)
12005       redraw_mask |= REDRAW_FPS;
12006   }
12007 }
12008
12009 static void GameActions_CheckSaveEngineSnapshot(void)
12010 {
12011   if (!game.snapshot.save_snapshot)
12012     return;
12013
12014   // clear flag for saving snapshot _before_ saving snapshot
12015   game.snapshot.save_snapshot = FALSE;
12016
12017   SaveEngineSnapshotToList();
12018 }
12019
12020 void GameActions(void)
12021 {
12022   GameActionsExt();
12023
12024   GameActions_CheckSaveEngineSnapshot();
12025 }
12026
12027 void GameActions_EM_Main(void)
12028 {
12029   byte effective_action[MAX_PLAYERS];
12030   int i;
12031
12032   for (i = 0; i < MAX_PLAYERS; i++)
12033     effective_action[i] = stored_player[i].effective_action;
12034
12035   GameActions_EM(effective_action);
12036 }
12037
12038 void GameActions_SP_Main(void)
12039 {
12040   byte effective_action[MAX_PLAYERS];
12041   int i;
12042
12043   for (i = 0; i < MAX_PLAYERS; i++)
12044     effective_action[i] = stored_player[i].effective_action;
12045
12046   GameActions_SP(effective_action);
12047
12048   for (i = 0; i < MAX_PLAYERS; i++)
12049   {
12050     if (stored_player[i].force_dropping)
12051       stored_player[i].action |= KEY_BUTTON_DROP;
12052
12053     stored_player[i].force_dropping = FALSE;
12054   }
12055 }
12056
12057 void GameActions_MM_Main(void)
12058 {
12059   AdvanceGfxFrame();
12060
12061   GameActions_MM(local_player->effective_mouse_action);
12062 }
12063
12064 void GameActions_RND_Main(void)
12065 {
12066   GameActions_RND();
12067 }
12068
12069 void GameActions_RND(void)
12070 {
12071   static struct MouseActionInfo mouse_action_last = { 0 };
12072   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12073   int magic_wall_x = 0, magic_wall_y = 0;
12074   int i, x, y, element, graphic, last_gfx_frame;
12075
12076   InitPlayfieldScanModeVars();
12077
12078   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12079   {
12080     SCAN_PLAYFIELD(x, y)
12081     {
12082       ChangeCount[x][y] = 0;
12083       ChangeEvent[x][y] = -1;
12084     }
12085   }
12086
12087   if (game.set_centered_player)
12088   {
12089     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12090
12091     // switching to "all players" only possible if all players fit to screen
12092     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12093     {
12094       game.centered_player_nr_next = game.centered_player_nr;
12095       game.set_centered_player = FALSE;
12096     }
12097
12098     // do not switch focus to non-existing (or non-active) player
12099     if (game.centered_player_nr_next >= 0 &&
12100         !stored_player[game.centered_player_nr_next].active)
12101     {
12102       game.centered_player_nr_next = game.centered_player_nr;
12103       game.set_centered_player = FALSE;
12104     }
12105   }
12106
12107   if (game.set_centered_player &&
12108       ScreenMovPos == 0)        // screen currently aligned at tile position
12109   {
12110     int sx, sy;
12111
12112     if (game.centered_player_nr_next == -1)
12113     {
12114       setScreenCenteredToAllPlayers(&sx, &sy);
12115     }
12116     else
12117     {
12118       sx = stored_player[game.centered_player_nr_next].jx;
12119       sy = stored_player[game.centered_player_nr_next].jy;
12120     }
12121
12122     game.centered_player_nr = game.centered_player_nr_next;
12123     game.set_centered_player = FALSE;
12124
12125     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12126     DrawGameDoorValues();
12127   }
12128
12129   // check single step mode (set flag and clear again if any player is active)
12130   game.enter_single_step_mode =
12131     (tape.single_step && tape.recording && !tape.pausing);
12132
12133   for (i = 0; i < MAX_PLAYERS; i++)
12134   {
12135     int actual_player_action = stored_player[i].effective_action;
12136
12137 #if 1
12138     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12139        - rnd_equinox_tetrachloride 048
12140        - rnd_equinox_tetrachloride_ii 096
12141        - rnd_emanuel_schmieg 002
12142        - doctor_sloan_ww 001, 020
12143     */
12144     if (stored_player[i].MovPos == 0)
12145       CheckGravityMovement(&stored_player[i]);
12146 #endif
12147
12148     // overwrite programmed action with tape action
12149     if (stored_player[i].programmed_action)
12150       actual_player_action = stored_player[i].programmed_action;
12151
12152     PlayerActions(&stored_player[i], actual_player_action);
12153
12154     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12155   }
12156
12157   // single step pause mode may already have been toggled by "ScrollPlayer()"
12158   if (game.enter_single_step_mode && !tape.pausing)
12159     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12160
12161   ScrollScreen(NULL, SCROLL_GO_ON);
12162
12163   /* for backwards compatibility, the following code emulates a fixed bug that
12164      occured when pushing elements (causing elements that just made their last
12165      pushing step to already (if possible) make their first falling step in the
12166      same game frame, which is bad); this code is also needed to use the famous
12167      "spring push bug" which is used in older levels and might be wanted to be
12168      used also in newer levels, but in this case the buggy pushing code is only
12169      affecting the "spring" element and no other elements */
12170
12171   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12172   {
12173     for (i = 0; i < MAX_PLAYERS; i++)
12174     {
12175       struct PlayerInfo *player = &stored_player[i];
12176       int x = player->jx;
12177       int y = player->jy;
12178
12179       if (player->active && player->is_pushing && player->is_moving &&
12180           IS_MOVING(x, y) &&
12181           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12182            Tile[x][y] == EL_SPRING))
12183       {
12184         ContinueMoving(x, y);
12185
12186         // continue moving after pushing (this is actually a bug)
12187         if (!IS_MOVING(x, y))
12188           Stop[x][y] = FALSE;
12189       }
12190     }
12191   }
12192
12193   SCAN_PLAYFIELD(x, y)
12194   {
12195     Last[x][y] = Tile[x][y];
12196
12197     ChangeCount[x][y] = 0;
12198     ChangeEvent[x][y] = -1;
12199
12200     // this must be handled before main playfield loop
12201     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12202     {
12203       MovDelay[x][y]--;
12204       if (MovDelay[x][y] <= 0)
12205         RemoveField(x, y);
12206     }
12207
12208     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12209     {
12210       MovDelay[x][y]--;
12211       if (MovDelay[x][y] <= 0)
12212       {
12213         int element = Store[x][y];
12214         int move_direction = MovDir[x][y];
12215         int player_index_bit = Store2[x][y];
12216
12217         Store[x][y] = 0;
12218         Store2[x][y] = 0;
12219
12220         RemoveField(x, y);
12221         TEST_DrawLevelField(x, y);
12222
12223         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12224
12225         if (IS_ENVELOPE(element))
12226           local_player->show_envelope = element;
12227       }
12228     }
12229
12230 #if DEBUG
12231     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12232     {
12233       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12234             x, y);
12235       Debug("game:playing:GameActions_RND", "This should never happen!");
12236
12237       ChangePage[x][y] = -1;
12238     }
12239 #endif
12240
12241     Stop[x][y] = FALSE;
12242     if (WasJustMoving[x][y] > 0)
12243       WasJustMoving[x][y]--;
12244     if (WasJustFalling[x][y] > 0)
12245       WasJustFalling[x][y]--;
12246     if (CheckCollision[x][y] > 0)
12247       CheckCollision[x][y]--;
12248     if (CheckImpact[x][y] > 0)
12249       CheckImpact[x][y]--;
12250
12251     GfxFrame[x][y]++;
12252
12253     /* reset finished pushing action (not done in ContinueMoving() to allow
12254        continuous pushing animation for elements with zero push delay) */
12255     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12256     {
12257       ResetGfxAnimation(x, y);
12258       TEST_DrawLevelField(x, y);
12259     }
12260
12261 #if DEBUG
12262     if (IS_BLOCKED(x, y))
12263     {
12264       int oldx, oldy;
12265
12266       Blocked2Moving(x, y, &oldx, &oldy);
12267       if (!IS_MOVING(oldx, oldy))
12268       {
12269         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12270         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12271         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12272         Debug("game:playing:GameActions_RND", "This should never happen!");
12273       }
12274     }
12275 #endif
12276   }
12277
12278   if (mouse_action.button)
12279   {
12280     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12281     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12282
12283     x = mouse_action.lx;
12284     y = mouse_action.ly;
12285     element = Tile[x][y];
12286
12287     if (new_button)
12288     {
12289       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12290       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12291                                          ch_button);
12292     }
12293
12294     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12295     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12296                                        ch_button);
12297
12298     if (level.use_step_counter)
12299     {
12300       boolean counted_click = FALSE;
12301
12302       // element clicked that can change when clicked/pressed
12303       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12304           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12305            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12306         counted_click = TRUE;
12307
12308       // element clicked that can trigger change when clicked/pressed
12309       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12310           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12311         counted_click = TRUE;
12312
12313       if (new_button && counted_click)
12314         CheckLevelTime_StepCounter();
12315     }
12316   }
12317
12318   SCAN_PLAYFIELD(x, y)
12319   {
12320     element = Tile[x][y];
12321     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12322     last_gfx_frame = GfxFrame[x][y];
12323
12324     if (element == EL_EMPTY)
12325       graphic = el2img(GfxElementEmpty[x][y]);
12326
12327     ResetGfxFrame(x, y);
12328
12329     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12330       DrawLevelGraphicAnimation(x, y, graphic);
12331
12332     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12333         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12334       ResetRandomAnimationValue(x, y);
12335
12336     SetRandomAnimationValue(x, y);
12337
12338     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12339
12340     if (IS_INACTIVE(element))
12341     {
12342       if (IS_ANIMATED(graphic))
12343         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12344
12345       continue;
12346     }
12347
12348     // this may take place after moving, so 'element' may have changed
12349     if (IS_CHANGING(x, y) &&
12350         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12351     {
12352       int page = element_info[element].event_page_nr[CE_DELAY];
12353
12354       HandleElementChange(x, y, page);
12355
12356       element = Tile[x][y];
12357       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12358     }
12359
12360     CheckNextToConditions(x, y);
12361
12362     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12363     {
12364       StartMoving(x, y);
12365
12366       element = Tile[x][y];
12367       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12368
12369       if (IS_ANIMATED(graphic) &&
12370           !IS_MOVING(x, y) &&
12371           !Stop[x][y])
12372         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12373
12374       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12375         TEST_DrawTwinkleOnField(x, y);
12376     }
12377     else if (element == EL_ACID)
12378     {
12379       if (!Stop[x][y])
12380         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12381     }
12382     else if ((element == EL_EXIT_OPEN ||
12383               element == EL_EM_EXIT_OPEN ||
12384               element == EL_SP_EXIT_OPEN ||
12385               element == EL_STEEL_EXIT_OPEN ||
12386               element == EL_EM_STEEL_EXIT_OPEN ||
12387               element == EL_SP_TERMINAL ||
12388               element == EL_SP_TERMINAL_ACTIVE ||
12389               element == EL_EXTRA_TIME ||
12390               element == EL_SHIELD_NORMAL ||
12391               element == EL_SHIELD_DEADLY) &&
12392              IS_ANIMATED(graphic))
12393       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12394     else if (IS_MOVING(x, y))
12395       ContinueMoving(x, y);
12396     else if (IS_ACTIVE_BOMB(element))
12397       CheckDynamite(x, y);
12398     else if (element == EL_AMOEBA_GROWING)
12399       AmoebaGrowing(x, y);
12400     else if (element == EL_AMOEBA_SHRINKING)
12401       AmoebaShrinking(x, y);
12402
12403 #if !USE_NEW_AMOEBA_CODE
12404     else if (IS_AMOEBALIVE(element))
12405       AmoebaReproduce(x, y);
12406 #endif
12407
12408     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12409       Life(x, y);
12410     else if (element == EL_EXIT_CLOSED)
12411       CheckExit(x, y);
12412     else if (element == EL_EM_EXIT_CLOSED)
12413       CheckExitEM(x, y);
12414     else if (element == EL_STEEL_EXIT_CLOSED)
12415       CheckExitSteel(x, y);
12416     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12417       CheckExitSteelEM(x, y);
12418     else if (element == EL_SP_EXIT_CLOSED)
12419       CheckExitSP(x, y);
12420     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12421              element == EL_EXPANDABLE_STEELWALL_GROWING)
12422       WallGrowing(x, y);
12423     else if (element == EL_EXPANDABLE_WALL ||
12424              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12425              element == EL_EXPANDABLE_WALL_VERTICAL ||
12426              element == EL_EXPANDABLE_WALL_ANY ||
12427              element == EL_BD_EXPANDABLE_WALL ||
12428              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12429              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12430              element == EL_EXPANDABLE_STEELWALL_ANY)
12431       CheckWallGrowing(x, y);
12432     else if (element == EL_FLAMES)
12433       CheckForDragon(x, y);
12434     else if (element == EL_EXPLOSION)
12435       ; // drawing of correct explosion animation is handled separately
12436     else if (element == EL_ELEMENT_SNAPPING ||
12437              element == EL_DIAGONAL_SHRINKING ||
12438              element == EL_DIAGONAL_GROWING)
12439     {
12440       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12441
12442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12443     }
12444     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12445       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12446
12447     if (IS_BELT_ACTIVE(element))
12448       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12449
12450     if (game.magic_wall_active)
12451     {
12452       int jx = local_player->jx, jy = local_player->jy;
12453
12454       // play the element sound at the position nearest to the player
12455       if ((element == EL_MAGIC_WALL_FULL ||
12456            element == EL_MAGIC_WALL_ACTIVE ||
12457            element == EL_MAGIC_WALL_EMPTYING ||
12458            element == EL_BD_MAGIC_WALL_FULL ||
12459            element == EL_BD_MAGIC_WALL_ACTIVE ||
12460            element == EL_BD_MAGIC_WALL_EMPTYING ||
12461            element == EL_DC_MAGIC_WALL_FULL ||
12462            element == EL_DC_MAGIC_WALL_ACTIVE ||
12463            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12464           ABS(x - jx) + ABS(y - jy) <
12465           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12466       {
12467         magic_wall_x = x;
12468         magic_wall_y = y;
12469       }
12470     }
12471   }
12472
12473 #if USE_NEW_AMOEBA_CODE
12474   // new experimental amoeba growth stuff
12475   if (!(FrameCounter % 8))
12476   {
12477     static unsigned int random = 1684108901;
12478
12479     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12480     {
12481       x = RND(lev_fieldx);
12482       y = RND(lev_fieldy);
12483       element = Tile[x][y];
12484
12485       if (!IS_PLAYER(x,y) &&
12486           (element == EL_EMPTY ||
12487            CAN_GROW_INTO(element) ||
12488            element == EL_QUICKSAND_EMPTY ||
12489            element == EL_QUICKSAND_FAST_EMPTY ||
12490            element == EL_ACID_SPLASH_LEFT ||
12491            element == EL_ACID_SPLASH_RIGHT))
12492       {
12493         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12494             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12495             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12496             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12497           Tile[x][y] = EL_AMOEBA_DROP;
12498       }
12499
12500       random = random * 129 + 1;
12501     }
12502   }
12503 #endif
12504
12505   game.explosions_delayed = FALSE;
12506
12507   SCAN_PLAYFIELD(x, y)
12508   {
12509     element = Tile[x][y];
12510
12511     if (ExplodeField[x][y])
12512       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12513     else if (element == EL_EXPLOSION)
12514       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12515
12516     ExplodeField[x][y] = EX_TYPE_NONE;
12517   }
12518
12519   game.explosions_delayed = TRUE;
12520
12521   if (game.magic_wall_active)
12522   {
12523     if (!(game.magic_wall_time_left % 4))
12524     {
12525       int element = Tile[magic_wall_x][magic_wall_y];
12526
12527       if (element == EL_BD_MAGIC_WALL_FULL ||
12528           element == EL_BD_MAGIC_WALL_ACTIVE ||
12529           element == EL_BD_MAGIC_WALL_EMPTYING)
12530         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12531       else if (element == EL_DC_MAGIC_WALL_FULL ||
12532                element == EL_DC_MAGIC_WALL_ACTIVE ||
12533                element == EL_DC_MAGIC_WALL_EMPTYING)
12534         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12535       else
12536         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12537     }
12538
12539     if (game.magic_wall_time_left > 0)
12540     {
12541       game.magic_wall_time_left--;
12542
12543       if (!game.magic_wall_time_left)
12544       {
12545         SCAN_PLAYFIELD(x, y)
12546         {
12547           element = Tile[x][y];
12548
12549           if (element == EL_MAGIC_WALL_ACTIVE ||
12550               element == EL_MAGIC_WALL_FULL)
12551           {
12552             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12553             TEST_DrawLevelField(x, y);
12554           }
12555           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12556                    element == EL_BD_MAGIC_WALL_FULL)
12557           {
12558             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12559             TEST_DrawLevelField(x, y);
12560           }
12561           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12562                    element == EL_DC_MAGIC_WALL_FULL)
12563           {
12564             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12565             TEST_DrawLevelField(x, y);
12566           }
12567         }
12568
12569         game.magic_wall_active = FALSE;
12570       }
12571     }
12572   }
12573
12574   if (game.light_time_left > 0)
12575   {
12576     game.light_time_left--;
12577
12578     if (game.light_time_left == 0)
12579       RedrawAllLightSwitchesAndInvisibleElements();
12580   }
12581
12582   if (game.timegate_time_left > 0)
12583   {
12584     game.timegate_time_left--;
12585
12586     if (game.timegate_time_left == 0)
12587       CloseAllOpenTimegates();
12588   }
12589
12590   if (game.lenses_time_left > 0)
12591   {
12592     game.lenses_time_left--;
12593
12594     if (game.lenses_time_left == 0)
12595       RedrawAllInvisibleElementsForLenses();
12596   }
12597
12598   if (game.magnify_time_left > 0)
12599   {
12600     game.magnify_time_left--;
12601
12602     if (game.magnify_time_left == 0)
12603       RedrawAllInvisibleElementsForMagnifier();
12604   }
12605
12606   for (i = 0; i < MAX_PLAYERS; i++)
12607   {
12608     struct PlayerInfo *player = &stored_player[i];
12609
12610     if (SHIELD_ON(player))
12611     {
12612       if (player->shield_deadly_time_left)
12613         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12614       else if (player->shield_normal_time_left)
12615         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12616     }
12617   }
12618
12619 #if USE_DELAYED_GFX_REDRAW
12620   SCAN_PLAYFIELD(x, y)
12621   {
12622     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12623     {
12624       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12625          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12626
12627       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12628         DrawLevelField(x, y);
12629
12630       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12631         DrawLevelFieldCrumbled(x, y);
12632
12633       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12634         DrawLevelFieldCrumbledNeighbours(x, y);
12635
12636       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12637         DrawTwinkleOnField(x, y);
12638     }
12639
12640     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12641   }
12642 #endif
12643
12644   DrawAllPlayers();
12645   PlayAllPlayersSound();
12646
12647   for (i = 0; i < MAX_PLAYERS; i++)
12648   {
12649     struct PlayerInfo *player = &stored_player[i];
12650
12651     if (player->show_envelope != 0 && (!player->active ||
12652                                        player->MovPos == 0))
12653     {
12654       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12655
12656       player->show_envelope = 0;
12657     }
12658   }
12659
12660   // use random number generator in every frame to make it less predictable
12661   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12662     RND(1);
12663
12664   mouse_action_last = mouse_action;
12665 }
12666
12667 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12668 {
12669   int min_x = x, min_y = y, max_x = x, max_y = y;
12670   int scr_fieldx = getScreenFieldSizeX();
12671   int scr_fieldy = getScreenFieldSizeY();
12672   int i;
12673
12674   for (i = 0; i < MAX_PLAYERS; i++)
12675   {
12676     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12677
12678     if (!stored_player[i].active || &stored_player[i] == player)
12679       continue;
12680
12681     min_x = MIN(min_x, jx);
12682     min_y = MIN(min_y, jy);
12683     max_x = MAX(max_x, jx);
12684     max_y = MAX(max_y, jy);
12685   }
12686
12687   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12688 }
12689
12690 static boolean AllPlayersInVisibleScreen(void)
12691 {
12692   int i;
12693
12694   for (i = 0; i < MAX_PLAYERS; i++)
12695   {
12696     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12697
12698     if (!stored_player[i].active)
12699       continue;
12700
12701     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12702       return FALSE;
12703   }
12704
12705   return TRUE;
12706 }
12707
12708 void ScrollLevel(int dx, int dy)
12709 {
12710   int scroll_offset = 2 * TILEX_VAR;
12711   int x, y;
12712
12713   BlitBitmap(drawto_field, drawto_field,
12714              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12715              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12716              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12717              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12718              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12719              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12720
12721   if (dx != 0)
12722   {
12723     x = (dx == 1 ? BX1 : BX2);
12724     for (y = BY1; y <= BY2; y++)
12725       DrawScreenField(x, y);
12726   }
12727
12728   if (dy != 0)
12729   {
12730     y = (dy == 1 ? BY1 : BY2);
12731     for (x = BX1; x <= BX2; x++)
12732       DrawScreenField(x, y);
12733   }
12734
12735   redraw_mask |= REDRAW_FIELD;
12736 }
12737
12738 static boolean canFallDown(struct PlayerInfo *player)
12739 {
12740   int jx = player->jx, jy = player->jy;
12741
12742   return (IN_LEV_FIELD(jx, jy + 1) &&
12743           (IS_FREE(jx, jy + 1) ||
12744            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12745           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12746           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12747 }
12748
12749 static boolean canPassField(int x, int y, int move_dir)
12750 {
12751   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12752   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12753   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12754   int nextx = x + dx;
12755   int nexty = y + dy;
12756   int element = Tile[x][y];
12757
12758   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12759           !CAN_MOVE(element) &&
12760           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12761           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12762           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12763 }
12764
12765 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12766 {
12767   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12768   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12769   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12770   int newx = x + dx;
12771   int newy = y + dy;
12772
12773   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12774           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12775           (IS_DIGGABLE(Tile[newx][newy]) ||
12776            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12777            canPassField(newx, newy, move_dir)));
12778 }
12779
12780 static void CheckGravityMovement(struct PlayerInfo *player)
12781 {
12782   if (player->gravity && !player->programmed_action)
12783   {
12784     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12785     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12786     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12787     int jx = player->jx, jy = player->jy;
12788     boolean player_is_moving_to_valid_field =
12789       (!player_is_snapping &&
12790        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12791         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12792     boolean player_can_fall_down = canFallDown(player);
12793
12794     if (player_can_fall_down &&
12795         !player_is_moving_to_valid_field)
12796       player->programmed_action = MV_DOWN;
12797   }
12798 }
12799
12800 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12801 {
12802   return CheckGravityMovement(player);
12803
12804   if (player->gravity && !player->programmed_action)
12805   {
12806     int jx = player->jx, jy = player->jy;
12807     boolean field_under_player_is_free =
12808       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12809     boolean player_is_standing_on_valid_field =
12810       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12811        (IS_WALKABLE(Tile[jx][jy]) &&
12812         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12813
12814     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12815       player->programmed_action = MV_DOWN;
12816   }
12817 }
12818
12819 /*
12820   MovePlayerOneStep()
12821   -----------------------------------------------------------------------------
12822   dx, dy:               direction (non-diagonal) to try to move the player to
12823   real_dx, real_dy:     direction as read from input device (can be diagonal)
12824 */
12825
12826 boolean MovePlayerOneStep(struct PlayerInfo *player,
12827                           int dx, int dy, int real_dx, int real_dy)
12828 {
12829   int jx = player->jx, jy = player->jy;
12830   int new_jx = jx + dx, new_jy = jy + dy;
12831   int can_move;
12832   boolean player_can_move = !player->cannot_move;
12833
12834   if (!player->active || (!dx && !dy))
12835     return MP_NO_ACTION;
12836
12837   player->MovDir = (dx < 0 ? MV_LEFT :
12838                     dx > 0 ? MV_RIGHT :
12839                     dy < 0 ? MV_UP :
12840                     dy > 0 ? MV_DOWN :  MV_NONE);
12841
12842   if (!IN_LEV_FIELD(new_jx, new_jy))
12843     return MP_NO_ACTION;
12844
12845   if (!player_can_move)
12846   {
12847     if (player->MovPos == 0)
12848     {
12849       player->is_moving = FALSE;
12850       player->is_digging = FALSE;
12851       player->is_collecting = FALSE;
12852       player->is_snapping = FALSE;
12853       player->is_pushing = FALSE;
12854     }
12855   }
12856
12857   if (!network.enabled && game.centered_player_nr == -1 &&
12858       !AllPlayersInSight(player, new_jx, new_jy))
12859     return MP_NO_ACTION;
12860
12861   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12862   if (can_move != MP_MOVING)
12863     return can_move;
12864
12865   // check if DigField() has caused relocation of the player
12866   if (player->jx != jx || player->jy != jy)
12867     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12868
12869   StorePlayer[jx][jy] = 0;
12870   player->last_jx = jx;
12871   player->last_jy = jy;
12872   player->jx = new_jx;
12873   player->jy = new_jy;
12874   StorePlayer[new_jx][new_jy] = player->element_nr;
12875
12876   if (player->move_delay_value_next != -1)
12877   {
12878     player->move_delay_value = player->move_delay_value_next;
12879     player->move_delay_value_next = -1;
12880   }
12881
12882   player->MovPos =
12883     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12884
12885   player->step_counter++;
12886
12887   PlayerVisit[jx][jy] = FrameCounter;
12888
12889   player->is_moving = TRUE;
12890
12891 #if 1
12892   // should better be called in MovePlayer(), but this breaks some tapes
12893   ScrollPlayer(player, SCROLL_INIT);
12894 #endif
12895
12896   return MP_MOVING;
12897 }
12898
12899 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12900 {
12901   int jx = player->jx, jy = player->jy;
12902   int old_jx = jx, old_jy = jy;
12903   int moved = MP_NO_ACTION;
12904
12905   if (!player->active)
12906     return FALSE;
12907
12908   if (!dx && !dy)
12909   {
12910     if (player->MovPos == 0)
12911     {
12912       player->is_moving = FALSE;
12913       player->is_digging = FALSE;
12914       player->is_collecting = FALSE;
12915       player->is_snapping = FALSE;
12916       player->is_pushing = FALSE;
12917     }
12918
12919     return FALSE;
12920   }
12921
12922   if (player->move_delay > 0)
12923     return FALSE;
12924
12925   player->move_delay = -1;              // set to "uninitialized" value
12926
12927   // store if player is automatically moved to next field
12928   player->is_auto_moving = (player->programmed_action != MV_NONE);
12929
12930   // remove the last programmed player action
12931   player->programmed_action = 0;
12932
12933   if (player->MovPos)
12934   {
12935     // should only happen if pre-1.2 tape recordings are played
12936     // this is only for backward compatibility
12937
12938     int original_move_delay_value = player->move_delay_value;
12939
12940 #if DEBUG
12941     Debug("game:playing:MovePlayer",
12942           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12943           tape.counter);
12944 #endif
12945
12946     // scroll remaining steps with finest movement resolution
12947     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12948
12949     while (player->MovPos)
12950     {
12951       ScrollPlayer(player, SCROLL_GO_ON);
12952       ScrollScreen(NULL, SCROLL_GO_ON);
12953
12954       AdvanceFrameAndPlayerCounters(player->index_nr);
12955
12956       DrawAllPlayers();
12957       BackToFront_WithFrameDelay(0);
12958     }
12959
12960     player->move_delay_value = original_move_delay_value;
12961   }
12962
12963   player->is_active = FALSE;
12964
12965   if (player->last_move_dir & MV_HORIZONTAL)
12966   {
12967     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12968       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12969   }
12970   else
12971   {
12972     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12973       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12974   }
12975
12976   if (!moved && !player->is_active)
12977   {
12978     player->is_moving = FALSE;
12979     player->is_digging = FALSE;
12980     player->is_collecting = FALSE;
12981     player->is_snapping = FALSE;
12982     player->is_pushing = FALSE;
12983   }
12984
12985   jx = player->jx;
12986   jy = player->jy;
12987
12988   if (moved & MP_MOVING && !ScreenMovPos &&
12989       (player->index_nr == game.centered_player_nr ||
12990        game.centered_player_nr == -1))
12991   {
12992     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12993
12994     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12995     {
12996       // actual player has left the screen -- scroll in that direction
12997       if (jx != old_jx)         // player has moved horizontally
12998         scroll_x += (jx - old_jx);
12999       else                      // player has moved vertically
13000         scroll_y += (jy - old_jy);
13001     }
13002     else
13003     {
13004       int offset_raw = game.scroll_delay_value;
13005
13006       if (jx != old_jx)         // player has moved horizontally
13007       {
13008         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13009         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13010         int new_scroll_x = jx - MIDPOSX + offset_x;
13011
13012         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13013             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13014           scroll_x = new_scroll_x;
13015
13016         // don't scroll over playfield boundaries
13017         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13018
13019         // don't scroll more than one field at a time
13020         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13021
13022         // don't scroll against the player's moving direction
13023         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13024             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13025           scroll_x = old_scroll_x;
13026       }
13027       else                      // player has moved vertically
13028       {
13029         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13030         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13031         int new_scroll_y = jy - MIDPOSY + offset_y;
13032
13033         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13034             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13035           scroll_y = new_scroll_y;
13036
13037         // don't scroll over playfield boundaries
13038         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13039
13040         // don't scroll more than one field at a time
13041         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13042
13043         // don't scroll against the player's moving direction
13044         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13045             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13046           scroll_y = old_scroll_y;
13047       }
13048     }
13049
13050     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13051     {
13052       if (!network.enabled && game.centered_player_nr == -1 &&
13053           !AllPlayersInVisibleScreen())
13054       {
13055         scroll_x = old_scroll_x;
13056         scroll_y = old_scroll_y;
13057       }
13058       else
13059       {
13060         ScrollScreen(player, SCROLL_INIT);
13061         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13062       }
13063     }
13064   }
13065
13066   player->StepFrame = 0;
13067
13068   if (moved & MP_MOVING)
13069   {
13070     if (old_jx != jx && old_jy == jy)
13071       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13072     else if (old_jx == jx && old_jy != jy)
13073       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13074
13075     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13076
13077     player->last_move_dir = player->MovDir;
13078     player->is_moving = TRUE;
13079     player->is_snapping = FALSE;
13080     player->is_switching = FALSE;
13081     player->is_dropping = FALSE;
13082     player->is_dropping_pressed = FALSE;
13083     player->drop_pressed_delay = 0;
13084
13085 #if 0
13086     // should better be called here than above, but this breaks some tapes
13087     ScrollPlayer(player, SCROLL_INIT);
13088 #endif
13089   }
13090   else
13091   {
13092     CheckGravityMovementWhenNotMoving(player);
13093
13094     player->is_moving = FALSE;
13095
13096     /* at this point, the player is allowed to move, but cannot move right now
13097        (e.g. because of something blocking the way) -- ensure that the player
13098        is also allowed to move in the next frame (in old versions before 3.1.1,
13099        the player was forced to wait again for eight frames before next try) */
13100
13101     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13102       player->move_delay = 0;   // allow direct movement in the next frame
13103   }
13104
13105   if (player->move_delay == -1)         // not yet initialized by DigField()
13106     player->move_delay = player->move_delay_value;
13107
13108   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13109   {
13110     TestIfPlayerTouchesBadThing(jx, jy);
13111     TestIfPlayerTouchesCustomElement(jx, jy);
13112   }
13113
13114   if (!player->active)
13115     RemovePlayer(player);
13116
13117   return moved;
13118 }
13119
13120 void ScrollPlayer(struct PlayerInfo *player, int mode)
13121 {
13122   int jx = player->jx, jy = player->jy;
13123   int last_jx = player->last_jx, last_jy = player->last_jy;
13124   int move_stepsize = TILEX / player->move_delay_value;
13125
13126   if (!player->active)
13127     return;
13128
13129   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13130     return;
13131
13132   if (mode == SCROLL_INIT)
13133   {
13134     player->actual_frame_counter.count = FrameCounter;
13135     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13136
13137     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13138         Tile[last_jx][last_jy] == EL_EMPTY)
13139     {
13140       int last_field_block_delay = 0;   // start with no blocking at all
13141       int block_delay_adjustment = player->block_delay_adjustment;
13142
13143       // if player blocks last field, add delay for exactly one move
13144       if (player->block_last_field)
13145       {
13146         last_field_block_delay += player->move_delay_value;
13147
13148         // when blocking enabled, prevent moving up despite gravity
13149         if (player->gravity && player->MovDir == MV_UP)
13150           block_delay_adjustment = -1;
13151       }
13152
13153       // add block delay adjustment (also possible when not blocking)
13154       last_field_block_delay += block_delay_adjustment;
13155
13156       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13157       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13158     }
13159
13160     if (player->MovPos != 0)    // player has not yet reached destination
13161       return;
13162   }
13163   else if (!FrameReached(&player->actual_frame_counter))
13164     return;
13165
13166   if (player->MovPos != 0)
13167   {
13168     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13169     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13170
13171     // before DrawPlayer() to draw correct player graphic for this case
13172     if (player->MovPos == 0)
13173       CheckGravityMovement(player);
13174   }
13175
13176   if (player->MovPos == 0)      // player reached destination field
13177   {
13178     if (player->move_delay_reset_counter > 0)
13179     {
13180       player->move_delay_reset_counter--;
13181
13182       if (player->move_delay_reset_counter == 0)
13183       {
13184         // continue with normal speed after quickly moving through gate
13185         HALVE_PLAYER_SPEED(player);
13186
13187         // be able to make the next move without delay
13188         player->move_delay = 0;
13189       }
13190     }
13191
13192     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13193         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13194         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13195         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13196         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13197         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13198         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13199         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13200     {
13201       ExitPlayer(player);
13202
13203       if (game.players_still_needed == 0 &&
13204           (game.friends_still_needed == 0 ||
13205            IS_SP_ELEMENT(Tile[jx][jy])))
13206         LevelSolved();
13207     }
13208
13209     player->last_jx = jx;
13210     player->last_jy = jy;
13211
13212     // this breaks one level: "machine", level 000
13213     {
13214       int move_direction = player->MovDir;
13215       int enter_side = MV_DIR_OPPOSITE(move_direction);
13216       int leave_side = move_direction;
13217       int old_jx = last_jx;
13218       int old_jy = last_jy;
13219       int old_element = Tile[old_jx][old_jy];
13220       int new_element = Tile[jx][jy];
13221
13222       if (IS_CUSTOM_ELEMENT(old_element))
13223         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13224                                    CE_LEFT_BY_PLAYER,
13225                                    player->index_bit, leave_side);
13226
13227       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13228                                           CE_PLAYER_LEAVES_X,
13229                                           player->index_bit, leave_side);
13230
13231       if (IS_CUSTOM_ELEMENT(new_element))
13232         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13233                                    player->index_bit, enter_side);
13234
13235       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13236                                           CE_PLAYER_ENTERS_X,
13237                                           player->index_bit, enter_side);
13238
13239       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13240                                         CE_MOVE_OF_X, move_direction);
13241     }
13242
13243     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13244     {
13245       TestIfPlayerTouchesBadThing(jx, jy);
13246       TestIfPlayerTouchesCustomElement(jx, jy);
13247
13248       /* needed because pushed element has not yet reached its destination,
13249          so it would trigger a change event at its previous field location */
13250       if (!player->is_pushing)
13251         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13252
13253       if (level.finish_dig_collect &&
13254           (player->is_digging || player->is_collecting))
13255       {
13256         int last_element = player->last_removed_element;
13257         int move_direction = player->MovDir;
13258         int enter_side = MV_DIR_OPPOSITE(move_direction);
13259         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13260                             CE_PLAYER_COLLECTS_X);
13261
13262         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13263                                             player->index_bit, enter_side);
13264
13265         player->last_removed_element = EL_UNDEFINED;
13266       }
13267
13268       if (!player->active)
13269         RemovePlayer(player);
13270     }
13271
13272     if (level.use_step_counter)
13273       CheckLevelTime_StepCounter();
13274
13275     if (tape.single_step && tape.recording && !tape.pausing &&
13276         !player->programmed_action)
13277       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13278
13279     if (!player->programmed_action)
13280       CheckSaveEngineSnapshot(player);
13281   }
13282 }
13283
13284 void ScrollScreen(struct PlayerInfo *player, int mode)
13285 {
13286   static DelayCounter screen_frame_counter = { 0 };
13287
13288   if (mode == SCROLL_INIT)
13289   {
13290     // set scrolling step size according to actual player's moving speed
13291     ScrollStepSize = TILEX / player->move_delay_value;
13292
13293     screen_frame_counter.count = FrameCounter;
13294     screen_frame_counter.value = 1;
13295
13296     ScreenMovDir = player->MovDir;
13297     ScreenMovPos = player->MovPos;
13298     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13299     return;
13300   }
13301   else if (!FrameReached(&screen_frame_counter))
13302     return;
13303
13304   if (ScreenMovPos)
13305   {
13306     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13307     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13308     redraw_mask |= REDRAW_FIELD;
13309   }
13310   else
13311     ScreenMovDir = MV_NONE;
13312 }
13313
13314 void CheckNextToConditions(int x, int y)
13315 {
13316   int element = Tile[x][y];
13317
13318   if (IS_PLAYER(x, y))
13319     TestIfPlayerNextToCustomElement(x, y);
13320
13321   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13322       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13323     TestIfElementNextToCustomElement(x, y);
13324 }
13325
13326 void TestIfPlayerNextToCustomElement(int x, int y)
13327 {
13328   struct XY *xy = xy_topdown;
13329   static int trigger_sides[4][2] =
13330   {
13331     // center side       border side
13332     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13333     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13334     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13335     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13336   };
13337   int i;
13338
13339   if (!IS_PLAYER(x, y))
13340     return;
13341
13342   struct PlayerInfo *player = PLAYERINFO(x, y);
13343
13344   if (player->is_moving)
13345     return;
13346
13347   for (i = 0; i < NUM_DIRECTIONS; i++)
13348   {
13349     int xx = x + xy[i].x;
13350     int yy = y + xy[i].y;
13351     int border_side = trigger_sides[i][1];
13352     int border_element;
13353
13354     if (!IN_LEV_FIELD(xx, yy))
13355       continue;
13356
13357     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13358       continue;         // center and border element not connected
13359
13360     border_element = Tile[xx][yy];
13361
13362     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13363                                player->index_bit, border_side);
13364     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13365                                         CE_PLAYER_NEXT_TO_X,
13366                                         player->index_bit, border_side);
13367
13368     /* use player element that is initially defined in the level playfield,
13369        not the player element that corresponds to the runtime player number
13370        (example: a level that contains EL_PLAYER_3 as the only player would
13371        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13372
13373     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13374                              CE_NEXT_TO_X, border_side);
13375   }
13376 }
13377
13378 void TestIfPlayerTouchesCustomElement(int x, int y)
13379 {
13380   struct XY *xy = xy_topdown;
13381   static int trigger_sides[4][2] =
13382   {
13383     // center side       border side
13384     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13385     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13386     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13387     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13388   };
13389   static int touch_dir[4] =
13390   {
13391     MV_LEFT | MV_RIGHT,
13392     MV_UP   | MV_DOWN,
13393     MV_UP   | MV_DOWN,
13394     MV_LEFT | MV_RIGHT
13395   };
13396   int center_element = Tile[x][y];      // should always be non-moving!
13397   int i;
13398
13399   for (i = 0; i < NUM_DIRECTIONS; i++)
13400   {
13401     int xx = x + xy[i].x;
13402     int yy = y + xy[i].y;
13403     int center_side = trigger_sides[i][0];
13404     int border_side = trigger_sides[i][1];
13405     int border_element;
13406
13407     if (!IN_LEV_FIELD(xx, yy))
13408       continue;
13409
13410     if (IS_PLAYER(x, y))                // player found at center element
13411     {
13412       struct PlayerInfo *player = PLAYERINFO(x, y);
13413
13414       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13415         border_element = Tile[xx][yy];          // may be moving!
13416       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13417         border_element = Tile[xx][yy];
13418       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13419         border_element = MovingOrBlocked2Element(xx, yy);
13420       else
13421         continue;               // center and border element do not touch
13422
13423       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13424                                  player->index_bit, border_side);
13425       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13426                                           CE_PLAYER_TOUCHES_X,
13427                                           player->index_bit, border_side);
13428
13429       {
13430         /* use player element that is initially defined in the level playfield,
13431            not the player element that corresponds to the runtime player number
13432            (example: a level that contains EL_PLAYER_3 as the only player would
13433            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13434         int player_element = PLAYERINFO(x, y)->initial_element;
13435
13436         CheckElementChangeBySide(xx, yy, border_element, player_element,
13437                                  CE_TOUCHING_X, border_side);
13438       }
13439     }
13440     else if (IS_PLAYER(xx, yy))         // player found at border element
13441     {
13442       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13443
13444       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13445       {
13446         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13447           continue;             // center and border element do not touch
13448       }
13449
13450       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13451                                  player->index_bit, center_side);
13452       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13453                                           CE_PLAYER_TOUCHES_X,
13454                                           player->index_bit, center_side);
13455
13456       {
13457         /* use player element that is initially defined in the level playfield,
13458            not the player element that corresponds to the runtime player number
13459            (example: a level that contains EL_PLAYER_3 as the only player would
13460            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13461         int player_element = PLAYERINFO(xx, yy)->initial_element;
13462
13463         CheckElementChangeBySide(x, y, center_element, player_element,
13464                                  CE_TOUCHING_X, center_side);
13465       }
13466
13467       break;
13468     }
13469   }
13470 }
13471
13472 void TestIfElementNextToCustomElement(int x, int y)
13473 {
13474   struct XY *xy = xy_topdown;
13475   static int trigger_sides[4][2] =
13476   {
13477     // center side      border side
13478     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13479     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13480     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13481     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13482   };
13483   int center_element = Tile[x][y];      // should always be non-moving!
13484   int i;
13485
13486   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13487     return;
13488
13489   for (i = 0; i < NUM_DIRECTIONS; i++)
13490   {
13491     int xx = x + xy[i].x;
13492     int yy = y + xy[i].y;
13493     int border_side = trigger_sides[i][1];
13494     int border_element;
13495
13496     if (!IN_LEV_FIELD(xx, yy))
13497       continue;
13498
13499     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13500       continue;                 // center and border element not connected
13501
13502     border_element = Tile[xx][yy];
13503
13504     // check for change of center element (but change it only once)
13505     if (CheckElementChangeBySide(x, y, center_element, border_element,
13506                                  CE_NEXT_TO_X, border_side))
13507       break;
13508   }
13509 }
13510
13511 void TestIfElementTouchesCustomElement(int x, int y)
13512 {
13513   struct XY *xy = xy_topdown;
13514   static int trigger_sides[4][2] =
13515   {
13516     // center side      border side
13517     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13518     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13519     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13520     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13521   };
13522   static int touch_dir[4] =
13523   {
13524     MV_LEFT | MV_RIGHT,
13525     MV_UP   | MV_DOWN,
13526     MV_UP   | MV_DOWN,
13527     MV_LEFT | MV_RIGHT
13528   };
13529   boolean change_center_element = FALSE;
13530   int center_element = Tile[x][y];      // should always be non-moving!
13531   int border_element_old[NUM_DIRECTIONS];
13532   int i;
13533
13534   for (i = 0; i < NUM_DIRECTIONS; i++)
13535   {
13536     int xx = x + xy[i].x;
13537     int yy = y + xy[i].y;
13538     int border_element;
13539
13540     border_element_old[i] = -1;
13541
13542     if (!IN_LEV_FIELD(xx, yy))
13543       continue;
13544
13545     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13546       border_element = Tile[xx][yy];    // may be moving!
13547     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13548       border_element = Tile[xx][yy];
13549     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13550       border_element = MovingOrBlocked2Element(xx, yy);
13551     else
13552       continue;                 // center and border element do not touch
13553
13554     border_element_old[i] = border_element;
13555   }
13556
13557   for (i = 0; i < NUM_DIRECTIONS; i++)
13558   {
13559     int xx = x + xy[i].x;
13560     int yy = y + xy[i].y;
13561     int center_side = trigger_sides[i][0];
13562     int border_element = border_element_old[i];
13563
13564     if (border_element == -1)
13565       continue;
13566
13567     // check for change of border element
13568     CheckElementChangeBySide(xx, yy, border_element, center_element,
13569                              CE_TOUCHING_X, center_side);
13570
13571     // (center element cannot be player, so we dont have to check this here)
13572   }
13573
13574   for (i = 0; i < NUM_DIRECTIONS; i++)
13575   {
13576     int xx = x + xy[i].x;
13577     int yy = y + xy[i].y;
13578     int border_side = trigger_sides[i][1];
13579     int border_element = border_element_old[i];
13580
13581     if (border_element == -1)
13582       continue;
13583
13584     // check for change of center element (but change it only once)
13585     if (!change_center_element)
13586       change_center_element =
13587         CheckElementChangeBySide(x, y, center_element, border_element,
13588                                  CE_TOUCHING_X, border_side);
13589
13590     if (IS_PLAYER(xx, yy))
13591     {
13592       /* use player element that is initially defined in the level playfield,
13593          not the player element that corresponds to the runtime player number
13594          (example: a level that contains EL_PLAYER_3 as the only player would
13595          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13596       int player_element = PLAYERINFO(xx, yy)->initial_element;
13597
13598       CheckElementChangeBySide(x, y, center_element, player_element,
13599                                CE_TOUCHING_X, border_side);
13600     }
13601   }
13602 }
13603
13604 void TestIfElementHitsCustomElement(int x, int y, int direction)
13605 {
13606   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13607   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13608   int hitx = x + dx, hity = y + dy;
13609   int hitting_element = Tile[x][y];
13610   int touched_element;
13611
13612   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13613     return;
13614
13615   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13616                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13617
13618   if (IN_LEV_FIELD(hitx, hity))
13619   {
13620     int opposite_direction = MV_DIR_OPPOSITE(direction);
13621     int hitting_side = direction;
13622     int touched_side = opposite_direction;
13623     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13624                           MovDir[hitx][hity] != direction ||
13625                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13626
13627     object_hit = TRUE;
13628
13629     if (object_hit)
13630     {
13631       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13632                                CE_HITTING_X, touched_side);
13633
13634       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13635                                CE_HIT_BY_X, hitting_side);
13636
13637       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13638                                CE_HIT_BY_SOMETHING, opposite_direction);
13639
13640       if (IS_PLAYER(hitx, hity))
13641       {
13642         /* use player element that is initially defined in the level playfield,
13643            not the player element that corresponds to the runtime player number
13644            (example: a level that contains EL_PLAYER_3 as the only player would
13645            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13646         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13647
13648         CheckElementChangeBySide(x, y, hitting_element, player_element,
13649                                  CE_HITTING_X, touched_side);
13650       }
13651     }
13652   }
13653
13654   // "hitting something" is also true when hitting the playfield border
13655   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13656                            CE_HITTING_SOMETHING, direction);
13657 }
13658
13659 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13660 {
13661   int i, kill_x = -1, kill_y = -1;
13662
13663   int bad_element = -1;
13664   struct XY *test_xy = xy_topdown;
13665   static int test_dir[4] =
13666   {
13667     MV_UP,
13668     MV_LEFT,
13669     MV_RIGHT,
13670     MV_DOWN
13671   };
13672
13673   for (i = 0; i < NUM_DIRECTIONS; i++)
13674   {
13675     int test_x, test_y, test_move_dir, test_element;
13676
13677     test_x = good_x + test_xy[i].x;
13678     test_y = good_y + test_xy[i].y;
13679
13680     if (!IN_LEV_FIELD(test_x, test_y))
13681       continue;
13682
13683     test_move_dir =
13684       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13685
13686     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13687
13688     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13689        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13690     */
13691     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13692         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13693     {
13694       kill_x = test_x;
13695       kill_y = test_y;
13696       bad_element = test_element;
13697
13698       break;
13699     }
13700   }
13701
13702   if (kill_x != -1 || kill_y != -1)
13703   {
13704     if (IS_PLAYER(good_x, good_y))
13705     {
13706       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13707
13708       if (player->shield_deadly_time_left > 0 &&
13709           !IS_INDESTRUCTIBLE(bad_element))
13710         Bang(kill_x, kill_y);
13711       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13712         KillPlayer(player);
13713     }
13714     else
13715       Bang(good_x, good_y);
13716   }
13717 }
13718
13719 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13720 {
13721   int i, kill_x = -1, kill_y = -1;
13722   int bad_element = Tile[bad_x][bad_y];
13723   struct XY *test_xy = xy_topdown;
13724   static int touch_dir[4] =
13725   {
13726     MV_LEFT | MV_RIGHT,
13727     MV_UP   | MV_DOWN,
13728     MV_UP   | MV_DOWN,
13729     MV_LEFT | MV_RIGHT
13730   };
13731   static int test_dir[4] =
13732   {
13733     MV_UP,
13734     MV_LEFT,
13735     MV_RIGHT,
13736     MV_DOWN
13737   };
13738
13739   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13740     return;
13741
13742   for (i = 0; i < NUM_DIRECTIONS; i++)
13743   {
13744     int test_x, test_y, test_move_dir, test_element;
13745
13746     test_x = bad_x + test_xy[i].x;
13747     test_y = bad_y + test_xy[i].y;
13748
13749     if (!IN_LEV_FIELD(test_x, test_y))
13750       continue;
13751
13752     test_move_dir =
13753       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13754
13755     test_element = Tile[test_x][test_y];
13756
13757     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13758        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13759     */
13760     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13761         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13762     {
13763       // good thing is player or penguin that does not move away
13764       if (IS_PLAYER(test_x, test_y))
13765       {
13766         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13767
13768         if (bad_element == EL_ROBOT && player->is_moving)
13769           continue;     // robot does not kill player if he is moving
13770
13771         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13772         {
13773           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13774             continue;           // center and border element do not touch
13775         }
13776
13777         kill_x = test_x;
13778         kill_y = test_y;
13779
13780         break;
13781       }
13782       else if (test_element == EL_PENGUIN)
13783       {
13784         kill_x = test_x;
13785         kill_y = test_y;
13786
13787         break;
13788       }
13789     }
13790   }
13791
13792   if (kill_x != -1 || kill_y != -1)
13793   {
13794     if (IS_PLAYER(kill_x, kill_y))
13795     {
13796       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13797
13798       if (player->shield_deadly_time_left > 0 &&
13799           !IS_INDESTRUCTIBLE(bad_element))
13800         Bang(bad_x, bad_y);
13801       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13802         KillPlayer(player);
13803     }
13804     else
13805       Bang(kill_x, kill_y);
13806   }
13807 }
13808
13809 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13810 {
13811   int bad_element = Tile[bad_x][bad_y];
13812   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13813   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13814   int test_x = bad_x + dx, test_y = bad_y + dy;
13815   int test_move_dir, test_element;
13816   int kill_x = -1, kill_y = -1;
13817
13818   if (!IN_LEV_FIELD(test_x, test_y))
13819     return;
13820
13821   test_move_dir =
13822     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13823
13824   test_element = Tile[test_x][test_y];
13825
13826   if (test_move_dir != bad_move_dir)
13827   {
13828     // good thing can be player or penguin that does not move away
13829     if (IS_PLAYER(test_x, test_y))
13830     {
13831       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13832
13833       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13834          player as being hit when he is moving towards the bad thing, because
13835          the "get hit by" condition would be lost after the player stops) */
13836       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13837         return;         // player moves away from bad thing
13838
13839       kill_x = test_x;
13840       kill_y = test_y;
13841     }
13842     else if (test_element == EL_PENGUIN)
13843     {
13844       kill_x = test_x;
13845       kill_y = test_y;
13846     }
13847   }
13848
13849   if (kill_x != -1 || kill_y != -1)
13850   {
13851     if (IS_PLAYER(kill_x, kill_y))
13852     {
13853       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13854
13855       if (player->shield_deadly_time_left > 0 &&
13856           !IS_INDESTRUCTIBLE(bad_element))
13857         Bang(bad_x, bad_y);
13858       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13859         KillPlayer(player);
13860     }
13861     else
13862       Bang(kill_x, kill_y);
13863   }
13864 }
13865
13866 void TestIfPlayerTouchesBadThing(int x, int y)
13867 {
13868   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13869 }
13870
13871 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13872 {
13873   TestIfGoodThingHitsBadThing(x, y, move_dir);
13874 }
13875
13876 void TestIfBadThingTouchesPlayer(int x, int y)
13877 {
13878   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13879 }
13880
13881 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13882 {
13883   TestIfBadThingHitsGoodThing(x, y, move_dir);
13884 }
13885
13886 void TestIfFriendTouchesBadThing(int x, int y)
13887 {
13888   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13889 }
13890
13891 void TestIfBadThingTouchesFriend(int x, int y)
13892 {
13893   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13894 }
13895
13896 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13897 {
13898   int i, kill_x = bad_x, kill_y = bad_y;
13899   struct XY *xy = xy_topdown;
13900
13901   for (i = 0; i < NUM_DIRECTIONS; i++)
13902   {
13903     int x, y, element;
13904
13905     x = bad_x + xy[i].x;
13906     y = bad_y + xy[i].y;
13907     if (!IN_LEV_FIELD(x, y))
13908       continue;
13909
13910     element = Tile[x][y];
13911     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13912         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13913     {
13914       kill_x = x;
13915       kill_y = y;
13916       break;
13917     }
13918   }
13919
13920   if (kill_x != bad_x || kill_y != bad_y)
13921     Bang(bad_x, bad_y);
13922 }
13923
13924 void KillPlayer(struct PlayerInfo *player)
13925 {
13926   int jx = player->jx, jy = player->jy;
13927
13928   if (!player->active)
13929     return;
13930
13931 #if 0
13932   Debug("game:playing:KillPlayer",
13933         "0: killed == %d, active == %d, reanimated == %d",
13934         player->killed, player->active, player->reanimated);
13935 #endif
13936
13937   /* the following code was introduced to prevent an infinite loop when calling
13938      -> Bang()
13939      -> CheckTriggeredElementChangeExt()
13940      -> ExecuteCustomElementAction()
13941      -> KillPlayer()
13942      -> (infinitely repeating the above sequence of function calls)
13943      which occurs when killing the player while having a CE with the setting
13944      "kill player X when explosion of <player X>"; the solution using a new
13945      field "player->killed" was chosen for backwards compatibility, although
13946      clever use of the fields "player->active" etc. would probably also work */
13947 #if 1
13948   if (player->killed)
13949     return;
13950 #endif
13951
13952   player->killed = TRUE;
13953
13954   // remove accessible field at the player's position
13955   Tile[jx][jy] = EL_EMPTY;
13956
13957   // deactivate shield (else Bang()/Explode() would not work right)
13958   player->shield_normal_time_left = 0;
13959   player->shield_deadly_time_left = 0;
13960
13961 #if 0
13962   Debug("game:playing:KillPlayer",
13963         "1: killed == %d, active == %d, reanimated == %d",
13964         player->killed, player->active, player->reanimated);
13965 #endif
13966
13967   Bang(jx, jy);
13968
13969 #if 0
13970   Debug("game:playing:KillPlayer",
13971         "2: killed == %d, active == %d, reanimated == %d",
13972         player->killed, player->active, player->reanimated);
13973 #endif
13974
13975   if (player->reanimated)       // killed player may have been reanimated
13976     player->killed = player->reanimated = FALSE;
13977   else
13978     BuryPlayer(player);
13979 }
13980
13981 static void KillPlayerUnlessEnemyProtected(int x, int y)
13982 {
13983   if (!PLAYER_ENEMY_PROTECTED(x, y))
13984     KillPlayer(PLAYERINFO(x, y));
13985 }
13986
13987 static void KillPlayerUnlessExplosionProtected(int x, int y)
13988 {
13989   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13990     KillPlayer(PLAYERINFO(x, y));
13991 }
13992
13993 void BuryPlayer(struct PlayerInfo *player)
13994 {
13995   int jx = player->jx, jy = player->jy;
13996
13997   if (!player->active)
13998     return;
13999
14000   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14001
14002   RemovePlayer(player);
14003
14004   player->buried = TRUE;
14005
14006   if (game.all_players_gone)
14007     game.GameOver = TRUE;
14008 }
14009
14010 void RemovePlayer(struct PlayerInfo *player)
14011 {
14012   int jx = player->jx, jy = player->jy;
14013   int i, found = FALSE;
14014
14015   player->present = FALSE;
14016   player->active = FALSE;
14017
14018   // required for some CE actions (even if the player is not active anymore)
14019   player->MovPos = 0;
14020
14021   if (!ExplodeField[jx][jy])
14022     StorePlayer[jx][jy] = 0;
14023
14024   if (player->is_moving)
14025     TEST_DrawLevelField(player->last_jx, player->last_jy);
14026
14027   for (i = 0; i < MAX_PLAYERS; i++)
14028     if (stored_player[i].active)
14029       found = TRUE;
14030
14031   if (!found)
14032   {
14033     game.all_players_gone = TRUE;
14034     game.GameOver = TRUE;
14035   }
14036
14037   game.exit_x = game.robot_wheel_x = jx;
14038   game.exit_y = game.robot_wheel_y = jy;
14039 }
14040
14041 void ExitPlayer(struct PlayerInfo *player)
14042 {
14043   DrawPlayer(player);   // needed here only to cleanup last field
14044   RemovePlayer(player);
14045
14046   if (game.players_still_needed > 0)
14047     game.players_still_needed--;
14048 }
14049
14050 static void SetFieldForSnapping(int x, int y, int element, int direction,
14051                                 int player_index_bit)
14052 {
14053   struct ElementInfo *ei = &element_info[element];
14054   int direction_bit = MV_DIR_TO_BIT(direction);
14055   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14056   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14057                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14058
14059   Tile[x][y] = EL_ELEMENT_SNAPPING;
14060   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14061   MovDir[x][y] = direction;
14062   Store[x][y] = element;
14063   Store2[x][y] = player_index_bit;
14064
14065   ResetGfxAnimation(x, y);
14066
14067   GfxElement[x][y] = element;
14068   GfxAction[x][y] = action;
14069   GfxDir[x][y] = direction;
14070   GfxFrame[x][y] = -1;
14071 }
14072
14073 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14074                                    int player_index_bit)
14075 {
14076   TestIfElementTouchesCustomElement(x, y);      // for empty space
14077
14078   if (level.finish_dig_collect)
14079   {
14080     int dig_side = MV_DIR_OPPOSITE(direction);
14081     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14082                         CE_PLAYER_COLLECTS_X);
14083
14084     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14085                                         player_index_bit, dig_side);
14086     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14087                                         player_index_bit, dig_side);
14088   }
14089 }
14090
14091 /*
14092   =============================================================================
14093   checkDiagonalPushing()
14094   -----------------------------------------------------------------------------
14095   check if diagonal input device direction results in pushing of object
14096   (by checking if the alternative direction is walkable, diggable, ...)
14097   =============================================================================
14098 */
14099
14100 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14101                                     int x, int y, int real_dx, int real_dy)
14102 {
14103   int jx, jy, dx, dy, xx, yy;
14104
14105   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14106     return TRUE;
14107
14108   // diagonal direction: check alternative direction
14109   jx = player->jx;
14110   jy = player->jy;
14111   dx = x - jx;
14112   dy = y - jy;
14113   xx = jx + (dx == 0 ? real_dx : 0);
14114   yy = jy + (dy == 0 ? real_dy : 0);
14115
14116   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14117 }
14118
14119 /*
14120   =============================================================================
14121   DigField()
14122   -----------------------------------------------------------------------------
14123   x, y:                 field next to player (non-diagonal) to try to dig to
14124   real_dx, real_dy:     direction as read from input device (can be diagonal)
14125   =============================================================================
14126 */
14127
14128 static int DigField(struct PlayerInfo *player,
14129                     int oldx, int oldy, int x, int y,
14130                     int real_dx, int real_dy, int mode)
14131 {
14132   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14133   boolean player_was_pushing = player->is_pushing;
14134   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14135   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14136   int jx = oldx, jy = oldy;
14137   int dx = x - jx, dy = y - jy;
14138   int nextx = x + dx, nexty = y + dy;
14139   int move_direction = (dx == -1 ? MV_LEFT  :
14140                         dx == +1 ? MV_RIGHT :
14141                         dy == -1 ? MV_UP    :
14142                         dy == +1 ? MV_DOWN  : MV_NONE);
14143   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14144   int dig_side = MV_DIR_OPPOSITE(move_direction);
14145   int old_element = Tile[jx][jy];
14146   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14147   int collect_count;
14148
14149   if (is_player)                // function can also be called by EL_PENGUIN
14150   {
14151     if (player->MovPos == 0)
14152     {
14153       player->is_digging = FALSE;
14154       player->is_collecting = FALSE;
14155     }
14156
14157     if (player->MovPos == 0)    // last pushing move finished
14158       player->is_pushing = FALSE;
14159
14160     if (mode == DF_NO_PUSH)     // player just stopped pushing
14161     {
14162       player->is_switching = FALSE;
14163       player->push_delay = -1;
14164
14165       return MP_NO_ACTION;
14166     }
14167   }
14168   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14169     old_element = Back[jx][jy];
14170
14171   // in case of element dropped at player position, check background
14172   else if (Back[jx][jy] != EL_EMPTY &&
14173            game.engine_version >= VERSION_IDENT(2,2,0,0))
14174     old_element = Back[jx][jy];
14175
14176   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14177     return MP_NO_ACTION;        // field has no opening in this direction
14178
14179   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14180     return MP_NO_ACTION;        // field has no opening in this direction
14181
14182   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14183   {
14184     SplashAcid(x, y);
14185
14186     Tile[jx][jy] = player->artwork_element;
14187     InitMovingField(jx, jy, MV_DOWN);
14188     Store[jx][jy] = EL_ACID;
14189     ContinueMoving(jx, jy);
14190     BuryPlayer(player);
14191
14192     return MP_DONT_RUN_INTO;
14193   }
14194
14195   if (player_can_move && DONT_RUN_INTO(element))
14196   {
14197     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14198
14199     return MP_DONT_RUN_INTO;
14200   }
14201
14202   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14203     return MP_NO_ACTION;
14204
14205   collect_count = element_info[element].collect_count_initial;
14206
14207   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14208     return MP_NO_ACTION;
14209
14210   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14211     player_can_move = player_can_move_or_snap;
14212
14213   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14214       game.engine_version >= VERSION_IDENT(2,2,0,0))
14215   {
14216     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14217                                player->index_bit, dig_side);
14218     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14219                                         player->index_bit, dig_side);
14220
14221     if (element == EL_DC_LANDMINE)
14222       Bang(x, y);
14223
14224     if (Tile[x][y] != element)          // field changed by snapping
14225       return MP_ACTION;
14226
14227     return MP_NO_ACTION;
14228   }
14229
14230   if (player->gravity && is_player && !player->is_auto_moving &&
14231       canFallDown(player) && move_direction != MV_DOWN &&
14232       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14233     return MP_NO_ACTION;        // player cannot walk here due to gravity
14234
14235   if (player_can_move &&
14236       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14237   {
14238     int sound_element = SND_ELEMENT(element);
14239     int sound_action = ACTION_WALKING;
14240
14241     if (IS_RND_GATE(element))
14242     {
14243       if (!player->key[RND_GATE_NR(element)])
14244         return MP_NO_ACTION;
14245     }
14246     else if (IS_RND_GATE_GRAY(element))
14247     {
14248       if (!player->key[RND_GATE_GRAY_NR(element)])
14249         return MP_NO_ACTION;
14250     }
14251     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14252     {
14253       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14254         return MP_NO_ACTION;
14255     }
14256     else if (element == EL_EXIT_OPEN ||
14257              element == EL_EM_EXIT_OPEN ||
14258              element == EL_EM_EXIT_OPENING ||
14259              element == EL_STEEL_EXIT_OPEN ||
14260              element == EL_EM_STEEL_EXIT_OPEN ||
14261              element == EL_EM_STEEL_EXIT_OPENING ||
14262              element == EL_SP_EXIT_OPEN ||
14263              element == EL_SP_EXIT_OPENING)
14264     {
14265       sound_action = ACTION_PASSING;    // player is passing exit
14266     }
14267     else if (element == EL_EMPTY)
14268     {
14269       sound_action = ACTION_MOVING;             // nothing to walk on
14270     }
14271
14272     // play sound from background or player, whatever is available
14273     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14274       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14275     else
14276       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14277   }
14278   else if (player_can_move &&
14279            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14280   {
14281     if (!ACCESS_FROM(element, opposite_direction))
14282       return MP_NO_ACTION;      // field not accessible from this direction
14283
14284     if (CAN_MOVE(element))      // only fixed elements can be passed!
14285       return MP_NO_ACTION;
14286
14287     if (IS_EM_GATE(element))
14288     {
14289       if (!player->key[EM_GATE_NR(element)])
14290         return MP_NO_ACTION;
14291     }
14292     else if (IS_EM_GATE_GRAY(element))
14293     {
14294       if (!player->key[EM_GATE_GRAY_NR(element)])
14295         return MP_NO_ACTION;
14296     }
14297     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14298     {
14299       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14300         return MP_NO_ACTION;
14301     }
14302     else if (IS_EMC_GATE(element))
14303     {
14304       if (!player->key[EMC_GATE_NR(element)])
14305         return MP_NO_ACTION;
14306     }
14307     else if (IS_EMC_GATE_GRAY(element))
14308     {
14309       if (!player->key[EMC_GATE_GRAY_NR(element)])
14310         return MP_NO_ACTION;
14311     }
14312     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14313     {
14314       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14315         return MP_NO_ACTION;
14316     }
14317     else if (element == EL_DC_GATE_WHITE ||
14318              element == EL_DC_GATE_WHITE_GRAY ||
14319              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14320     {
14321       if (player->num_white_keys == 0)
14322         return MP_NO_ACTION;
14323
14324       player->num_white_keys--;
14325     }
14326     else if (IS_SP_PORT(element))
14327     {
14328       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14329           element == EL_SP_GRAVITY_PORT_RIGHT ||
14330           element == EL_SP_GRAVITY_PORT_UP ||
14331           element == EL_SP_GRAVITY_PORT_DOWN)
14332         player->gravity = !player->gravity;
14333       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14334                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14335                element == EL_SP_GRAVITY_ON_PORT_UP ||
14336                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14337         player->gravity = TRUE;
14338       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14339                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14340                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14341                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14342         player->gravity = FALSE;
14343     }
14344
14345     // automatically move to the next field with double speed
14346     player->programmed_action = move_direction;
14347
14348     if (player->move_delay_reset_counter == 0)
14349     {
14350       player->move_delay_reset_counter = 2;     // two double speed steps
14351
14352       DOUBLE_PLAYER_SPEED(player);
14353     }
14354
14355     PlayLevelSoundAction(x, y, ACTION_PASSING);
14356   }
14357   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14358   {
14359     RemoveField(x, y);
14360
14361     if (mode != DF_SNAP)
14362     {
14363       GfxElement[x][y] = GFX_ELEMENT(element);
14364       player->is_digging = TRUE;
14365     }
14366
14367     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14368
14369     // use old behaviour for old levels (digging)
14370     if (!level.finish_dig_collect)
14371     {
14372       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14373                                           player->index_bit, dig_side);
14374
14375       // if digging triggered player relocation, finish digging tile
14376       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14377         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14378     }
14379
14380     if (mode == DF_SNAP)
14381     {
14382       if (level.block_snap_field)
14383         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14384       else
14385         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14386
14387       // use old behaviour for old levels (snapping)
14388       if (!level.finish_dig_collect)
14389         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14390                                             player->index_bit, dig_side);
14391     }
14392   }
14393   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14394   {
14395     RemoveField(x, y);
14396
14397     if (is_player && mode != DF_SNAP)
14398     {
14399       GfxElement[x][y] = element;
14400       player->is_collecting = TRUE;
14401     }
14402
14403     if (element == EL_SPEED_PILL)
14404     {
14405       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14406     }
14407     else if (element == EL_EXTRA_TIME && level.time > 0)
14408     {
14409       TimeLeft += level.extra_time;
14410
14411       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14412
14413       DisplayGameControlValues();
14414     }
14415     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14416     {
14417       int shield_time = (element == EL_SHIELD_DEADLY ?
14418                          level.shield_deadly_time :
14419                          level.shield_normal_time);
14420
14421       player->shield_normal_time_left += shield_time;
14422       if (element == EL_SHIELD_DEADLY)
14423         player->shield_deadly_time_left += shield_time;
14424     }
14425     else if (element == EL_DYNAMITE ||
14426              element == EL_EM_DYNAMITE ||
14427              element == EL_SP_DISK_RED)
14428     {
14429       if (player->inventory_size < MAX_INVENTORY_SIZE)
14430         player->inventory_element[player->inventory_size++] = element;
14431
14432       DrawGameDoorValues();
14433     }
14434     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14435     {
14436       player->dynabomb_count++;
14437       player->dynabombs_left++;
14438     }
14439     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14440     {
14441       player->dynabomb_size++;
14442     }
14443     else if (element == EL_DYNABOMB_INCREASE_POWER)
14444     {
14445       player->dynabomb_xl = TRUE;
14446     }
14447     else if (IS_KEY(element))
14448     {
14449       player->key[KEY_NR(element)] = TRUE;
14450
14451       DrawGameDoorValues();
14452     }
14453     else if (element == EL_DC_KEY_WHITE)
14454     {
14455       player->num_white_keys++;
14456
14457       // display white keys?
14458       // DrawGameDoorValues();
14459     }
14460     else if (IS_ENVELOPE(element))
14461     {
14462       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14463
14464       if (!wait_for_snapping)
14465         player->show_envelope = element;
14466     }
14467     else if (element == EL_EMC_LENSES)
14468     {
14469       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14470
14471       RedrawAllInvisibleElementsForLenses();
14472     }
14473     else if (element == EL_EMC_MAGNIFIER)
14474     {
14475       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14476
14477       RedrawAllInvisibleElementsForMagnifier();
14478     }
14479     else if (IS_DROPPABLE(element) ||
14480              IS_THROWABLE(element))     // can be collected and dropped
14481     {
14482       int i;
14483
14484       if (collect_count == 0)
14485         player->inventory_infinite_element = element;
14486       else
14487         for (i = 0; i < collect_count; i++)
14488           if (player->inventory_size < MAX_INVENTORY_SIZE)
14489             player->inventory_element[player->inventory_size++] = element;
14490
14491       DrawGameDoorValues();
14492     }
14493     else if (collect_count > 0)
14494     {
14495       game.gems_still_needed -= collect_count;
14496       if (game.gems_still_needed < 0)
14497         game.gems_still_needed = 0;
14498
14499       game.snapshot.collected_item = TRUE;
14500
14501       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14502
14503       DisplayGameControlValues();
14504     }
14505
14506     RaiseScoreElement(element);
14507     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14508
14509     // use old behaviour for old levels (collecting)
14510     if (!level.finish_dig_collect && is_player)
14511     {
14512       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14513                                           player->index_bit, dig_side);
14514
14515       // if collecting triggered player relocation, finish collecting tile
14516       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14517         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14518     }
14519
14520     if (mode == DF_SNAP)
14521     {
14522       if (level.block_snap_field)
14523         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14524       else
14525         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14526
14527       // use old behaviour for old levels (snapping)
14528       if (!level.finish_dig_collect)
14529         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14530                                             player->index_bit, dig_side);
14531     }
14532   }
14533   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14534   {
14535     if (mode == DF_SNAP && element != EL_BD_ROCK)
14536       return MP_NO_ACTION;
14537
14538     if (CAN_FALL(element) && dy)
14539       return MP_NO_ACTION;
14540
14541     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14542         !(element == EL_SPRING && level.use_spring_bug))
14543       return MP_NO_ACTION;
14544
14545     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14546         ((move_direction & MV_VERTICAL &&
14547           ((element_info[element].move_pattern & MV_LEFT &&
14548             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14549            (element_info[element].move_pattern & MV_RIGHT &&
14550             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14551          (move_direction & MV_HORIZONTAL &&
14552           ((element_info[element].move_pattern & MV_UP &&
14553             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14554            (element_info[element].move_pattern & MV_DOWN &&
14555             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14556       return MP_NO_ACTION;
14557
14558     // do not push elements already moving away faster than player
14559     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14560         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14561       return MP_NO_ACTION;
14562
14563     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14564     {
14565       if (player->push_delay_value == -1 || !player_was_pushing)
14566         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14567     }
14568     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14569     {
14570       if (player->push_delay_value == -1)
14571         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14572     }
14573     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14574     {
14575       if (!player->is_pushing)
14576         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14577     }
14578
14579     player->is_pushing = TRUE;
14580     player->is_active = TRUE;
14581
14582     if (!(IN_LEV_FIELD(nextx, nexty) &&
14583           (IS_FREE(nextx, nexty) ||
14584            (IS_SB_ELEMENT(element) &&
14585             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14586            (IS_CUSTOM_ELEMENT(element) &&
14587             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14588       return MP_NO_ACTION;
14589
14590     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14591       return MP_NO_ACTION;
14592
14593     if (player->push_delay == -1)       // new pushing; restart delay
14594       player->push_delay = 0;
14595
14596     if (player->push_delay < player->push_delay_value &&
14597         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14598         element != EL_SPRING && element != EL_BALLOON)
14599     {
14600       // make sure that there is no move delay before next try to push
14601       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14602         player->move_delay = 0;
14603
14604       return MP_NO_ACTION;
14605     }
14606
14607     if (IS_CUSTOM_ELEMENT(element) &&
14608         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14609     {
14610       if (!DigFieldByCE(nextx, nexty, element))
14611         return MP_NO_ACTION;
14612     }
14613
14614     if (IS_SB_ELEMENT(element))
14615     {
14616       boolean sokoban_task_solved = FALSE;
14617
14618       if (element == EL_SOKOBAN_FIELD_FULL)
14619       {
14620         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14621
14622         IncrementSokobanFieldsNeeded();
14623         IncrementSokobanObjectsNeeded();
14624       }
14625
14626       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14627       {
14628         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14629
14630         DecrementSokobanFieldsNeeded();
14631         DecrementSokobanObjectsNeeded();
14632
14633         // sokoban object was pushed from empty field to sokoban field
14634         if (Back[x][y] == EL_EMPTY)
14635           sokoban_task_solved = TRUE;
14636       }
14637
14638       Tile[x][y] = EL_SOKOBAN_OBJECT;
14639
14640       if (Back[x][y] == Back[nextx][nexty])
14641         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14642       else if (Back[x][y] != 0)
14643         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14644                                     ACTION_EMPTYING);
14645       else
14646         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14647                                     ACTION_FILLING);
14648
14649       if (sokoban_task_solved &&
14650           game.sokoban_fields_still_needed == 0 &&
14651           game.sokoban_objects_still_needed == 0 &&
14652           level.auto_exit_sokoban)
14653       {
14654         game.players_still_needed = 0;
14655
14656         LevelSolved();
14657
14658         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14659       }
14660     }
14661     else
14662       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14663
14664     InitMovingField(x, y, move_direction);
14665     GfxAction[x][y] = ACTION_PUSHING;
14666
14667     if (mode == DF_SNAP)
14668       ContinueMoving(x, y);
14669     else
14670       MovPos[x][y] = (dx != 0 ? dx : dy);
14671
14672     Pushed[x][y] = TRUE;
14673     Pushed[nextx][nexty] = TRUE;
14674
14675     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14676       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14677     else
14678       player->push_delay_value = -1;    // get new value later
14679
14680     // check for element change _after_ element has been pushed
14681     if (game.use_change_when_pushing_bug)
14682     {
14683       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14684                                  player->index_bit, dig_side);
14685       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14686                                           player->index_bit, dig_side);
14687     }
14688   }
14689   else if (IS_SWITCHABLE(element))
14690   {
14691     if (PLAYER_SWITCHING(player, x, y))
14692     {
14693       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14694                                           player->index_bit, dig_side);
14695
14696       return MP_ACTION;
14697     }
14698
14699     player->is_switching = TRUE;
14700     player->switch_x = x;
14701     player->switch_y = y;
14702
14703     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14704
14705     if (element == EL_ROBOT_WHEEL)
14706     {
14707       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14708
14709       game.robot_wheel_x = x;
14710       game.robot_wheel_y = y;
14711       game.robot_wheel_active = TRUE;
14712
14713       TEST_DrawLevelField(x, y);
14714     }
14715     else if (element == EL_SP_TERMINAL)
14716     {
14717       int xx, yy;
14718
14719       SCAN_PLAYFIELD(xx, yy)
14720       {
14721         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14722         {
14723           Bang(xx, yy);
14724         }
14725         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14726         {
14727           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14728
14729           ResetGfxAnimation(xx, yy);
14730           TEST_DrawLevelField(xx, yy);
14731         }
14732       }
14733     }
14734     else if (IS_BELT_SWITCH(element))
14735     {
14736       ToggleBeltSwitch(x, y);
14737     }
14738     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14739              element == EL_SWITCHGATE_SWITCH_DOWN ||
14740              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14741              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14742     {
14743       ToggleSwitchgateSwitch();
14744     }
14745     else if (element == EL_LIGHT_SWITCH ||
14746              element == EL_LIGHT_SWITCH_ACTIVE)
14747     {
14748       ToggleLightSwitch(x, y);
14749     }
14750     else if (element == EL_TIMEGATE_SWITCH ||
14751              element == EL_DC_TIMEGATE_SWITCH)
14752     {
14753       ActivateTimegateSwitch(x, y);
14754     }
14755     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14756              element == EL_BALLOON_SWITCH_RIGHT ||
14757              element == EL_BALLOON_SWITCH_UP    ||
14758              element == EL_BALLOON_SWITCH_DOWN  ||
14759              element == EL_BALLOON_SWITCH_NONE  ||
14760              element == EL_BALLOON_SWITCH_ANY)
14761     {
14762       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14763                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14764                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14765                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14766                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14767                              move_direction);
14768     }
14769     else if (element == EL_LAMP)
14770     {
14771       Tile[x][y] = EL_LAMP_ACTIVE;
14772       game.lights_still_needed--;
14773
14774       ResetGfxAnimation(x, y);
14775       TEST_DrawLevelField(x, y);
14776     }
14777     else if (element == EL_TIME_ORB_FULL)
14778     {
14779       Tile[x][y] = EL_TIME_ORB_EMPTY;
14780
14781       if (level.time > 0 || level.use_time_orb_bug)
14782       {
14783         TimeLeft += level.time_orb_time;
14784         game.no_level_time_limit = FALSE;
14785
14786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14787
14788         DisplayGameControlValues();
14789       }
14790
14791       ResetGfxAnimation(x, y);
14792       TEST_DrawLevelField(x, y);
14793     }
14794     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14795              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14796     {
14797       int xx, yy;
14798
14799       game.ball_active = !game.ball_active;
14800
14801       SCAN_PLAYFIELD(xx, yy)
14802       {
14803         int e = Tile[xx][yy];
14804
14805         if (game.ball_active)
14806         {
14807           if (e == EL_EMC_MAGIC_BALL)
14808             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14809           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14810             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14811         }
14812         else
14813         {
14814           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14815             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14816           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14817             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14818         }
14819       }
14820     }
14821
14822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14823                                         player->index_bit, dig_side);
14824
14825     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14826                                         player->index_bit, dig_side);
14827
14828     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14829                                         player->index_bit, dig_side);
14830
14831     return MP_ACTION;
14832   }
14833   else
14834   {
14835     if (!PLAYER_SWITCHING(player, x, y))
14836     {
14837       player->is_switching = TRUE;
14838       player->switch_x = x;
14839       player->switch_y = y;
14840
14841       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14842                                  player->index_bit, dig_side);
14843       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14844                                           player->index_bit, dig_side);
14845
14846       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14847                                  player->index_bit, dig_side);
14848       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14849                                           player->index_bit, dig_side);
14850     }
14851
14852     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14853                                player->index_bit, dig_side);
14854     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14855                                         player->index_bit, dig_side);
14856
14857     return MP_NO_ACTION;
14858   }
14859
14860   player->push_delay = -1;
14861
14862   if (is_player)                // function can also be called by EL_PENGUIN
14863   {
14864     if (Tile[x][y] != element)          // really digged/collected something
14865     {
14866       player->is_collecting = !player->is_digging;
14867       player->is_active = TRUE;
14868
14869       player->last_removed_element = element;
14870     }
14871   }
14872
14873   return MP_MOVING;
14874 }
14875
14876 static boolean DigFieldByCE(int x, int y, int digging_element)
14877 {
14878   int element = Tile[x][y];
14879
14880   if (!IS_FREE(x, y))
14881   {
14882     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14883                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14884                   ACTION_BREAKING);
14885
14886     // no element can dig solid indestructible elements
14887     if (IS_INDESTRUCTIBLE(element) &&
14888         !IS_DIGGABLE(element) &&
14889         !IS_COLLECTIBLE(element))
14890       return FALSE;
14891
14892     if (AmoebaNr[x][y] &&
14893         (element == EL_AMOEBA_FULL ||
14894          element == EL_BD_AMOEBA ||
14895          element == EL_AMOEBA_GROWING))
14896     {
14897       AmoebaCnt[AmoebaNr[x][y]]--;
14898       AmoebaCnt2[AmoebaNr[x][y]]--;
14899     }
14900
14901     if (IS_MOVING(x, y))
14902       RemoveMovingField(x, y);
14903     else
14904     {
14905       RemoveField(x, y);
14906       TEST_DrawLevelField(x, y);
14907     }
14908
14909     // if digged element was about to explode, prevent the explosion
14910     ExplodeField[x][y] = EX_TYPE_NONE;
14911
14912     PlayLevelSoundAction(x, y, action);
14913   }
14914
14915   Store[x][y] = EL_EMPTY;
14916
14917   // this makes it possible to leave the removed element again
14918   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14919     Store[x][y] = element;
14920
14921   return TRUE;
14922 }
14923
14924 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14925 {
14926   int jx = player->jx, jy = player->jy;
14927   int x = jx + dx, y = jy + dy;
14928   int snap_direction = (dx == -1 ? MV_LEFT  :
14929                         dx == +1 ? MV_RIGHT :
14930                         dy == -1 ? MV_UP    :
14931                         dy == +1 ? MV_DOWN  : MV_NONE);
14932   boolean can_continue_snapping = (level.continuous_snapping &&
14933                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14934
14935   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14936     return FALSE;
14937
14938   if (!player->active || !IN_LEV_FIELD(x, y))
14939     return FALSE;
14940
14941   if (dx && dy)
14942     return FALSE;
14943
14944   if (!dx && !dy)
14945   {
14946     if (player->MovPos == 0)
14947       player->is_pushing = FALSE;
14948
14949     player->is_snapping = FALSE;
14950
14951     if (player->MovPos == 0)
14952     {
14953       player->is_moving = FALSE;
14954       player->is_digging = FALSE;
14955       player->is_collecting = FALSE;
14956     }
14957
14958     return FALSE;
14959   }
14960
14961   // prevent snapping with already pressed snap key when not allowed
14962   if (player->is_snapping && !can_continue_snapping)
14963     return FALSE;
14964
14965   player->MovDir = snap_direction;
14966
14967   if (player->MovPos == 0)
14968   {
14969     player->is_moving = FALSE;
14970     player->is_digging = FALSE;
14971     player->is_collecting = FALSE;
14972   }
14973
14974   player->is_dropping = FALSE;
14975   player->is_dropping_pressed = FALSE;
14976   player->drop_pressed_delay = 0;
14977
14978   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14979     return FALSE;
14980
14981   player->is_snapping = TRUE;
14982   player->is_active = TRUE;
14983
14984   if (player->MovPos == 0)
14985   {
14986     player->is_moving = FALSE;
14987     player->is_digging = FALSE;
14988     player->is_collecting = FALSE;
14989   }
14990
14991   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14992     TEST_DrawLevelField(player->last_jx, player->last_jy);
14993
14994   TEST_DrawLevelField(x, y);
14995
14996   return TRUE;
14997 }
14998
14999 static boolean DropElement(struct PlayerInfo *player)
15000 {
15001   int old_element, new_element;
15002   int dropx = player->jx, dropy = player->jy;
15003   int drop_direction = player->MovDir;
15004   int drop_side = drop_direction;
15005   int drop_element = get_next_dropped_element(player);
15006
15007   /* do not drop an element on top of another element; when holding drop key
15008      pressed without moving, dropped element must move away before the next
15009      element can be dropped (this is especially important if the next element
15010      is dynamite, which can be placed on background for historical reasons) */
15011   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15012     return MP_ACTION;
15013
15014   if (IS_THROWABLE(drop_element))
15015   {
15016     dropx += GET_DX_FROM_DIR(drop_direction);
15017     dropy += GET_DY_FROM_DIR(drop_direction);
15018
15019     if (!IN_LEV_FIELD(dropx, dropy))
15020       return FALSE;
15021   }
15022
15023   old_element = Tile[dropx][dropy];     // old element at dropping position
15024   new_element = drop_element;           // default: no change when dropping
15025
15026   // check if player is active, not moving and ready to drop
15027   if (!player->active || player->MovPos || player->drop_delay > 0)
15028     return FALSE;
15029
15030   // check if player has anything that can be dropped
15031   if (new_element == EL_UNDEFINED)
15032     return FALSE;
15033
15034   // only set if player has anything that can be dropped
15035   player->is_dropping_pressed = TRUE;
15036
15037   // check if drop key was pressed long enough for EM style dynamite
15038   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15039     return FALSE;
15040
15041   // check if anything can be dropped at the current position
15042   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15043     return FALSE;
15044
15045   // collected custom elements can only be dropped on empty fields
15046   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15047     return FALSE;
15048
15049   if (old_element != EL_EMPTY)
15050     Back[dropx][dropy] = old_element;   // store old element on this field
15051
15052   ResetGfxAnimation(dropx, dropy);
15053   ResetRandomAnimationValue(dropx, dropy);
15054
15055   if (player->inventory_size > 0 ||
15056       player->inventory_infinite_element != EL_UNDEFINED)
15057   {
15058     if (player->inventory_size > 0)
15059     {
15060       player->inventory_size--;
15061
15062       DrawGameDoorValues();
15063
15064       if (new_element == EL_DYNAMITE)
15065         new_element = EL_DYNAMITE_ACTIVE;
15066       else if (new_element == EL_EM_DYNAMITE)
15067         new_element = EL_EM_DYNAMITE_ACTIVE;
15068       else if (new_element == EL_SP_DISK_RED)
15069         new_element = EL_SP_DISK_RED_ACTIVE;
15070     }
15071
15072     Tile[dropx][dropy] = new_element;
15073
15074     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15075       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15076                           el2img(Tile[dropx][dropy]), 0);
15077
15078     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15079
15080     // needed if previous element just changed to "empty" in the last frame
15081     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15082
15083     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15084                                player->index_bit, drop_side);
15085     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15086                                         CE_PLAYER_DROPS_X,
15087                                         player->index_bit, drop_side);
15088
15089     TestIfElementTouchesCustomElement(dropx, dropy);
15090   }
15091   else          // player is dropping a dyna bomb
15092   {
15093     player->dynabombs_left--;
15094
15095     Tile[dropx][dropy] = new_element;
15096
15097     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15098       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15099                           el2img(Tile[dropx][dropy]), 0);
15100
15101     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15102   }
15103
15104   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15105     InitField_WithBug1(dropx, dropy, FALSE);
15106
15107   new_element = Tile[dropx][dropy];     // element might have changed
15108
15109   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15110       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15111   {
15112     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15113       MovDir[dropx][dropy] = drop_direction;
15114
15115     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15116
15117     // do not cause impact style collision by dropping elements that can fall
15118     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15119   }
15120
15121   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15122   player->is_dropping = TRUE;
15123
15124   player->drop_pressed_delay = 0;
15125   player->is_dropping_pressed = FALSE;
15126
15127   player->drop_x = dropx;
15128   player->drop_y = dropy;
15129
15130   return TRUE;
15131 }
15132
15133 // ----------------------------------------------------------------------------
15134 // game sound playing functions
15135 // ----------------------------------------------------------------------------
15136
15137 static int *loop_sound_frame = NULL;
15138 static int *loop_sound_volume = NULL;
15139
15140 void InitPlayLevelSound(void)
15141 {
15142   int num_sounds = getSoundListSize();
15143
15144   checked_free(loop_sound_frame);
15145   checked_free(loop_sound_volume);
15146
15147   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15148   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15149 }
15150
15151 static void PlayLevelSound(int x, int y, int nr)
15152 {
15153   int sx = SCREENX(x), sy = SCREENY(y);
15154   int volume, stereo_position;
15155   int max_distance = 8;
15156   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15157
15158   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15159       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15160     return;
15161
15162   if (!IN_LEV_FIELD(x, y) ||
15163       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15164       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15165     return;
15166
15167   volume = SOUND_MAX_VOLUME;
15168
15169   if (!IN_SCR_FIELD(sx, sy))
15170   {
15171     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15172     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15173
15174     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15175   }
15176
15177   stereo_position = (SOUND_MAX_LEFT +
15178                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15179                      (SCR_FIELDX + 2 * max_distance));
15180
15181   if (IS_LOOP_SOUND(nr))
15182   {
15183     /* This assures that quieter loop sounds do not overwrite louder ones,
15184        while restarting sound volume comparison with each new game frame. */
15185
15186     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15187       return;
15188
15189     loop_sound_volume[nr] = volume;
15190     loop_sound_frame[nr] = FrameCounter;
15191   }
15192
15193   PlaySoundExt(nr, volume, stereo_position, type);
15194 }
15195
15196 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15197 {
15198   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15199                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15200                  y < LEVELY(BY1) ? LEVELY(BY1) :
15201                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15202                  sound_action);
15203 }
15204
15205 static void PlayLevelSoundAction(int x, int y, int action)
15206 {
15207   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15208 }
15209
15210 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15211 {
15212   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15213
15214   if (sound_effect != SND_UNDEFINED)
15215     PlayLevelSound(x, y, sound_effect);
15216 }
15217
15218 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15219                                               int action)
15220 {
15221   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15222
15223   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15224     PlayLevelSound(x, y, sound_effect);
15225 }
15226
15227 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15228 {
15229   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15230
15231   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15232     PlayLevelSound(x, y, sound_effect);
15233 }
15234
15235 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15236 {
15237   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15238
15239   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15240     StopSound(sound_effect);
15241 }
15242
15243 static int getLevelMusicNr(void)
15244 {
15245   if (levelset.music[level_nr] != MUS_UNDEFINED)
15246     return levelset.music[level_nr];            // from config file
15247   else
15248     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15249 }
15250
15251 static void FadeLevelSounds(void)
15252 {
15253   FadeSounds();
15254 }
15255
15256 static void FadeLevelMusic(void)
15257 {
15258   int music_nr = getLevelMusicNr();
15259   char *curr_music = getCurrentlyPlayingMusicFilename();
15260   char *next_music = getMusicInfoEntryFilename(music_nr);
15261
15262   if (!strEqual(curr_music, next_music))
15263     FadeMusic();
15264 }
15265
15266 void FadeLevelSoundsAndMusic(void)
15267 {
15268   FadeLevelSounds();
15269   FadeLevelMusic();
15270 }
15271
15272 static void PlayLevelMusic(void)
15273 {
15274   int music_nr = getLevelMusicNr();
15275   char *curr_music = getCurrentlyPlayingMusicFilename();
15276   char *next_music = getMusicInfoEntryFilename(music_nr);
15277
15278   if (!strEqual(curr_music, next_music))
15279     PlayMusicLoop(music_nr);
15280 }
15281
15282 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15283 {
15284   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15285   int offset = 0;
15286   int x = xx - offset;
15287   int y = yy - offset;
15288
15289   switch (sample)
15290   {
15291     case SOUND_blank:
15292       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15293       break;
15294
15295     case SOUND_roll:
15296       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15297       break;
15298
15299     case SOUND_stone:
15300       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15301       break;
15302
15303     case SOUND_nut:
15304       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15305       break;
15306
15307     case SOUND_crack:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15309       break;
15310
15311     case SOUND_bug:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15313       break;
15314
15315     case SOUND_tank:
15316       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15317       break;
15318
15319     case SOUND_android_clone:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15321       break;
15322
15323     case SOUND_android_move:
15324       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15325       break;
15326
15327     case SOUND_spring:
15328       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15329       break;
15330
15331     case SOUND_slurp:
15332       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15333       break;
15334
15335     case SOUND_eater:
15336       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15337       break;
15338
15339     case SOUND_eater_eat:
15340       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15341       break;
15342
15343     case SOUND_alien:
15344       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15345       break;
15346
15347     case SOUND_collect:
15348       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15349       break;
15350
15351     case SOUND_diamond:
15352       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15353       break;
15354
15355     case SOUND_squash:
15356       // !!! CHECK THIS !!!
15357 #if 1
15358       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15359 #else
15360       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15361 #endif
15362       break;
15363
15364     case SOUND_wonderfall:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15366       break;
15367
15368     case SOUND_drip:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15370       break;
15371
15372     case SOUND_push:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15374       break;
15375
15376     case SOUND_dirt:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15378       break;
15379
15380     case SOUND_acid:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15382       break;
15383
15384     case SOUND_ball:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15386       break;
15387
15388     case SOUND_slide:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15390       break;
15391
15392     case SOUND_wonder:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15394       break;
15395
15396     case SOUND_door:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15398       break;
15399
15400     case SOUND_exit_open:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15402       break;
15403
15404     case SOUND_exit_leave:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15406       break;
15407
15408     case SOUND_dynamite:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15410       break;
15411
15412     case SOUND_tick:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15414       break;
15415
15416     case SOUND_press:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15418       break;
15419
15420     case SOUND_wheel:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15422       break;
15423
15424     case SOUND_boom:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15426       break;
15427
15428     case SOUND_die:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15430       break;
15431
15432     case SOUND_time:
15433       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15434       break;
15435
15436     default:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15438       break;
15439   }
15440 }
15441
15442 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15443 {
15444   int element = map_element_SP_to_RND(element_sp);
15445   int action = map_action_SP_to_RND(action_sp);
15446   int offset = (setup.sp_show_border_elements ? 0 : 1);
15447   int x = xx - offset;
15448   int y = yy - offset;
15449
15450   PlayLevelSoundElementAction(x, y, element, action);
15451 }
15452
15453 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15454 {
15455   int element = map_element_MM_to_RND(element_mm);
15456   int action = map_action_MM_to_RND(action_mm);
15457   int offset = 0;
15458   int x = xx - offset;
15459   int y = yy - offset;
15460
15461   if (!IS_MM_ELEMENT(element))
15462     element = EL_MM_DEFAULT;
15463
15464   PlayLevelSoundElementAction(x, y, element, action);
15465 }
15466
15467 void PlaySound_MM(int sound_mm)
15468 {
15469   int sound = map_sound_MM_to_RND(sound_mm);
15470
15471   if (sound == SND_UNDEFINED)
15472     return;
15473
15474   PlaySound(sound);
15475 }
15476
15477 void PlaySoundLoop_MM(int sound_mm)
15478 {
15479   int sound = map_sound_MM_to_RND(sound_mm);
15480
15481   if (sound == SND_UNDEFINED)
15482     return;
15483
15484   PlaySoundLoop(sound);
15485 }
15486
15487 void StopSound_MM(int sound_mm)
15488 {
15489   int sound = map_sound_MM_to_RND(sound_mm);
15490
15491   if (sound == SND_UNDEFINED)
15492     return;
15493
15494   StopSound(sound);
15495 }
15496
15497 void RaiseScore(int value)
15498 {
15499   game.score += value;
15500
15501   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15502
15503   DisplayGameControlValues();
15504 }
15505
15506 void RaiseScoreElement(int element)
15507 {
15508   switch (element)
15509   {
15510     case EL_EMERALD:
15511     case EL_BD_DIAMOND:
15512     case EL_EMERALD_YELLOW:
15513     case EL_EMERALD_RED:
15514     case EL_EMERALD_PURPLE:
15515     case EL_SP_INFOTRON:
15516       RaiseScore(level.score[SC_EMERALD]);
15517       break;
15518     case EL_DIAMOND:
15519       RaiseScore(level.score[SC_DIAMOND]);
15520       break;
15521     case EL_CRYSTAL:
15522       RaiseScore(level.score[SC_CRYSTAL]);
15523       break;
15524     case EL_PEARL:
15525       RaiseScore(level.score[SC_PEARL]);
15526       break;
15527     case EL_BUG:
15528     case EL_BD_BUTTERFLY:
15529     case EL_SP_ELECTRON:
15530       RaiseScore(level.score[SC_BUG]);
15531       break;
15532     case EL_SPACESHIP:
15533     case EL_BD_FIREFLY:
15534     case EL_SP_SNIKSNAK:
15535       RaiseScore(level.score[SC_SPACESHIP]);
15536       break;
15537     case EL_YAMYAM:
15538     case EL_DARK_YAMYAM:
15539       RaiseScore(level.score[SC_YAMYAM]);
15540       break;
15541     case EL_ROBOT:
15542       RaiseScore(level.score[SC_ROBOT]);
15543       break;
15544     case EL_PACMAN:
15545       RaiseScore(level.score[SC_PACMAN]);
15546       break;
15547     case EL_NUT:
15548       RaiseScore(level.score[SC_NUT]);
15549       break;
15550     case EL_DYNAMITE:
15551     case EL_EM_DYNAMITE:
15552     case EL_SP_DISK_RED:
15553     case EL_DYNABOMB_INCREASE_NUMBER:
15554     case EL_DYNABOMB_INCREASE_SIZE:
15555     case EL_DYNABOMB_INCREASE_POWER:
15556       RaiseScore(level.score[SC_DYNAMITE]);
15557       break;
15558     case EL_SHIELD_NORMAL:
15559     case EL_SHIELD_DEADLY:
15560       RaiseScore(level.score[SC_SHIELD]);
15561       break;
15562     case EL_EXTRA_TIME:
15563       RaiseScore(level.extra_time_score);
15564       break;
15565     case EL_KEY_1:
15566     case EL_KEY_2:
15567     case EL_KEY_3:
15568     case EL_KEY_4:
15569     case EL_EM_KEY_1:
15570     case EL_EM_KEY_2:
15571     case EL_EM_KEY_3:
15572     case EL_EM_KEY_4:
15573     case EL_EMC_KEY_5:
15574     case EL_EMC_KEY_6:
15575     case EL_EMC_KEY_7:
15576     case EL_EMC_KEY_8:
15577     case EL_DC_KEY_WHITE:
15578       RaiseScore(level.score[SC_KEY]);
15579       break;
15580     default:
15581       RaiseScore(element_info[element].collect_score);
15582       break;
15583   }
15584 }
15585
15586 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15587 {
15588   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15589   {
15590     if (!quick_quit)
15591     {
15592       // prevent short reactivation of overlay buttons while closing door
15593       SetOverlayActive(FALSE);
15594       UnmapGameButtons();
15595
15596       // door may still be open due to skipped or envelope style request
15597       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15598     }
15599
15600     if (network.enabled)
15601       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15602     else
15603     {
15604       if (quick_quit)
15605         FadeSkipNextFadeIn();
15606
15607       SetGameStatus(GAME_MODE_MAIN);
15608
15609       DrawMainMenu();
15610     }
15611   }
15612   else          // continue playing the game
15613   {
15614     if (tape.playing && tape.deactivate_display)
15615       TapeDeactivateDisplayOff(TRUE);
15616
15617     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15618
15619     if (tape.playing && tape.deactivate_display)
15620       TapeDeactivateDisplayOn();
15621   }
15622 }
15623
15624 void RequestQuitGame(boolean escape_key_pressed)
15625 {
15626   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15627   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15628                         level_editor_test_game);
15629   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15630                           quick_quit || score_info_tape_play);
15631
15632   RequestQuitGameExt(skip_request, quick_quit,
15633                      "Do you really want to quit the game?");
15634 }
15635
15636 void RequestRestartGame(char *message)
15637 {
15638   game.restart_game_message = NULL;
15639
15640   boolean has_started_game = hasStartedNetworkGame();
15641   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15642   int door_state = DOOR_CLOSE_1;
15643
15644   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15645   {
15646     CloseDoor(door_state);
15647
15648     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15649   }
15650   else
15651   {
15652     // if game was invoked from level editor, also close tape recorder door
15653     if (level_editor_test_game)
15654       door_state = DOOR_CLOSE_ALL;
15655
15656     CloseDoor(door_state);
15657
15658     SetGameStatus(GAME_MODE_MAIN);
15659
15660     DrawMainMenu();
15661   }
15662 }
15663
15664 static char *getRestartGameMessage(void)
15665 {
15666   boolean play_again = hasStartedNetworkGame();
15667   static char message[MAX_OUTPUT_LINESIZE];
15668   char *game_over_text = "Game over!";
15669   char *play_again_text = " Play it again?";
15670
15671   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15672       game_mm.game_over_message != NULL)
15673     game_over_text = game_mm.game_over_message;
15674
15675   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15676            (play_again ? play_again_text : ""));
15677
15678   return message;
15679 }
15680
15681 void CheckGameOver(void)
15682 {
15683   static boolean last_game_over = FALSE;
15684   static int game_over_delay = 0;
15685   int game_over_delay_value = 50;
15686   boolean game_over = checkGameFailed();
15687
15688   // do not handle game over if request dialog is already active
15689   if (game.request_active)
15690     return;
15691
15692   // do not ask to play again if game was never actually played
15693   if (!game.GamePlayed)
15694     return;
15695
15696   if (!game_over)
15697   {
15698     last_game_over = FALSE;
15699     game_over_delay = game_over_delay_value;
15700
15701     return;
15702   }
15703
15704   if (game_over_delay > 0)
15705   {
15706     if (game_over_delay == game_over_delay_value / 2)
15707       PlaySound(SND_GAME_LOSING);
15708
15709     game_over_delay--;
15710
15711     return;
15712   }
15713
15714   if (last_game_over != game_over)
15715     game.restart_game_message = getRestartGameMessage();
15716
15717   last_game_over = game_over;
15718 }
15719
15720 boolean checkGameSolved(void)
15721 {
15722   // set for all game engines if level was solved
15723   return game.LevelSolved_GameEnd;
15724 }
15725
15726 boolean checkGameFailed(void)
15727 {
15728   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15729     return (game_em.game_over && !game_em.level_solved);
15730   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15731     return (game_sp.game_over && !game_sp.level_solved);
15732   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15733     return (game_mm.game_over && !game_mm.level_solved);
15734   else                          // GAME_ENGINE_TYPE_RND
15735     return (game.GameOver && !game.LevelSolved);
15736 }
15737
15738 boolean checkGameEnded(void)
15739 {
15740   return (checkGameSolved() || checkGameFailed());
15741 }
15742
15743
15744 // ----------------------------------------------------------------------------
15745 // random generator functions
15746 // ----------------------------------------------------------------------------
15747
15748 unsigned int InitEngineRandom_RND(int seed)
15749 {
15750   game.num_random_calls = 0;
15751
15752   return InitEngineRandom(seed);
15753 }
15754
15755 unsigned int RND(int max)
15756 {
15757   if (max > 0)
15758   {
15759     game.num_random_calls++;
15760
15761     return GetEngineRandom(max);
15762   }
15763
15764   return 0;
15765 }
15766
15767
15768 // ----------------------------------------------------------------------------
15769 // game engine snapshot handling functions
15770 // ----------------------------------------------------------------------------
15771
15772 struct EngineSnapshotInfo
15773 {
15774   // runtime values for custom element collect score
15775   int collect_score[NUM_CUSTOM_ELEMENTS];
15776
15777   // runtime values for group element choice position
15778   int choice_pos[NUM_GROUP_ELEMENTS];
15779
15780   // runtime values for belt position animations
15781   int belt_graphic[4][NUM_BELT_PARTS];
15782   int belt_anim_mode[4][NUM_BELT_PARTS];
15783 };
15784
15785 static struct EngineSnapshotInfo engine_snapshot_rnd;
15786 static char *snapshot_level_identifier = NULL;
15787 static int snapshot_level_nr = -1;
15788
15789 static void SaveEngineSnapshotValues_RND(void)
15790 {
15791   static int belt_base_active_element[4] =
15792   {
15793     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15794     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15795     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15796     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15797   };
15798   int i, j;
15799
15800   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15801   {
15802     int element = EL_CUSTOM_START + i;
15803
15804     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15805   }
15806
15807   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15808   {
15809     int element = EL_GROUP_START + i;
15810
15811     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15812   }
15813
15814   for (i = 0; i < 4; i++)
15815   {
15816     for (j = 0; j < NUM_BELT_PARTS; j++)
15817     {
15818       int element = belt_base_active_element[i] + j;
15819       int graphic = el2img(element);
15820       int anim_mode = graphic_info[graphic].anim_mode;
15821
15822       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15823       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15824     }
15825   }
15826 }
15827
15828 static void LoadEngineSnapshotValues_RND(void)
15829 {
15830   unsigned int num_random_calls = game.num_random_calls;
15831   int i, j;
15832
15833   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15834   {
15835     int element = EL_CUSTOM_START + i;
15836
15837     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15838   }
15839
15840   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15841   {
15842     int element = EL_GROUP_START + i;
15843
15844     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15845   }
15846
15847   for (i = 0; i < 4; i++)
15848   {
15849     for (j = 0; j < NUM_BELT_PARTS; j++)
15850     {
15851       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15852       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15853
15854       graphic_info[graphic].anim_mode = anim_mode;
15855     }
15856   }
15857
15858   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15859   {
15860     InitRND(tape.random_seed);
15861     for (i = 0; i < num_random_calls; i++)
15862       RND(1);
15863   }
15864
15865   if (game.num_random_calls != num_random_calls)
15866   {
15867     Error("number of random calls out of sync");
15868     Error("number of random calls should be %d", num_random_calls);
15869     Error("number of random calls is %d", game.num_random_calls);
15870
15871     Fail("this should not happen -- please debug");
15872   }
15873 }
15874
15875 void FreeEngineSnapshotSingle(void)
15876 {
15877   FreeSnapshotSingle();
15878
15879   setString(&snapshot_level_identifier, NULL);
15880   snapshot_level_nr = -1;
15881 }
15882
15883 void FreeEngineSnapshotList(void)
15884 {
15885   FreeSnapshotList();
15886 }
15887
15888 static ListNode *SaveEngineSnapshotBuffers(void)
15889 {
15890   ListNode *buffers = NULL;
15891
15892   // copy some special values to a structure better suited for the snapshot
15893
15894   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15895     SaveEngineSnapshotValues_RND();
15896   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15897     SaveEngineSnapshotValues_EM();
15898   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15899     SaveEngineSnapshotValues_SP(&buffers);
15900   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15901     SaveEngineSnapshotValues_MM();
15902
15903   // save values stored in special snapshot structure
15904
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15906     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15908     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15909   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15910     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15911   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15912     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15913
15914   // save further RND engine values
15915
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15917   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15918   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15919
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15921   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15924   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15925
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15929
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15931
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15934
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15953
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15956
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15960
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15963
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15973
15974 #if 0
15975   ListNode *node = engine_snapshot_list_rnd;
15976   int num_bytes = 0;
15977
15978   while (node != NULL)
15979   {
15980     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15981
15982     node = node->next;
15983   }
15984
15985   Debug("game:playing:SaveEngineSnapshotBuffers",
15986         "size of engine snapshot: %d bytes", num_bytes);
15987 #endif
15988
15989   return buffers;
15990 }
15991
15992 void SaveEngineSnapshotSingle(void)
15993 {
15994   ListNode *buffers = SaveEngineSnapshotBuffers();
15995
15996   // finally save all snapshot buffers to single snapshot
15997   SaveSnapshotSingle(buffers);
15998
15999   // save level identification information
16000   setString(&snapshot_level_identifier, leveldir_current->identifier);
16001   snapshot_level_nr = level_nr;
16002 }
16003
16004 boolean CheckSaveEngineSnapshotToList(void)
16005 {
16006   boolean save_snapshot =
16007     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16008      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16009       game.snapshot.changed_action) ||
16010      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16011       game.snapshot.collected_item));
16012
16013   game.snapshot.changed_action = FALSE;
16014   game.snapshot.collected_item = FALSE;
16015   game.snapshot.save_snapshot = save_snapshot;
16016
16017   return save_snapshot;
16018 }
16019
16020 void SaveEngineSnapshotToList(void)
16021 {
16022   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16023       tape.quick_resume)
16024     return;
16025
16026   ListNode *buffers = SaveEngineSnapshotBuffers();
16027
16028   // finally save all snapshot buffers to snapshot list
16029   SaveSnapshotToList(buffers);
16030 }
16031
16032 void SaveEngineSnapshotToListInitial(void)
16033 {
16034   FreeEngineSnapshotList();
16035
16036   SaveEngineSnapshotToList();
16037 }
16038
16039 static void LoadEngineSnapshotValues(void)
16040 {
16041   // restore special values from snapshot structure
16042
16043   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16044     LoadEngineSnapshotValues_RND();
16045   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16046     LoadEngineSnapshotValues_EM();
16047   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16048     LoadEngineSnapshotValues_SP();
16049   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16050     LoadEngineSnapshotValues_MM();
16051 }
16052
16053 void LoadEngineSnapshotSingle(void)
16054 {
16055   LoadSnapshotSingle();
16056
16057   LoadEngineSnapshotValues();
16058 }
16059
16060 static void LoadEngineSnapshot_Undo(int steps)
16061 {
16062   LoadSnapshotFromList_Older(steps);
16063
16064   LoadEngineSnapshotValues();
16065 }
16066
16067 static void LoadEngineSnapshot_Redo(int steps)
16068 {
16069   LoadSnapshotFromList_Newer(steps);
16070
16071   LoadEngineSnapshotValues();
16072 }
16073
16074 boolean CheckEngineSnapshotSingle(void)
16075 {
16076   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16077           snapshot_level_nr == level_nr);
16078 }
16079
16080 boolean CheckEngineSnapshotList(void)
16081 {
16082   return CheckSnapshotList();
16083 }
16084
16085
16086 // ---------- new game button stuff -------------------------------------------
16087
16088 static struct
16089 {
16090   int graphic;
16091   struct XY *pos;
16092   int gadget_id;
16093   boolean *setup_value;
16094   boolean allowed_on_tape;
16095   boolean is_touch_button;
16096   char *infotext;
16097 } gamebutton_info[NUM_GAME_BUTTONS] =
16098 {
16099   {
16100     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16101     GAME_CTRL_ID_STOP,                          NULL,
16102     TRUE, FALSE,                                "stop game"
16103   },
16104   {
16105     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16106     GAME_CTRL_ID_PAUSE,                         NULL,
16107     TRUE, FALSE,                                "pause game"
16108   },
16109   {
16110     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16111     GAME_CTRL_ID_PLAY,                          NULL,
16112     TRUE, FALSE,                                "play game"
16113   },
16114   {
16115     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16116     GAME_CTRL_ID_UNDO,                          NULL,
16117     TRUE, FALSE,                                "undo step"
16118   },
16119   {
16120     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16121     GAME_CTRL_ID_REDO,                          NULL,
16122     TRUE, FALSE,                                "redo step"
16123   },
16124   {
16125     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16126     GAME_CTRL_ID_SAVE,                          NULL,
16127     TRUE, FALSE,                                "save game"
16128   },
16129   {
16130     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16131     GAME_CTRL_ID_PAUSE2,                        NULL,
16132     TRUE, FALSE,                                "pause game"
16133   },
16134   {
16135     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16136     GAME_CTRL_ID_LOAD,                          NULL,
16137     TRUE, FALSE,                                "load game"
16138   },
16139   {
16140     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16141     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16142     FALSE, FALSE,                               "stop game"
16143   },
16144   {
16145     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16146     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16147     FALSE, FALSE,                               "pause game"
16148   },
16149   {
16150     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16151     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16152     FALSE, FALSE,                               "play game"
16153   },
16154   {
16155     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16156     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16157     FALSE, TRUE,                                "stop game"
16158   },
16159   {
16160     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16161     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16162     FALSE, TRUE,                                "pause game"
16163   },
16164   {
16165     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16166     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16167     TRUE, FALSE,                                "background music on/off"
16168   },
16169   {
16170     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16171     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16172     TRUE, FALSE,                                "sound loops on/off"
16173   },
16174   {
16175     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16176     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16177     TRUE, FALSE,                                "normal sounds on/off"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16181     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16182     FALSE, FALSE,                               "background music on/off"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16186     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16187     FALSE, FALSE,                               "sound loops on/off"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16191     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16192     FALSE, FALSE,                               "normal sounds on/off"
16193   }
16194 };
16195
16196 void CreateGameButtons(void)
16197 {
16198   int i;
16199
16200   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16201   {
16202     int graphic = gamebutton_info[i].graphic;
16203     struct GraphicInfo *gfx = &graphic_info[graphic];
16204     struct XY *pos = gamebutton_info[i].pos;
16205     struct GadgetInfo *gi;
16206     int button_type;
16207     boolean checked;
16208     unsigned int event_mask;
16209     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16210     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16211     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16212     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16213     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16214     int gd_x   = gfx->src_x;
16215     int gd_y   = gfx->src_y;
16216     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16217     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16218     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16219     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16220     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16221     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16222     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16223     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16224     int id = i;
16225
16226     // do not use touch buttons if overlay touch buttons are disabled
16227     if (is_touch_button && !setup.touch.overlay_buttons)
16228       continue;
16229
16230     if (gfx->bitmap == NULL)
16231     {
16232       game_gadget[id] = NULL;
16233
16234       continue;
16235     }
16236
16237     if (id == GAME_CTRL_ID_STOP ||
16238         id == GAME_CTRL_ID_PANEL_STOP ||
16239         id == GAME_CTRL_ID_TOUCH_STOP ||
16240         id == GAME_CTRL_ID_PLAY ||
16241         id == GAME_CTRL_ID_PANEL_PLAY ||
16242         id == GAME_CTRL_ID_SAVE ||
16243         id == GAME_CTRL_ID_LOAD)
16244     {
16245       button_type = GD_TYPE_NORMAL_BUTTON;
16246       checked = FALSE;
16247       event_mask = GD_EVENT_RELEASED;
16248     }
16249     else if (id == GAME_CTRL_ID_UNDO ||
16250              id == GAME_CTRL_ID_REDO)
16251     {
16252       button_type = GD_TYPE_NORMAL_BUTTON;
16253       checked = FALSE;
16254       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16255     }
16256     else
16257     {
16258       button_type = GD_TYPE_CHECK_BUTTON;
16259       checked = (gamebutton_info[i].setup_value != NULL ?
16260                  *gamebutton_info[i].setup_value : FALSE);
16261       event_mask = GD_EVENT_PRESSED;
16262     }
16263
16264     gi = CreateGadget(GDI_CUSTOM_ID, id,
16265                       GDI_IMAGE_ID, graphic,
16266                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16267                       GDI_X, base_x + x,
16268                       GDI_Y, base_y + y,
16269                       GDI_WIDTH, gfx->width,
16270                       GDI_HEIGHT, gfx->height,
16271                       GDI_TYPE, button_type,
16272                       GDI_STATE, GD_BUTTON_UNPRESSED,
16273                       GDI_CHECKED, checked,
16274                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16275                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16276                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16277                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16278                       GDI_DIRECT_DRAW, FALSE,
16279                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16280                       GDI_EVENT_MASK, event_mask,
16281                       GDI_CALLBACK_ACTION, HandleGameButtons,
16282                       GDI_END);
16283
16284     if (gi == NULL)
16285       Fail("cannot create gadget");
16286
16287     game_gadget[id] = gi;
16288   }
16289 }
16290
16291 void FreeGameButtons(void)
16292 {
16293   int i;
16294
16295   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16296     FreeGadget(game_gadget[i]);
16297 }
16298
16299 static void UnmapGameButtonsAtSamePosition(int id)
16300 {
16301   int i;
16302
16303   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16304     if (i != id &&
16305         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16306         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16307       UnmapGadget(game_gadget[i]);
16308 }
16309
16310 static void UnmapGameButtonsAtSamePosition_All(void)
16311 {
16312   if (setup.show_load_save_buttons)
16313   {
16314     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16315     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16316     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16317   }
16318   else if (setup.show_undo_redo_buttons)
16319   {
16320     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16321     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16323   }
16324   else
16325   {
16326     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16329
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16331     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16333   }
16334 }
16335
16336 void MapLoadSaveButtons(void)
16337 {
16338   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16339   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16340
16341   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16342   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16343 }
16344
16345 void MapUndoRedoButtons(void)
16346 {
16347   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16348   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16349
16350   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16351   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16352 }
16353
16354 void ModifyPauseButtons(void)
16355 {
16356   static int ids[] =
16357   {
16358     GAME_CTRL_ID_PAUSE,
16359     GAME_CTRL_ID_PAUSE2,
16360     GAME_CTRL_ID_PANEL_PAUSE,
16361     GAME_CTRL_ID_TOUCH_PAUSE,
16362     -1
16363   };
16364   int i;
16365
16366   for (i = 0; ids[i] > -1; i++)
16367     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16368 }
16369
16370 static void MapGameButtonsExt(boolean on_tape)
16371 {
16372   int i;
16373
16374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16375   {
16376     if ((i == GAME_CTRL_ID_UNDO ||
16377          i == GAME_CTRL_ID_REDO) &&
16378         game_status != GAME_MODE_PLAYING)
16379       continue;
16380
16381     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16382       MapGadget(game_gadget[i]);
16383   }
16384
16385   UnmapGameButtonsAtSamePosition_All();
16386
16387   RedrawGameButtons();
16388 }
16389
16390 static void UnmapGameButtonsExt(boolean on_tape)
16391 {
16392   int i;
16393
16394   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16395     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16396       UnmapGadget(game_gadget[i]);
16397 }
16398
16399 static void RedrawGameButtonsExt(boolean on_tape)
16400 {
16401   int i;
16402
16403   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16404     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16405       RedrawGadget(game_gadget[i]);
16406 }
16407
16408 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16409 {
16410   if (gi == NULL)
16411     return;
16412
16413   gi->checked = state;
16414 }
16415
16416 static void RedrawSoundButtonGadget(int id)
16417 {
16418   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16419              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16420              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16421              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16422              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16423              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16424              id);
16425
16426   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16427   RedrawGadget(game_gadget[id2]);
16428 }
16429
16430 void MapGameButtons(void)
16431 {
16432   MapGameButtonsExt(FALSE);
16433 }
16434
16435 void UnmapGameButtons(void)
16436 {
16437   UnmapGameButtonsExt(FALSE);
16438 }
16439
16440 void RedrawGameButtons(void)
16441 {
16442   RedrawGameButtonsExt(FALSE);
16443 }
16444
16445 void MapGameButtonsOnTape(void)
16446 {
16447   MapGameButtonsExt(TRUE);
16448 }
16449
16450 void UnmapGameButtonsOnTape(void)
16451 {
16452   UnmapGameButtonsExt(TRUE);
16453 }
16454
16455 void RedrawGameButtonsOnTape(void)
16456 {
16457   RedrawGameButtonsExt(TRUE);
16458 }
16459
16460 static void GameUndoRedoExt(void)
16461 {
16462   ClearPlayerAction();
16463
16464   tape.pausing = TRUE;
16465
16466   RedrawPlayfield();
16467   UpdateAndDisplayGameControlValues();
16468
16469   DrawCompleteVideoDisplay();
16470   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16471   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16472   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16473
16474   ModifyPauseButtons();
16475
16476   BackToFront();
16477 }
16478
16479 static void GameUndo(int steps)
16480 {
16481   if (!CheckEngineSnapshotList())
16482     return;
16483
16484   int tape_property_bits = tape.property_bits;
16485
16486   LoadEngineSnapshot_Undo(steps);
16487
16488   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16489
16490   GameUndoRedoExt();
16491 }
16492
16493 static void GameRedo(int steps)
16494 {
16495   if (!CheckEngineSnapshotList())
16496     return;
16497
16498   int tape_property_bits = tape.property_bits;
16499
16500   LoadEngineSnapshot_Redo(steps);
16501
16502   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16503
16504   GameUndoRedoExt();
16505 }
16506
16507 static void HandleGameButtonsExt(int id, int button)
16508 {
16509   static boolean game_undo_executed = FALSE;
16510   int steps = BUTTON_STEPSIZE(button);
16511   boolean handle_game_buttons =
16512     (game_status == GAME_MODE_PLAYING ||
16513      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16514
16515   if (!handle_game_buttons)
16516     return;
16517
16518   switch (id)
16519   {
16520     case GAME_CTRL_ID_STOP:
16521     case GAME_CTRL_ID_PANEL_STOP:
16522     case GAME_CTRL_ID_TOUCH_STOP:
16523       TapeStopGame();
16524
16525       break;
16526
16527     case GAME_CTRL_ID_PAUSE:
16528     case GAME_CTRL_ID_PAUSE2:
16529     case GAME_CTRL_ID_PANEL_PAUSE:
16530     case GAME_CTRL_ID_TOUCH_PAUSE:
16531       if (network.enabled && game_status == GAME_MODE_PLAYING)
16532       {
16533         if (tape.pausing)
16534           SendToServer_ContinuePlaying();
16535         else
16536           SendToServer_PausePlaying();
16537       }
16538       else
16539         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16540
16541       game_undo_executed = FALSE;
16542
16543       break;
16544
16545     case GAME_CTRL_ID_PLAY:
16546     case GAME_CTRL_ID_PANEL_PLAY:
16547       if (game_status == GAME_MODE_MAIN)
16548       {
16549         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16550       }
16551       else if (tape.pausing)
16552       {
16553         if (network.enabled)
16554           SendToServer_ContinuePlaying();
16555         else
16556           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16557       }
16558       break;
16559
16560     case GAME_CTRL_ID_UNDO:
16561       // Important: When using "save snapshot when collecting an item" mode,
16562       // load last (current) snapshot for first "undo" after pressing "pause"
16563       // (else the last-but-one snapshot would be loaded, because the snapshot
16564       // pointer already points to the last snapshot when pressing "pause",
16565       // which is fine for "every step/move" mode, but not for "every collect")
16566       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16567           !game_undo_executed)
16568         steps--;
16569
16570       game_undo_executed = TRUE;
16571
16572       GameUndo(steps);
16573       break;
16574
16575     case GAME_CTRL_ID_REDO:
16576       GameRedo(steps);
16577       break;
16578
16579     case GAME_CTRL_ID_SAVE:
16580       TapeQuickSave();
16581       break;
16582
16583     case GAME_CTRL_ID_LOAD:
16584       TapeQuickLoad();
16585       break;
16586
16587     case SOUND_CTRL_ID_MUSIC:
16588     case SOUND_CTRL_ID_PANEL_MUSIC:
16589       if (setup.sound_music)
16590       { 
16591         setup.sound_music = FALSE;
16592
16593         FadeMusic();
16594       }
16595       else if (audio.music_available)
16596       { 
16597         setup.sound = setup.sound_music = TRUE;
16598
16599         SetAudioMode(setup.sound);
16600
16601         if (game_status == GAME_MODE_PLAYING)
16602           PlayLevelMusic();
16603       }
16604
16605       RedrawSoundButtonGadget(id);
16606
16607       break;
16608
16609     case SOUND_CTRL_ID_LOOPS:
16610     case SOUND_CTRL_ID_PANEL_LOOPS:
16611       if (setup.sound_loops)
16612         setup.sound_loops = FALSE;
16613       else if (audio.loops_available)
16614       {
16615         setup.sound = setup.sound_loops = TRUE;
16616
16617         SetAudioMode(setup.sound);
16618       }
16619
16620       RedrawSoundButtonGadget(id);
16621
16622       break;
16623
16624     case SOUND_CTRL_ID_SIMPLE:
16625     case SOUND_CTRL_ID_PANEL_SIMPLE:
16626       if (setup.sound_simple)
16627         setup.sound_simple = FALSE;
16628       else if (audio.sound_available)
16629       {
16630         setup.sound = setup.sound_simple = TRUE;
16631
16632         SetAudioMode(setup.sound);
16633       }
16634
16635       RedrawSoundButtonGadget(id);
16636
16637       break;
16638
16639     default:
16640       break;
16641   }
16642 }
16643
16644 static void HandleGameButtons(struct GadgetInfo *gi)
16645 {
16646   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16647 }
16648
16649 void HandleSoundButtonKeys(Key key)
16650 {
16651   if (key == setup.shortcut.sound_simple)
16652     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16653   else if (key == setup.shortcut.sound_loops)
16654     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16655   else if (key == setup.shortcut.sound_music)
16656     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16657 }