68fb40c6e12dc3c2c27433c0955fb5ea7a4e086a
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_SCORE ||
2686           nr == GAME_PANEL_HIGHSCORE ||
2687           nr == GAME_PANEL_TIME)
2688       {
2689         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2690
2691         if (use_dynamic_size)           // use dynamic number of digits
2692         {
2693           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2694                               nr == GAME_PANEL_INVENTORY_COUNT ||
2695                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2696           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2697                           nr == GAME_PANEL_INVENTORY_COUNT ||
2698                           nr == GAME_PANEL_TIME ? 1 : 2);
2699           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2700                        nr == GAME_PANEL_INVENTORY_COUNT ||
2701                        nr == GAME_PANEL_TIME ? 3 : 5);
2702           int size2 = size1 + size_add;
2703           int font1 = pos->font;
2704           int font2 = pos->font_alt;
2705
2706           size = (value < value_change ? size1 : size2);
2707           font = (value < value_change ? font1 : font2);
2708         }
2709       }
2710
2711       // correct text size if "digits" is zero or less
2712       if (size <= 0)
2713         size = strlen(int2str(value, size));
2714
2715       // dynamically correct text alignment
2716       pos->width = size * getFontWidth(font);
2717
2718       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2719                   int2str(value, size), font, mask_mode);
2720     }
2721     else if (type == TYPE_ELEMENT)
2722     {
2723       int element, graphic;
2724       Bitmap *src_bitmap;
2725       int src_x, src_y;
2726       int width, height;
2727       int dst_x = PANEL_XPOS(pos);
2728       int dst_y = PANEL_YPOS(pos);
2729
2730       if (value != EL_UNDEFINED && value != EL_EMPTY)
2731       {
2732         element = value;
2733         graphic = el2panelimg(value);
2734
2735 #if 0
2736         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2737               element, EL_NAME(element), size);
2738 #endif
2739
2740         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2741           size = TILESIZE;
2742
2743         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2744                               &src_x, &src_y);
2745
2746         width  = graphic_info[graphic].width  * size / TILESIZE;
2747         height = graphic_info[graphic].height * size / TILESIZE;
2748
2749         if (draw_masked)
2750           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2751                            dst_x, dst_y);
2752         else
2753           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2754                      dst_x, dst_y);
2755       }
2756     }
2757     else if (type == TYPE_GRAPHIC)
2758     {
2759       int graphic        = gpc->graphic;
2760       int graphic_active = gpc->graphic_active;
2761       Bitmap *src_bitmap;
2762       int src_x, src_y;
2763       int width, height;
2764       int dst_x = PANEL_XPOS(pos);
2765       int dst_y = PANEL_YPOS(pos);
2766       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2767                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2768
2769       if (graphic != IMG_UNDEFINED && !skip)
2770       {
2771         if (pos->style == STYLE_REVERSE)
2772           value = 100 - value;
2773
2774         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2775
2776         if (pos->direction & MV_HORIZONTAL)
2777         {
2778           width  = graphic_info[graphic_active].width * value / 100;
2779           height = graphic_info[graphic_active].height;
2780
2781           if (pos->direction == MV_LEFT)
2782           {
2783             src_x += graphic_info[graphic_active].width - width;
2784             dst_x += graphic_info[graphic_active].width - width;
2785           }
2786         }
2787         else
2788         {
2789           width  = graphic_info[graphic_active].width;
2790           height = graphic_info[graphic_active].height * value / 100;
2791
2792           if (pos->direction == MV_UP)
2793           {
2794             src_y += graphic_info[graphic_active].height - height;
2795             dst_y += graphic_info[graphic_active].height - height;
2796           }
2797         }
2798
2799         if (draw_masked)
2800           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2801                            dst_x, dst_y);
2802         else
2803           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2804                      dst_x, dst_y);
2805
2806         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2807
2808         if (pos->direction & MV_HORIZONTAL)
2809         {
2810           if (pos->direction == MV_RIGHT)
2811           {
2812             src_x += width;
2813             dst_x += width;
2814           }
2815           else
2816           {
2817             dst_x = PANEL_XPOS(pos);
2818           }
2819
2820           width = graphic_info[graphic].width - width;
2821         }
2822         else
2823         {
2824           if (pos->direction == MV_DOWN)
2825           {
2826             src_y += height;
2827             dst_y += height;
2828           }
2829           else
2830           {
2831             dst_y = PANEL_YPOS(pos);
2832           }
2833
2834           height = graphic_info[graphic].height - height;
2835         }
2836
2837         if (draw_masked)
2838           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2839                            dst_x, dst_y);
2840         else
2841           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2842                      dst_x, dst_y);
2843       }
2844     }
2845     else if (type == TYPE_STRING)
2846     {
2847       boolean active = (value != 0);
2848       char *state_normal = "off";
2849       char *state_active = "on";
2850       char *state = (active ? state_active : state_normal);
2851       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2852                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2853                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2854                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2855
2856       if (nr == GAME_PANEL_GRAVITY_STATE)
2857       {
2858         int font1 = pos->font;          // (used for normal state)
2859         int font2 = pos->font_alt;      // (used for active state)
2860
2861         font = (active ? font2 : font1);
2862       }
2863
2864       if (s != NULL)
2865       {
2866         char *s_cut;
2867
2868         if (size <= 0)
2869         {
2870           // don't truncate output if "chars" is zero or less
2871           size = strlen(s);
2872
2873           // dynamically correct text alignment
2874           pos->width = size * getFontWidth(font);
2875         }
2876
2877         s_cut = getStringCopyN(s, size);
2878
2879         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2880                     s_cut, font, mask_mode);
2881
2882         free(s_cut);
2883       }
2884     }
2885
2886     redraw_mask |= REDRAW_DOOR_1;
2887   }
2888
2889   SetGameStatus(GAME_MODE_PLAYING);
2890 }
2891
2892 void UpdateAndDisplayGameControlValues(void)
2893 {
2894   if (tape.deactivate_display)
2895     return;
2896
2897   UpdateGameControlValues();
2898   DisplayGameControlValues();
2899 }
2900
2901 void UpdateGameDoorValues(void)
2902 {
2903   UpdateGameControlValues();
2904 }
2905
2906 void DrawGameDoorValues(void)
2907 {
2908   DisplayGameControlValues();
2909 }
2910
2911
2912 // ============================================================================
2913 // InitGameEngine()
2914 // ----------------------------------------------------------------------------
2915 // initialize game engine due to level / tape version number
2916 // ============================================================================
2917
2918 static void InitGameEngine(void)
2919 {
2920   int i, j, k, l, x, y;
2921
2922   // set game engine from tape file when re-playing, else from level file
2923   game.engine_version = (tape.playing ? tape.engine_version :
2924                          level.game_version);
2925
2926   // set single or multi-player game mode (needed for re-playing tapes)
2927   game.team_mode = setup.team_mode;
2928
2929   if (tape.playing)
2930   {
2931     int num_players = 0;
2932
2933     for (i = 0; i < MAX_PLAYERS; i++)
2934       if (tape.player_participates[i])
2935         num_players++;
2936
2937     // multi-player tapes contain input data for more than one player
2938     game.team_mode = (num_players > 1);
2939   }
2940
2941 #if 0
2942   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2943         level.game_version);
2944   Debug("game:init:level", "          tape.file_version   == %06d",
2945         tape.file_version);
2946   Debug("game:init:level", "          tape.game_version   == %06d",
2947         tape.game_version);
2948   Debug("game:init:level", "          tape.engine_version == %06d",
2949         tape.engine_version);
2950   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2951         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2952 #endif
2953
2954   // --------------------------------------------------------------------------
2955   // set flags for bugs and changes according to active game engine version
2956   // --------------------------------------------------------------------------
2957
2958   /*
2959     Summary of bugfix:
2960     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2961
2962     Bug was introduced in version:
2963     2.0.1
2964
2965     Bug was fixed in version:
2966     4.2.0.0
2967
2968     Description:
2969     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2970     but the property "can fall" was missing, which caused some levels to be
2971     unsolvable. This was fixed in version 4.2.0.0.
2972
2973     Affected levels/tapes:
2974     An example for a tape that was fixed by this bugfix is tape 029 from the
2975     level set "rnd_sam_bateman".
2976     The wrong behaviour will still be used for all levels or tapes that were
2977     created/recorded with it. An example for this is tape 023 from the level
2978     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2979   */
2980
2981   boolean use_amoeba_dropping_cannot_fall_bug =
2982     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2983       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2984      (tape.playing &&
2985       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2986       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2987
2988   /*
2989     Summary of bugfix/change:
2990     Fixed move speed of elements entering or leaving magic wall.
2991
2992     Fixed/changed in version:
2993     2.0.1
2994
2995     Description:
2996     Before 2.0.1, move speed of elements entering or leaving magic wall was
2997     twice as fast as it is now.
2998     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2999
3000     Affected levels/tapes:
3001     The first condition is generally needed for all levels/tapes before version
3002     2.0.1, which might use the old behaviour before it was changed; known tapes
3003     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3004     The second condition is an exception from the above case and is needed for
3005     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3006     above, but before it was known that this change would break tapes like the
3007     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3008     although the engine version while recording maybe was before 2.0.1. There
3009     are a lot of tapes that are affected by this exception, like tape 006 from
3010     the level set "rnd_conor_mancone".
3011   */
3012
3013   boolean use_old_move_stepsize_for_magic_wall =
3014     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3015      !(tape.playing &&
3016        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3017        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3018
3019   /*
3020     Summary of bugfix/change:
3021     Fixed handling for custom elements that change when pushed by the player.
3022
3023     Fixed/changed in version:
3024     3.1.0
3025
3026     Description:
3027     Before 3.1.0, custom elements that "change when pushing" changed directly
3028     after the player started pushing them (until then handled in "DigField()").
3029     Since 3.1.0, these custom elements are not changed until the "pushing"
3030     move of the element is finished (now handled in "ContinueMoving()").
3031
3032     Affected levels/tapes:
3033     The first condition is generally needed for all levels/tapes before version
3034     3.1.0, which might use the old behaviour before it was changed; known tapes
3035     that are affected are some tapes from the level set "Walpurgis Gardens" by
3036     Jamie Cullen.
3037     The second condition is an exception from the above case and is needed for
3038     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3039     above (including some development versions of 3.1.0), but before it was
3040     known that this change would break tapes like the above and was fixed in
3041     3.1.1, so that the changed behaviour was active although the engine version
3042     while recording maybe was before 3.1.0. There is at least one tape that is
3043     affected by this exception, which is the tape for the one-level set "Bug
3044     Machine" by Juergen Bonhagen.
3045   */
3046
3047   game.use_change_when_pushing_bug =
3048     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3049      !(tape.playing &&
3050        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3051        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for blocking the field the player leaves when moving.
3056
3057     Fixed/changed in version:
3058     3.1.1
3059
3060     Description:
3061     Before 3.1.1, when "block last field when moving" was enabled, the field
3062     the player is leaving when moving was blocked for the time of the move,
3063     and was directly unblocked afterwards. This resulted in the last field
3064     being blocked for exactly one less than the number of frames of one player
3065     move. Additionally, even when blocking was disabled, the last field was
3066     blocked for exactly one frame.
3067     Since 3.1.1, due to changes in player movement handling, the last field
3068     is not blocked at all when blocking is disabled. When blocking is enabled,
3069     the last field is blocked for exactly the number of frames of one player
3070     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3071     last field is blocked for exactly one more than the number of frames of
3072     one player move.
3073
3074     Affected levels/tapes:
3075     (!!! yet to be determined -- probably many !!!)
3076   */
3077
3078   game.use_block_last_field_bug =
3079     (game.engine_version < VERSION_IDENT(3,1,1,0));
3080
3081   /* various special flags and settings for native Emerald Mine game engine */
3082
3083   game_em.use_single_button =
3084     (game.engine_version > VERSION_IDENT(4,0,0,2));
3085
3086   game_em.use_snap_key_bug =
3087     (game.engine_version < VERSION_IDENT(4,0,1,0));
3088
3089   game_em.use_random_bug =
3090     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3091
3092   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3093
3094   game_em.use_old_explosions            = use_old_em_engine;
3095   game_em.use_old_android               = use_old_em_engine;
3096   game_em.use_old_push_elements         = use_old_em_engine;
3097   game_em.use_old_push_into_acid        = use_old_em_engine;
3098
3099   game_em.use_wrap_around               = !use_old_em_engine;
3100
3101   // --------------------------------------------------------------------------
3102
3103   // set maximal allowed number of custom element changes per game frame
3104   game.max_num_changes_per_frame = 1;
3105
3106   // default scan direction: scan playfield from top/left to bottom/right
3107   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3108
3109   // dynamically adjust element properties according to game engine version
3110   InitElementPropertiesEngine(game.engine_version);
3111
3112   // ---------- initialize special element properties -------------------------
3113
3114   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3115   if (use_amoeba_dropping_cannot_fall_bug)
3116     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3117
3118   // ---------- initialize player's initial move delay ------------------------
3119
3120   // dynamically adjust player properties according to level information
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     game.initial_move_delay_value[i] =
3123       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3124
3125   // dynamically adjust player properties according to game engine version
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     game.initial_move_delay[i] =
3128       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3129        game.initial_move_delay_value[i] : 0);
3130
3131   // ---------- initialize player's initial push delay ------------------------
3132
3133   // dynamically adjust player properties according to game engine version
3134   game.initial_push_delay_value =
3135     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3136
3137   // ---------- initialize changing elements ----------------------------------
3138
3139   // initialize changing elements information
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141   {
3142     struct ElementInfo *ei = &element_info[i];
3143
3144     // this pointer might have been changed in the level editor
3145     ei->change = &ei->change_page[0];
3146
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       ei->change->target_element = EL_EMPTY_SPACE;
3150       ei->change->delay_fixed = 0;
3151       ei->change->delay_random = 0;
3152       ei->change->delay_frames = 1;
3153     }
3154
3155     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3156     {
3157       ei->has_change_event[j] = FALSE;
3158
3159       ei->event_page_nr[j] = 0;
3160       ei->event_page[j] = &ei->change_page[0];
3161     }
3162   }
3163
3164   // add changing elements from pre-defined list
3165   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3166   {
3167     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3168     struct ElementInfo *ei = &element_info[ch_delay->element];
3169
3170     ei->change->target_element       = ch_delay->target_element;
3171     ei->change->delay_fixed          = ch_delay->change_delay;
3172
3173     ei->change->pre_change_function  = ch_delay->pre_change_function;
3174     ei->change->change_function      = ch_delay->change_function;
3175     ei->change->post_change_function = ch_delay->post_change_function;
3176
3177     ei->change->can_change = TRUE;
3178     ei->change->can_change_or_has_action = TRUE;
3179
3180     ei->has_change_event[CE_DELAY] = TRUE;
3181
3182     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3183     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3184   }
3185
3186   // ---------- initialize internal run-time variables ------------------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].can_change_or_has_action =
3195         (ei->change_page[j].can_change |
3196          ei->change_page[j].has_action);
3197     }
3198   }
3199
3200   // add change events from custom element configuration
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3204
3205     for (j = 0; j < ei->num_change_pages; j++)
3206     {
3207       if (!ei->change_page[j].can_change_or_has_action)
3208         continue;
3209
3210       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3211       {
3212         // only add event page for the first page found with this event
3213         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3214         {
3215           ei->has_change_event[k] = TRUE;
3216
3217           ei->event_page_nr[k] = j;
3218           ei->event_page[k] = &ei->change_page[j];
3219         }
3220       }
3221     }
3222   }
3223
3224   // ---------- initialize reference elements in change conditions ------------
3225
3226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3227   {
3228     int element = EL_CUSTOM_START + i;
3229     struct ElementInfo *ei = &element_info[element];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       int trigger_element = ei->change_page[j].initial_trigger_element;
3234
3235       if (trigger_element >= EL_PREV_CE_8 &&
3236           trigger_element <= EL_NEXT_CE_8)
3237         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3238
3239       ei->change_page[j].trigger_element = trigger_element;
3240     }
3241   }
3242
3243   // ---------- initialize run-time trigger player and element ----------------
3244
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3252       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3253       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3254       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3255       ei->change_page[j].actual_trigger_ce_value = 0;
3256       ei->change_page[j].actual_trigger_ce_score = 0;
3257     }
3258   }
3259
3260   // ---------- initialize trigger events -------------------------------------
3261
3262   // initialize trigger events information
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3265       trigger_events[i][j] = FALSE;
3266
3267   // add trigger events from element change event properties
3268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       if (!ei->change_page[j].can_change_or_has_action)
3275         continue;
3276
3277       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3278       {
3279         int trigger_element = ei->change_page[j].trigger_element;
3280
3281         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3282         {
3283           if (ei->change_page[j].has_event[k])
3284           {
3285             if (IS_GROUP_ELEMENT(trigger_element))
3286             {
3287               struct ElementGroupInfo *group =
3288                 element_info[trigger_element].group;
3289
3290               for (l = 0; l < group->num_elements_resolved; l++)
3291                 trigger_events[group->element_resolved[l]][k] = TRUE;
3292             }
3293             else if (trigger_element == EL_ANY_ELEMENT)
3294               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3295                 trigger_events[l][k] = TRUE;
3296             else
3297               trigger_events[trigger_element][k] = TRUE;
3298           }
3299         }
3300       }
3301     }
3302   }
3303
3304   // ---------- initialize push delay -----------------------------------------
3305
3306   // initialize push delay values to default
3307   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308   {
3309     if (!IS_CUSTOM_ELEMENT(i))
3310     {
3311       // set default push delay values (corrected since version 3.0.7-1)
3312       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3313       {
3314         element_info[i].push_delay_fixed = 2;
3315         element_info[i].push_delay_random = 8;
3316       }
3317       else
3318       {
3319         element_info[i].push_delay_fixed = 8;
3320         element_info[i].push_delay_random = 8;
3321       }
3322     }
3323   }
3324
3325   // set push delay value for certain elements from pre-defined list
3326   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3327   {
3328     int e = push_delay_list[i].element;
3329
3330     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3331     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3332   }
3333
3334   // set push delay value for Supaplex elements for newer engine versions
3335   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3336   {
3337     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338     {
3339       if (IS_SP_ELEMENT(i))
3340       {
3341         // set SP push delay to just enough to push under a falling zonk
3342         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3343
3344         element_info[i].push_delay_fixed  = delay;
3345         element_info[i].push_delay_random = 0;
3346       }
3347     }
3348   }
3349
3350   // ---------- initialize move stepsize --------------------------------------
3351
3352   // initialize move stepsize values to default
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (!IS_CUSTOM_ELEMENT(i))
3355       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3356
3357   // set move stepsize value for certain elements from pre-defined list
3358   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = move_stepsize_list[i].element;
3361
3362     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3363
3364     // set move stepsize value for certain elements for older engine versions
3365     if (use_old_move_stepsize_for_magic_wall)
3366     {
3367       if (e == EL_MAGIC_WALL_FILLING ||
3368           e == EL_MAGIC_WALL_EMPTYING ||
3369           e == EL_BD_MAGIC_WALL_FILLING ||
3370           e == EL_BD_MAGIC_WALL_EMPTYING)
3371         element_info[e].move_stepsize *= 2;
3372     }
3373   }
3374
3375   // ---------- initialize collect score --------------------------------------
3376
3377   // initialize collect score values for custom elements from initial value
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (IS_CUSTOM_ELEMENT(i))
3380       element_info[i].collect_score = element_info[i].collect_score_initial;
3381
3382   // ---------- initialize collect count --------------------------------------
3383
3384   // initialize collect count values for non-custom elements
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].collect_count_initial = 0;
3388
3389   // add collect count values for all elements from pre-defined list
3390   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3391     element_info[collect_count_list[i].element].collect_count_initial =
3392       collect_count_list[i].count;
3393
3394   // ---------- initialize access direction -----------------------------------
3395
3396   // initialize access direction values to default (access from every side)
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3400
3401   // set access direction value for certain elements from pre-defined list
3402   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3403     element_info[access_direction_list[i].element].access_direction =
3404       access_direction_list[i].direction;
3405
3406   // ---------- initialize explosion content ----------------------------------
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408   {
3409     if (IS_CUSTOM_ELEMENT(i))
3410       continue;
3411
3412     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3413     {
3414       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3415
3416       element_info[i].content.e[x][y] =
3417         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3418          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3419          i == EL_PLAYER_3 ? EL_EMERALD :
3420          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3421          i == EL_MOLE ? EL_EMERALD_RED :
3422          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3423          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3424          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3425          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3426          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3427          i == EL_WALL_EMERALD ? EL_EMERALD :
3428          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3429          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3430          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3431          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3432          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3433          i == EL_WALL_PEARL ? EL_PEARL :
3434          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3435          EL_EMPTY);
3436     }
3437   }
3438
3439   // ---------- initialize recursion detection --------------------------------
3440   recursion_loop_depth = 0;
3441   recursion_loop_detected = FALSE;
3442   recursion_loop_element = EL_UNDEFINED;
3443
3444   // ---------- initialize graphics engine ------------------------------------
3445   game.scroll_delay_value =
3446     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3447      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3448      !setup.forced_scroll_delay           ? 0 :
3449      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3450   game.scroll_delay_value =
3451     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3452
3453   // ---------- initialize game engine snapshots ------------------------------
3454   for (i = 0; i < MAX_PLAYERS; i++)
3455     game.snapshot.last_action[i] = 0;
3456   game.snapshot.changed_action = FALSE;
3457   game.snapshot.collected_item = FALSE;
3458   game.snapshot.mode =
3459     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3460      SNAPSHOT_MODE_EVERY_STEP :
3461      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3462      SNAPSHOT_MODE_EVERY_MOVE :
3463      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3464      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3465   game.snapshot.save_snapshot = FALSE;
3466
3467   // ---------- initialize level time for Supaplex engine ---------------------
3468   // Supaplex levels with time limit currently unsupported -- should be added
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3470     level.time = 0;
3471
3472   // ---------- initialize flags for handling game actions --------------------
3473
3474   // set flags for game actions to default values
3475   game.use_key_actions = TRUE;
3476   game.use_mouse_actions = FALSE;
3477
3478   // when using Mirror Magic game engine, handle mouse events only
3479   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3480   {
3481     game.use_key_actions = FALSE;
3482     game.use_mouse_actions = TRUE;
3483   }
3484
3485   // check for custom elements with mouse click events
3486   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3487   {
3488     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3489     {
3490       int element = EL_CUSTOM_START + i;
3491
3492       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3493           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3494           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3495           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3496         game.use_mouse_actions = TRUE;
3497     }
3498   }
3499 }
3500
3501 static int get_num_special_action(int element, int action_first,
3502                                   int action_last)
3503 {
3504   int num_special_action = 0;
3505   int i, j;
3506
3507   for (i = action_first; i <= action_last; i++)
3508   {
3509     boolean found = FALSE;
3510
3511     for (j = 0; j < NUM_DIRECTIONS; j++)
3512       if (el_act_dir2img(element, i, j) !=
3513           el_act_dir2img(element, ACTION_DEFAULT, j))
3514         found = TRUE;
3515
3516     if (found)
3517       num_special_action++;
3518     else
3519       break;
3520   }
3521
3522   return num_special_action;
3523 }
3524
3525
3526 // ============================================================================
3527 // InitGame()
3528 // ----------------------------------------------------------------------------
3529 // initialize and start new game
3530 // ============================================================================
3531
3532 #if DEBUG_INIT_PLAYER
3533 static void DebugPrintPlayerStatus(char *message)
3534 {
3535   int i;
3536
3537   if (!options.debug)
3538     return;
3539
3540   Debug("game:init:player", "%s:", message);
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543   {
3544     struct PlayerInfo *player = &stored_player[i];
3545
3546     Debug("game:init:player",
3547           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3548           i + 1,
3549           player->present,
3550           player->connected,
3551           player->connected_locally,
3552           player->connected_network,
3553           player->active,
3554           (local_player == player ? " (local player)" : ""));
3555   }
3556 }
3557 #endif
3558
3559 void InitGame(void)
3560 {
3561   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3562   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3563   int fade_mask = REDRAW_FIELD;
3564
3565   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3566   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3567   int initial_move_dir = MV_DOWN;
3568   int i, j, x, y;
3569
3570   // required here to update video display before fading (FIX THIS)
3571   DrawMaskedBorder(REDRAW_DOOR_2);
3572
3573   if (!game.restart_level)
3574     CloseDoor(DOOR_CLOSE_1);
3575
3576   SetGameStatus(GAME_MODE_PLAYING);
3577
3578   if (level_editor_test_game)
3579     FadeSkipNextFadeOut();
3580   else
3581     FadeSetEnterScreen();
3582
3583   if (CheckFadeAll())
3584     fade_mask = REDRAW_ALL;
3585
3586   FadeLevelSoundsAndMusic();
3587
3588   ExpireSoundLoops(TRUE);
3589
3590   FadeOut(fade_mask);
3591
3592   if (level_editor_test_game)
3593     FadeSkipNextFadeIn();
3594
3595   // needed if different viewport properties defined for playing
3596   ChangeViewportPropertiesIfNeeded();
3597
3598   ClearField();
3599
3600   DrawCompleteVideoDisplay();
3601
3602   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3603
3604   InitGameEngine();
3605   InitGameControlValues();
3606
3607   if (tape.recording)
3608   {
3609     // initialize tape actions from game when recording tape
3610     tape.use_key_actions   = game.use_key_actions;
3611     tape.use_mouse_actions = game.use_mouse_actions;
3612
3613     // initialize visible playfield size when recording tape (for team mode)
3614     tape.scr_fieldx = SCR_FIELDX;
3615     tape.scr_fieldy = SCR_FIELDY;
3616   }
3617
3618   // don't play tapes over network
3619   network_playing = (network.enabled && !tape.playing);
3620
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     player->index_nr = i;
3626     player->index_bit = (1 << i);
3627     player->element_nr = EL_PLAYER_1 + i;
3628
3629     player->present = FALSE;
3630     player->active = FALSE;
3631     player->mapped = FALSE;
3632
3633     player->killed = FALSE;
3634     player->reanimated = FALSE;
3635     player->buried = FALSE;
3636
3637     player->action = 0;
3638     player->effective_action = 0;
3639     player->programmed_action = 0;
3640     player->snap_action = 0;
3641
3642     player->mouse_action.lx = 0;
3643     player->mouse_action.ly = 0;
3644     player->mouse_action.button = 0;
3645     player->mouse_action.button_hint = 0;
3646
3647     player->effective_mouse_action.lx = 0;
3648     player->effective_mouse_action.ly = 0;
3649     player->effective_mouse_action.button = 0;
3650     player->effective_mouse_action.button_hint = 0;
3651
3652     for (j = 0; j < MAX_NUM_KEYS; j++)
3653       player->key[j] = FALSE;
3654
3655     player->num_white_keys = 0;
3656
3657     player->dynabomb_count = 0;
3658     player->dynabomb_size = 1;
3659     player->dynabombs_left = 0;
3660     player->dynabomb_xl = FALSE;
3661
3662     player->MovDir = initial_move_dir;
3663     player->MovPos = 0;
3664     player->GfxPos = 0;
3665     player->GfxDir = initial_move_dir;
3666     player->GfxAction = ACTION_DEFAULT;
3667     player->Frame = 0;
3668     player->StepFrame = 0;
3669
3670     player->initial_element = player->element_nr;
3671     player->artwork_element =
3672       (level.use_artwork_element[i] ? level.artwork_element[i] :
3673        player->element_nr);
3674     player->use_murphy = FALSE;
3675
3676     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3677     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3678
3679     player->gravity = level.initial_player_gravity[i];
3680
3681     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3682
3683     player->actual_frame_counter = 0;
3684
3685     player->step_counter = 0;
3686
3687     player->last_move_dir = initial_move_dir;
3688
3689     player->is_active = FALSE;
3690
3691     player->is_waiting = FALSE;
3692     player->is_moving = FALSE;
3693     player->is_auto_moving = FALSE;
3694     player->is_digging = FALSE;
3695     player->is_snapping = FALSE;
3696     player->is_collecting = FALSE;
3697     player->is_pushing = FALSE;
3698     player->is_switching = FALSE;
3699     player->is_dropping = FALSE;
3700     player->is_dropping_pressed = FALSE;
3701
3702     player->is_bored = FALSE;
3703     player->is_sleeping = FALSE;
3704
3705     player->was_waiting = TRUE;
3706     player->was_moving = FALSE;
3707     player->was_snapping = FALSE;
3708     player->was_dropping = FALSE;
3709
3710     player->force_dropping = FALSE;
3711
3712     player->frame_counter_bored = -1;
3713     player->frame_counter_sleeping = -1;
3714
3715     player->anim_delay_counter = 0;
3716     player->post_delay_counter = 0;
3717
3718     player->dir_waiting = initial_move_dir;
3719     player->action_waiting = ACTION_DEFAULT;
3720     player->last_action_waiting = ACTION_DEFAULT;
3721     player->special_action_bored = ACTION_DEFAULT;
3722     player->special_action_sleeping = ACTION_DEFAULT;
3723
3724     player->switch_x = -1;
3725     player->switch_y = -1;
3726
3727     player->drop_x = -1;
3728     player->drop_y = -1;
3729
3730     player->show_envelope = 0;
3731
3732     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3733
3734     player->push_delay       = -1;      // initialized when pushing starts
3735     player->push_delay_value = game.initial_push_delay_value;
3736
3737     player->drop_delay = 0;
3738     player->drop_pressed_delay = 0;
3739
3740     player->last_jx = -1;
3741     player->last_jy = -1;
3742     player->jx = -1;
3743     player->jy = -1;
3744
3745     player->shield_normal_time_left = 0;
3746     player->shield_deadly_time_left = 0;
3747
3748     player->last_removed_element = EL_UNDEFINED;
3749
3750     player->inventory_infinite_element = EL_UNDEFINED;
3751     player->inventory_size = 0;
3752
3753     if (level.use_initial_inventory[i])
3754     {
3755       for (j = 0; j < level.initial_inventory_size[i]; j++)
3756       {
3757         int element = level.initial_inventory_content[i][j];
3758         int collect_count = element_info[element].collect_count_initial;
3759         int k;
3760
3761         if (!IS_CUSTOM_ELEMENT(element))
3762           collect_count = 1;
3763
3764         if (collect_count == 0)
3765           player->inventory_infinite_element = element;
3766         else
3767           for (k = 0; k < collect_count; k++)
3768             if (player->inventory_size < MAX_INVENTORY_SIZE)
3769               player->inventory_element[player->inventory_size++] = element;
3770       }
3771     }
3772
3773     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3774     SnapField(player, 0, 0);
3775
3776     map_player_action[i] = i;
3777   }
3778
3779   network_player_action_received = FALSE;
3780
3781   // initial null action
3782   if (network_playing)
3783     SendToServer_MovePlayer(MV_NONE);
3784
3785   FrameCounter = 0;
3786   TimeFrames = 0;
3787   TimePlayed = 0;
3788   TimeLeft = level.time;
3789   TapeTime = 0;
3790
3791   ScreenMovDir = MV_NONE;
3792   ScreenMovPos = 0;
3793   ScreenGfxPos = 0;
3794
3795   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3796
3797   game.robot_wheel_x = -1;
3798   game.robot_wheel_y = -1;
3799
3800   game.exit_x = -1;
3801   game.exit_y = -1;
3802
3803   game.all_players_gone = FALSE;
3804
3805   game.LevelSolved = FALSE;
3806   game.GameOver = FALSE;
3807
3808   game.GamePlayed = !tape.playing;
3809
3810   game.LevelSolved_GameWon = FALSE;
3811   game.LevelSolved_GameEnd = FALSE;
3812   game.LevelSolved_SaveTape = FALSE;
3813   game.LevelSolved_SaveScore = FALSE;
3814
3815   game.LevelSolved_CountingTime = 0;
3816   game.LevelSolved_CountingScore = 0;
3817   game.LevelSolved_CountingHealth = 0;
3818
3819   game.panel.active = TRUE;
3820
3821   game.no_time_limit = (level.time == 0);
3822
3823   game.yamyam_content_nr = 0;
3824   game.robot_wheel_active = FALSE;
3825   game.magic_wall_active = FALSE;
3826   game.magic_wall_time_left = 0;
3827   game.light_time_left = 0;
3828   game.timegate_time_left = 0;
3829   game.switchgate_pos = 0;
3830   game.wind_direction = level.wind_direction_initial;
3831
3832   game.time_final = 0;
3833   game.score_time_final = 0;
3834
3835   game.score = 0;
3836   game.score_final = 0;
3837
3838   game.health = MAX_HEALTH;
3839   game.health_final = MAX_HEALTH;
3840
3841   game.gems_still_needed = level.gems_needed;
3842   game.sokoban_fields_still_needed = 0;
3843   game.sokoban_objects_still_needed = 0;
3844   game.lights_still_needed = 0;
3845   game.players_still_needed = 0;
3846   game.friends_still_needed = 0;
3847
3848   game.lenses_time_left = 0;
3849   game.magnify_time_left = 0;
3850
3851   game.ball_active = level.ball_active_initial;
3852   game.ball_content_nr = 0;
3853
3854   game.explosions_delayed = TRUE;
3855
3856   game.envelope_active = FALSE;
3857
3858   // special case: set custom artwork setting to initial value
3859   game.use_masked_elements = game.use_masked_elements_initial;
3860
3861   for (i = 0; i < NUM_BELTS; i++)
3862   {
3863     game.belt_dir[i] = MV_NONE;
3864     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3865   }
3866
3867   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3868     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3869
3870 #if DEBUG_INIT_PLAYER
3871   DebugPrintPlayerStatus("Player status at level initialization");
3872 #endif
3873
3874   SCAN_PLAYFIELD(x, y)
3875   {
3876     Tile[x][y] = Last[x][y] = level.field[x][y];
3877     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3878     ChangeDelay[x][y] = 0;
3879     ChangePage[x][y] = -1;
3880     CustomValue[x][y] = 0;              // initialized in InitField()
3881     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3882     AmoebaNr[x][y] = 0;
3883     WasJustMoving[x][y] = 0;
3884     WasJustFalling[x][y] = 0;
3885     CheckCollision[x][y] = 0;
3886     CheckImpact[x][y] = 0;
3887     Stop[x][y] = FALSE;
3888     Pushed[x][y] = FALSE;
3889
3890     ChangeCount[x][y] = 0;
3891     ChangeEvent[x][y] = -1;
3892
3893     ExplodePhase[x][y] = 0;
3894     ExplodeDelay[x][y] = 0;
3895     ExplodeField[x][y] = EX_TYPE_NONE;
3896
3897     RunnerVisit[x][y] = 0;
3898     PlayerVisit[x][y] = 0;
3899
3900     GfxFrame[x][y] = 0;
3901     GfxRandom[x][y] = INIT_GFX_RANDOM();
3902     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3903     GfxElement[x][y] = EL_UNDEFINED;
3904     GfxElementEmpty[x][y] = EL_EMPTY;
3905     GfxAction[x][y] = ACTION_DEFAULT;
3906     GfxDir[x][y] = MV_NONE;
3907     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3908   }
3909
3910   SCAN_PLAYFIELD(x, y)
3911   {
3912     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3913       emulate_bd = FALSE;
3914     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3915       emulate_sp = FALSE;
3916
3917     InitField(x, y, TRUE);
3918
3919     ResetGfxAnimation(x, y);
3920   }
3921
3922   InitBeltMovement();
3923
3924   for (i = 0; i < MAX_PLAYERS; i++)
3925   {
3926     struct PlayerInfo *player = &stored_player[i];
3927
3928     // set number of special actions for bored and sleeping animation
3929     player->num_special_action_bored =
3930       get_num_special_action(player->artwork_element,
3931                              ACTION_BORING_1, ACTION_BORING_LAST);
3932     player->num_special_action_sleeping =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3935   }
3936
3937   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3938                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3939
3940   // initialize type of slippery elements
3941   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3942   {
3943     if (!IS_CUSTOM_ELEMENT(i))
3944     {
3945       // default: elements slip down either to the left or right randomly
3946       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3947
3948       // SP style elements prefer to slip down on the left side
3949       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3950         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3951
3952       // BD style elements prefer to slip down on the left side
3953       if (game.emulation == EMU_BOULDERDASH)
3954         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3955     }
3956   }
3957
3958   // initialize explosion and ignition delay
3959   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3960   {
3961     if (!IS_CUSTOM_ELEMENT(i))
3962     {
3963       int num_phase = 8;
3964       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3965                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3966                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3967       int last_phase = (num_phase + 1) * delay;
3968       int half_phase = (num_phase / 2) * delay;
3969
3970       element_info[i].explosion_delay = last_phase - 1;
3971       element_info[i].ignition_delay = half_phase;
3972
3973       if (i == EL_BLACK_ORB)
3974         element_info[i].ignition_delay = 1;
3975     }
3976   }
3977
3978   // correct non-moving belts to start moving left
3979   for (i = 0; i < NUM_BELTS; i++)
3980     if (game.belt_dir[i] == MV_NONE)
3981       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3982
3983 #if USE_NEW_PLAYER_ASSIGNMENTS
3984   // use preferred player also in local single-player mode
3985   if (!network.enabled && !game.team_mode)
3986   {
3987     int new_index_nr = setup.network_player_nr;
3988
3989     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3990     {
3991       for (i = 0; i < MAX_PLAYERS; i++)
3992         stored_player[i].connected_locally = FALSE;
3993
3994       stored_player[new_index_nr].connected_locally = TRUE;
3995     }
3996   }
3997
3998   for (i = 0; i < MAX_PLAYERS; i++)
3999   {
4000     stored_player[i].connected = FALSE;
4001
4002     // in network game mode, the local player might not be the first player
4003     if (stored_player[i].connected_locally)
4004       local_player = &stored_player[i];
4005   }
4006
4007   if (!network.enabled)
4008     local_player->connected = TRUE;
4009
4010   if (tape.playing)
4011   {
4012     for (i = 0; i < MAX_PLAYERS; i++)
4013       stored_player[i].connected = tape.player_participates[i];
4014   }
4015   else if (network.enabled)
4016   {
4017     // add team mode players connected over the network (needed for correct
4018     // assignment of player figures from level to locally playing players)
4019
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (stored_player[i].connected_network)
4022         stored_player[i].connected = TRUE;
4023   }
4024   else if (game.team_mode)
4025   {
4026     // try to guess locally connected team mode players (needed for correct
4027     // assignment of player figures from level to locally playing players)
4028
4029     for (i = 0; i < MAX_PLAYERS; i++)
4030       if (setup.input[i].use_joystick ||
4031           setup.input[i].key.left != KSYM_UNDEFINED)
4032         stored_player[i].connected = TRUE;
4033   }
4034
4035 #if DEBUG_INIT_PLAYER
4036   DebugPrintPlayerStatus("Player status after level initialization");
4037 #endif
4038
4039 #if DEBUG_INIT_PLAYER
4040   Debug("game:init:player", "Reassigning players ...");
4041 #endif
4042
4043   // check if any connected player was not found in playfield
4044   for (i = 0; i < MAX_PLAYERS; i++)
4045   {
4046     struct PlayerInfo *player = &stored_player[i];
4047
4048     if (player->connected && !player->present)
4049     {
4050       struct PlayerInfo *field_player = NULL;
4051
4052 #if DEBUG_INIT_PLAYER
4053       Debug("game:init:player",
4054             "- looking for field player for player %d ...", i + 1);
4055 #endif
4056
4057       // assign first free player found that is present in the playfield
4058
4059       // first try: look for unmapped playfield player that is not connected
4060       for (j = 0; j < MAX_PLAYERS; j++)
4061         if (field_player == NULL &&
4062             stored_player[j].present &&
4063             !stored_player[j].mapped &&
4064             !stored_player[j].connected)
4065           field_player = &stored_player[j];
4066
4067       // second try: look for *any* unmapped playfield player
4068       for (j = 0; j < MAX_PLAYERS; j++)
4069         if (field_player == NULL &&
4070             stored_player[j].present &&
4071             !stored_player[j].mapped)
4072           field_player = &stored_player[j];
4073
4074       if (field_player != NULL)
4075       {
4076         int jx = field_player->jx, jy = field_player->jy;
4077
4078 #if DEBUG_INIT_PLAYER
4079         Debug("game:init:player", "- found player %d",
4080               field_player->index_nr + 1);
4081 #endif
4082
4083         player->present = FALSE;
4084         player->active = FALSE;
4085
4086         field_player->present = TRUE;
4087         field_player->active = TRUE;
4088
4089         /*
4090         player->initial_element = field_player->initial_element;
4091         player->artwork_element = field_player->artwork_element;
4092
4093         player->block_last_field       = field_player->block_last_field;
4094         player->block_delay_adjustment = field_player->block_delay_adjustment;
4095         */
4096
4097         StorePlayer[jx][jy] = field_player->element_nr;
4098
4099         field_player->jx = field_player->last_jx = jx;
4100         field_player->jy = field_player->last_jy = jy;
4101
4102         if (local_player == player)
4103           local_player = field_player;
4104
4105         map_player_action[field_player->index_nr] = i;
4106
4107         field_player->mapped = TRUE;
4108
4109 #if DEBUG_INIT_PLAYER
4110         Debug("game:init:player", "- map_player_action[%d] == %d",
4111               field_player->index_nr + 1, i + 1);
4112 #endif
4113       }
4114     }
4115
4116     if (player->connected && player->present)
4117       player->mapped = TRUE;
4118   }
4119
4120 #if DEBUG_INIT_PLAYER
4121   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4122 #endif
4123
4124 #else
4125
4126   // check if any connected player was not found in playfield
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128   {
4129     struct PlayerInfo *player = &stored_player[i];
4130
4131     if (player->connected && !player->present)
4132     {
4133       for (j = 0; j < MAX_PLAYERS; j++)
4134       {
4135         struct PlayerInfo *field_player = &stored_player[j];
4136         int jx = field_player->jx, jy = field_player->jy;
4137
4138         // assign first free player found that is present in the playfield
4139         if (field_player->present && !field_player->connected)
4140         {
4141           player->present = TRUE;
4142           player->active = TRUE;
4143
4144           field_player->present = FALSE;
4145           field_player->active = FALSE;
4146
4147           player->initial_element = field_player->initial_element;
4148           player->artwork_element = field_player->artwork_element;
4149
4150           player->block_last_field       = field_player->block_last_field;
4151           player->block_delay_adjustment = field_player->block_delay_adjustment;
4152
4153           StorePlayer[jx][jy] = player->element_nr;
4154
4155           player->jx = player->last_jx = jx;
4156           player->jy = player->last_jy = jy;
4157
4158           break;
4159         }
4160       }
4161     }
4162   }
4163 #endif
4164
4165 #if 0
4166   Debug("game:init:player", "local_player->present == %d",
4167         local_player->present);
4168 #endif
4169
4170   // set focus to local player for network games, else to all players
4171   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4172   game.centered_player_nr_next = game.centered_player_nr;
4173   game.set_centered_player = FALSE;
4174   game.set_centered_player_wrap = FALSE;
4175
4176   if (network_playing && tape.recording)
4177   {
4178     // store client dependent player focus when recording network games
4179     tape.centered_player_nr_next = game.centered_player_nr_next;
4180     tape.set_centered_player = TRUE;
4181   }
4182
4183   if (tape.playing)
4184   {
4185     // when playing a tape, eliminate all players who do not participate
4186
4187 #if USE_NEW_PLAYER_ASSIGNMENTS
4188
4189     if (!game.team_mode)
4190     {
4191       for (i = 0; i < MAX_PLAYERS; i++)
4192       {
4193         if (stored_player[i].active &&
4194             !tape.player_participates[map_player_action[i]])
4195         {
4196           struct PlayerInfo *player = &stored_player[i];
4197           int jx = player->jx, jy = player->jy;
4198
4199 #if DEBUG_INIT_PLAYER
4200           Debug("game:init:player", "Removing player %d at (%d, %d)",
4201                 i + 1, jx, jy);
4202 #endif
4203
4204           player->active = FALSE;
4205           StorePlayer[jx][jy] = 0;
4206           Tile[jx][jy] = EL_EMPTY;
4207         }
4208       }
4209     }
4210
4211 #else
4212
4213     for (i = 0; i < MAX_PLAYERS; i++)
4214     {
4215       if (stored_player[i].active &&
4216           !tape.player_participates[i])
4217       {
4218         struct PlayerInfo *player = &stored_player[i];
4219         int jx = player->jx, jy = player->jy;
4220
4221         player->active = FALSE;
4222         StorePlayer[jx][jy] = 0;
4223         Tile[jx][jy] = EL_EMPTY;
4224       }
4225     }
4226 #endif
4227   }
4228   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4229   {
4230     // when in single player mode, eliminate all but the local player
4231
4232     for (i = 0; i < MAX_PLAYERS; i++)
4233     {
4234       struct PlayerInfo *player = &stored_player[i];
4235
4236       if (player->active && player != local_player)
4237       {
4238         int jx = player->jx, jy = player->jy;
4239
4240         player->active = FALSE;
4241         player->present = FALSE;
4242
4243         StorePlayer[jx][jy] = 0;
4244         Tile[jx][jy] = EL_EMPTY;
4245       }
4246     }
4247   }
4248
4249   for (i = 0; i < MAX_PLAYERS; i++)
4250     if (stored_player[i].active)
4251       game.players_still_needed++;
4252
4253   if (level.solved_by_one_player)
4254     game.players_still_needed = 1;
4255
4256   // when recording the game, store which players take part in the game
4257   if (tape.recording)
4258   {
4259 #if USE_NEW_PLAYER_ASSIGNMENTS
4260     for (i = 0; i < MAX_PLAYERS; i++)
4261       if (stored_player[i].connected)
4262         tape.player_participates[i] = TRUE;
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265       if (stored_player[i].active)
4266         tape.player_participates[i] = TRUE;
4267 #endif
4268   }
4269
4270 #if DEBUG_INIT_PLAYER
4271   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4272 #endif
4273
4274   if (BorderElement == EL_EMPTY)
4275   {
4276     SBX_Left = 0;
4277     SBX_Right = lev_fieldx - SCR_FIELDX;
4278     SBY_Upper = 0;
4279     SBY_Lower = lev_fieldy - SCR_FIELDY;
4280   }
4281   else
4282   {
4283     SBX_Left = -1;
4284     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4285     SBY_Upper = -1;
4286     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4287   }
4288
4289   if (full_lev_fieldx <= SCR_FIELDX)
4290     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4291   if (full_lev_fieldy <= SCR_FIELDY)
4292     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4293
4294   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4295     SBX_Left--;
4296   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4297     SBY_Upper--;
4298
4299   // if local player not found, look for custom element that might create
4300   // the player (make some assumptions about the right custom element)
4301   if (!local_player->present)
4302   {
4303     int start_x = 0, start_y = 0;
4304     int found_rating = 0;
4305     int found_element = EL_UNDEFINED;
4306     int player_nr = local_player->index_nr;
4307
4308     SCAN_PLAYFIELD(x, y)
4309     {
4310       int element = Tile[x][y];
4311       int content;
4312       int xx, yy;
4313       boolean is_player;
4314
4315       if (level.use_start_element[player_nr] &&
4316           level.start_element[player_nr] == element &&
4317           found_rating < 4)
4318       {
4319         start_x = x;
4320         start_y = y;
4321
4322         found_rating = 4;
4323         found_element = element;
4324       }
4325
4326       if (!IS_CUSTOM_ELEMENT(element))
4327         continue;
4328
4329       if (CAN_CHANGE(element))
4330       {
4331         for (i = 0; i < element_info[element].num_change_pages; i++)
4332         {
4333           // check for player created from custom element as single target
4334           content = element_info[element].change_page[i].target_element;
4335           is_player = IS_PLAYER_ELEMENT(content);
4336
4337           if (is_player && (found_rating < 3 ||
4338                             (found_rating == 3 && element < found_element)))
4339           {
4340             start_x = x;
4341             start_y = y;
4342
4343             found_rating = 3;
4344             found_element = element;
4345           }
4346         }
4347       }
4348
4349       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4350       {
4351         // check for player created from custom element as explosion content
4352         content = element_info[element].content.e[xx][yy];
4353         is_player = IS_PLAYER_ELEMENT(content);
4354
4355         if (is_player && (found_rating < 2 ||
4356                           (found_rating == 2 && element < found_element)))
4357         {
4358           start_x = x + xx - 1;
4359           start_y = y + yy - 1;
4360
4361           found_rating = 2;
4362           found_element = element;
4363         }
4364
4365         if (!CAN_CHANGE(element))
4366           continue;
4367
4368         for (i = 0; i < element_info[element].num_change_pages; i++)
4369         {
4370           // check for player created from custom element as extended target
4371           content =
4372             element_info[element].change_page[i].target_content.e[xx][yy];
4373
4374           is_player = IS_PLAYER_ELEMENT(content);
4375
4376           if (is_player && (found_rating < 1 ||
4377                             (found_rating == 1 && element < found_element)))
4378           {
4379             start_x = x + xx - 1;
4380             start_y = y + yy - 1;
4381
4382             found_rating = 1;
4383             found_element = element;
4384           }
4385         }
4386       }
4387     }
4388
4389     scroll_x = SCROLL_POSITION_X(start_x);
4390     scroll_y = SCROLL_POSITION_Y(start_y);
4391   }
4392   else
4393   {
4394     scroll_x = SCROLL_POSITION_X(local_player->jx);
4395     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4396   }
4397
4398   // !!! FIX THIS (START) !!!
4399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4400   {
4401     InitGameEngine_EM();
4402   }
4403   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4404   {
4405     InitGameEngine_SP();
4406   }
4407   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4408   {
4409     InitGameEngine_MM();
4410   }
4411   else
4412   {
4413     DrawLevel(REDRAW_FIELD);
4414     DrawAllPlayers();
4415
4416     // after drawing the level, correct some elements
4417     if (game.timegate_time_left == 0)
4418       CloseAllOpenTimegates();
4419   }
4420
4421   // blit playfield from scroll buffer to normal back buffer for fading in
4422   BlitScreenToBitmap(backbuffer);
4423   // !!! FIX THIS (END) !!!
4424
4425   DrawMaskedBorder(fade_mask);
4426
4427   FadeIn(fade_mask);
4428
4429 #if 1
4430   // full screen redraw is required at this point in the following cases:
4431   // - special editor door undrawn when game was started from level editor
4432   // - drawing area (playfield) was changed and has to be removed completely
4433   redraw_mask = REDRAW_ALL;
4434   BackToFront();
4435 #endif
4436
4437   if (!game.restart_level)
4438   {
4439     // copy default game door content to main double buffer
4440
4441     // !!! CHECK AGAIN !!!
4442     SetPanelBackground();
4443     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4444     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4445   }
4446
4447   SetPanelBackground();
4448   SetDrawBackgroundMask(REDRAW_DOOR_1);
4449
4450   UpdateAndDisplayGameControlValues();
4451
4452   if (!game.restart_level)
4453   {
4454     UnmapGameButtons();
4455     UnmapTapeButtons();
4456
4457     FreeGameButtons();
4458     CreateGameButtons();
4459
4460     MapGameButtons();
4461     MapTapeButtons();
4462
4463     // copy actual game door content to door double buffer for OpenDoor()
4464     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4465
4466     OpenDoor(DOOR_OPEN_ALL);
4467
4468     KeyboardAutoRepeatOffUnlessAutoplay();
4469
4470 #if DEBUG_INIT_PLAYER
4471     DebugPrintPlayerStatus("Player status (final)");
4472 #endif
4473   }
4474
4475   UnmapAllGadgets();
4476
4477   MapGameButtons();
4478   MapTapeButtons();
4479
4480   if (!game.restart_level && !tape.playing)
4481   {
4482     LevelStats_incPlayed(level_nr);
4483
4484     SaveLevelSetup_SeriesInfo();
4485   }
4486
4487   game.restart_level = FALSE;
4488   game.restart_game_message = NULL;
4489
4490   game.request_active = FALSE;
4491   game.request_active_or_moving = FALSE;
4492
4493   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4494     InitGameActions_MM();
4495
4496   SaveEngineSnapshotToListInitial();
4497
4498   if (!game.restart_level)
4499   {
4500     PlaySound(SND_GAME_STARTING);
4501
4502     if (setup.sound_music)
4503       PlayLevelMusic();
4504   }
4505
4506   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4507 }
4508
4509 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4510                         int actual_player_x, int actual_player_y)
4511 {
4512   // this is used for non-R'n'D game engines to update certain engine values
4513
4514   // needed to determine if sounds are played within the visible screen area
4515   scroll_x = actual_scroll_x;
4516   scroll_y = actual_scroll_y;
4517
4518   // needed to get player position for "follow finger" playing input method
4519   local_player->jx = actual_player_x;
4520   local_player->jy = actual_player_y;
4521 }
4522
4523 void InitMovDir(int x, int y)
4524 {
4525   int i, element = Tile[x][y];
4526   static int xy[4][2] =
4527   {
4528     {  0, +1 },
4529     { +1,  0 },
4530     {  0, -1 },
4531     { -1,  0 }
4532   };
4533   static int direction[3][4] =
4534   {
4535     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4536     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4537     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4538   };
4539
4540   switch (element)
4541   {
4542     case EL_BUG_RIGHT:
4543     case EL_BUG_UP:
4544     case EL_BUG_LEFT:
4545     case EL_BUG_DOWN:
4546       Tile[x][y] = EL_BUG;
4547       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4548       break;
4549
4550     case EL_SPACESHIP_RIGHT:
4551     case EL_SPACESHIP_UP:
4552     case EL_SPACESHIP_LEFT:
4553     case EL_SPACESHIP_DOWN:
4554       Tile[x][y] = EL_SPACESHIP;
4555       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4556       break;
4557
4558     case EL_BD_BUTTERFLY_RIGHT:
4559     case EL_BD_BUTTERFLY_UP:
4560     case EL_BD_BUTTERFLY_LEFT:
4561     case EL_BD_BUTTERFLY_DOWN:
4562       Tile[x][y] = EL_BD_BUTTERFLY;
4563       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4564       break;
4565
4566     case EL_BD_FIREFLY_RIGHT:
4567     case EL_BD_FIREFLY_UP:
4568     case EL_BD_FIREFLY_LEFT:
4569     case EL_BD_FIREFLY_DOWN:
4570       Tile[x][y] = EL_BD_FIREFLY;
4571       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4572       break;
4573
4574     case EL_PACMAN_RIGHT:
4575     case EL_PACMAN_UP:
4576     case EL_PACMAN_LEFT:
4577     case EL_PACMAN_DOWN:
4578       Tile[x][y] = EL_PACMAN;
4579       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4580       break;
4581
4582     case EL_YAMYAM_LEFT:
4583     case EL_YAMYAM_RIGHT:
4584     case EL_YAMYAM_UP:
4585     case EL_YAMYAM_DOWN:
4586       Tile[x][y] = EL_YAMYAM;
4587       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4588       break;
4589
4590     case EL_SP_SNIKSNAK:
4591       MovDir[x][y] = MV_UP;
4592       break;
4593
4594     case EL_SP_ELECTRON:
4595       MovDir[x][y] = MV_LEFT;
4596       break;
4597
4598     case EL_MOLE_LEFT:
4599     case EL_MOLE_RIGHT:
4600     case EL_MOLE_UP:
4601     case EL_MOLE_DOWN:
4602       Tile[x][y] = EL_MOLE;
4603       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4604       break;
4605
4606     case EL_SPRING_LEFT:
4607     case EL_SPRING_RIGHT:
4608       Tile[x][y] = EL_SPRING;
4609       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4610       break;
4611
4612     default:
4613       if (IS_CUSTOM_ELEMENT(element))
4614       {
4615         struct ElementInfo *ei = &element_info[element];
4616         int move_direction_initial = ei->move_direction_initial;
4617         int move_pattern = ei->move_pattern;
4618
4619         if (move_direction_initial == MV_START_PREVIOUS)
4620         {
4621           if (MovDir[x][y] != MV_NONE)
4622             return;
4623
4624           move_direction_initial = MV_START_AUTOMATIC;
4625         }
4626
4627         if (move_direction_initial == MV_START_RANDOM)
4628           MovDir[x][y] = 1 << RND(4);
4629         else if (move_direction_initial & MV_ANY_DIRECTION)
4630           MovDir[x][y] = move_direction_initial;
4631         else if (move_pattern == MV_ALL_DIRECTIONS ||
4632                  move_pattern == MV_TURNING_LEFT ||
4633                  move_pattern == MV_TURNING_RIGHT ||
4634                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4635                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4636                  move_pattern == MV_TURNING_RANDOM)
4637           MovDir[x][y] = 1 << RND(4);
4638         else if (move_pattern == MV_HORIZONTAL)
4639           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4640         else if (move_pattern == MV_VERTICAL)
4641           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4642         else if (move_pattern & MV_ANY_DIRECTION)
4643           MovDir[x][y] = element_info[element].move_pattern;
4644         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4645                  move_pattern == MV_ALONG_RIGHT_SIDE)
4646         {
4647           // use random direction as default start direction
4648           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4649             MovDir[x][y] = 1 << RND(4);
4650
4651           for (i = 0; i < NUM_DIRECTIONS; i++)
4652           {
4653             int x1 = x + xy[i][0];
4654             int y1 = y + xy[i][1];
4655
4656             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4657             {
4658               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4659                 MovDir[x][y] = direction[0][i];
4660               else
4661                 MovDir[x][y] = direction[1][i];
4662
4663               break;
4664             }
4665           }
4666         }                
4667       }
4668       else
4669       {
4670         MovDir[x][y] = 1 << RND(4);
4671
4672         if (element != EL_BUG &&
4673             element != EL_SPACESHIP &&
4674             element != EL_BD_BUTTERFLY &&
4675             element != EL_BD_FIREFLY)
4676           break;
4677
4678         for (i = 0; i < NUM_DIRECTIONS; i++)
4679         {
4680           int x1 = x + xy[i][0];
4681           int y1 = y + xy[i][1];
4682
4683           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4684           {
4685             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4686             {
4687               MovDir[x][y] = direction[0][i];
4688               break;
4689             }
4690             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4691                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4692             {
4693               MovDir[x][y] = direction[1][i];
4694               break;
4695             }
4696           }
4697         }
4698       }
4699       break;
4700   }
4701
4702   GfxDir[x][y] = MovDir[x][y];
4703 }
4704
4705 void InitAmoebaNr(int x, int y)
4706 {
4707   int i;
4708   int group_nr = AmoebaNeighbourNr(x, y);
4709
4710   if (group_nr == 0)
4711   {
4712     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4713     {
4714       if (AmoebaCnt[i] == 0)
4715       {
4716         group_nr = i;
4717         break;
4718       }
4719     }
4720   }
4721
4722   AmoebaNr[x][y] = group_nr;
4723   AmoebaCnt[group_nr]++;
4724   AmoebaCnt2[group_nr]++;
4725 }
4726
4727 static void LevelSolved_SetFinalGameValues(void)
4728 {
4729   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4730   game.score_time_final = (level.use_step_counter ? TimePlayed :
4731                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4732
4733   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4734                       game_em.lev->score :
4735                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4736                       game_mm.score :
4737                       game.score);
4738
4739   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                        MM_HEALTH(game_mm.laser_overload_value) :
4741                        game.health);
4742
4743   game.LevelSolved_CountingTime = game.time_final;
4744   game.LevelSolved_CountingScore = game.score_final;
4745   game.LevelSolved_CountingHealth = game.health_final;
4746 }
4747
4748 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4749 {
4750   game.LevelSolved_CountingTime = time;
4751   game.LevelSolved_CountingScore = score;
4752   game.LevelSolved_CountingHealth = health;
4753
4754   game_panel_controls[GAME_PANEL_TIME].value = time;
4755   game_panel_controls[GAME_PANEL_SCORE].value = score;
4756   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4757
4758   DisplayGameControlValues();
4759 }
4760
4761 static void LevelSolved(void)
4762 {
4763   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4764       game.players_still_needed > 0)
4765     return;
4766
4767   game.LevelSolved = TRUE;
4768   game.GameOver = TRUE;
4769
4770   // needed here to display correct panel values while player walks into exit
4771   LevelSolved_SetFinalGameValues();
4772 }
4773
4774 void GameWon(void)
4775 {
4776   static int time_count_steps;
4777   static int time, time_final;
4778   static float score, score_final; // needed for time score < 10 for 10 seconds
4779   static int health, health_final;
4780   static int game_over_delay_1 = 0;
4781   static int game_over_delay_2 = 0;
4782   static int game_over_delay_3 = 0;
4783   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4784   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4785
4786   if (!game.LevelSolved_GameWon)
4787   {
4788     int i;
4789
4790     // do not start end game actions before the player stops moving (to exit)
4791     if (local_player->active && local_player->MovPos)
4792       return;
4793
4794     // calculate final game values after player finished walking into exit
4795     LevelSolved_SetFinalGameValues();
4796
4797     game.LevelSolved_GameWon = TRUE;
4798     game.LevelSolved_SaveTape = tape.recording;
4799     game.LevelSolved_SaveScore = !tape.playing;
4800
4801     if (!tape.playing)
4802     {
4803       LevelStats_incSolved(level_nr);
4804
4805       SaveLevelSetup_SeriesInfo();
4806     }
4807
4808     if (tape.auto_play)         // tape might already be stopped here
4809       tape.auto_play_level_solved = TRUE;
4810
4811     TapeStop();
4812
4813     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4814     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4815     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4816
4817     time = time_final = game.time_final;
4818     score = score_final = game.score_final;
4819     health = health_final = game.health_final;
4820
4821     // update game panel values before (delayed) counting of score (if any)
4822     LevelSolved_DisplayFinalGameValues(time, score, health);
4823
4824     // if level has time score defined, calculate new final game values
4825     if (time_score > 0)
4826     {
4827       int time_final_max = 999;
4828       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4829       int time_frames = 0;
4830       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4831       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4832
4833       if (TimeLeft > 0)
4834       {
4835         time_final = 0;
4836         time_frames = time_frames_left;
4837       }
4838       else if (game.no_time_limit && TimePlayed < time_final_max)
4839       {
4840         time_final = time_final_max;
4841         time_frames = time_frames_final_max - time_frames_played;
4842       }
4843
4844       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4845
4846       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4847
4848       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4849       {
4850         health_final = 0;
4851         score_final += health * time_score;
4852       }
4853
4854       game.score_final = score_final;
4855       game.health_final = health_final;
4856     }
4857
4858     // if not counting score after game, immediately update game panel values
4859     if (level_editor_test_game || !setup.count_score_after_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864       LevelSolved_DisplayFinalGameValues(time, score, health);
4865     }
4866
4867     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4868     {
4869       // check if last player has left the level
4870       if (game.exit_x >= 0 &&
4871           game.exit_y >= 0)
4872       {
4873         int x = game.exit_x;
4874         int y = game.exit_y;
4875         int element = Tile[x][y];
4876
4877         // close exit door after last player
4878         if ((game.all_players_gone &&
4879              (element == EL_EXIT_OPEN ||
4880               element == EL_SP_EXIT_OPEN ||
4881               element == EL_STEEL_EXIT_OPEN)) ||
4882             element == EL_EM_EXIT_OPEN ||
4883             element == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885
4886           Tile[x][y] =
4887             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4888              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4889              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4890              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4891              EL_EM_STEEL_EXIT_CLOSING);
4892
4893           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4894         }
4895
4896         // player disappears
4897         DrawLevelField(x, y);
4898       }
4899
4900       for (i = 0; i < MAX_PLAYERS; i++)
4901       {
4902         struct PlayerInfo *player = &stored_player[i];
4903
4904         if (player->present)
4905         {
4906           RemovePlayer(player);
4907
4908           // player disappears
4909           DrawLevelField(player->jx, player->jy);
4910         }
4911       }
4912     }
4913
4914     PlaySound(SND_GAME_WINNING);
4915   }
4916
4917   if (setup.count_score_after_game)
4918   {
4919     if (time != time_final)
4920     {
4921       if (game_over_delay_1 > 0)
4922       {
4923         game_over_delay_1--;
4924
4925         return;
4926       }
4927
4928       int time_to_go = ABS(time_final - time);
4929       int time_count_dir = (time < time_final ? +1 : -1);
4930
4931       if (time_to_go < time_count_steps)
4932         time_count_steps = 1;
4933
4934       time  += time_count_steps * time_count_dir;
4935       score += time_count_steps * time_score;
4936
4937       // set final score to correct rounding differences after counting score
4938       if (time == time_final)
4939         score = score_final;
4940
4941       LevelSolved_DisplayFinalGameValues(time, score, health);
4942
4943       if (time == time_final)
4944         StopSound(SND_GAME_LEVELTIME_BONUS);
4945       else if (setup.sound_loops)
4946         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4947       else
4948         PlaySound(SND_GAME_LEVELTIME_BONUS);
4949
4950       return;
4951     }
4952
4953     if (health != health_final)
4954     {
4955       if (game_over_delay_2 > 0)
4956       {
4957         game_over_delay_2--;
4958
4959         return;
4960       }
4961
4962       int health_count_dir = (health < health_final ? +1 : -1);
4963
4964       health += health_count_dir;
4965       score  += time_score;
4966
4967       LevelSolved_DisplayFinalGameValues(time, score, health);
4968
4969       if (health == health_final)
4970         StopSound(SND_GAME_LEVELTIME_BONUS);
4971       else if (setup.sound_loops)
4972         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4973       else
4974         PlaySound(SND_GAME_LEVELTIME_BONUS);
4975
4976       return;
4977     }
4978   }
4979
4980   game.panel.active = FALSE;
4981
4982   if (game_over_delay_3 > 0)
4983   {
4984     game_over_delay_3--;
4985
4986     return;
4987   }
4988
4989   GameEnd();
4990 }
4991
4992 void GameEnd(void)
4993 {
4994   // used instead of "level_nr" (needed for network games)
4995   int last_level_nr = levelset.level_nr;
4996   boolean tape_saved = FALSE;
4997
4998   game.LevelSolved_GameEnd = TRUE;
4999
5000   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5001   {
5002     // make sure that request dialog to save tape does not open door again
5003     if (!global.use_envelope_request)
5004       CloseDoor(DOOR_CLOSE_1);
5005
5006     // ask to save tape
5007     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5008
5009     // set unique basename for score tape (also saved in high score table)
5010     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5011   }
5012
5013   // if no tape is to be saved, close both doors simultaneously
5014   CloseDoor(DOOR_CLOSE_ALL);
5015
5016   if (level_editor_test_game || score_info_tape_play)
5017   {
5018     SetGameStatus(GAME_MODE_MAIN);
5019
5020     DrawMainMenu();
5021
5022     return;
5023   }
5024
5025   if (!game.LevelSolved_SaveScore)
5026   {
5027     SetGameStatus(GAME_MODE_MAIN);
5028
5029     DrawMainMenu();
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037
5038     SaveLevelSetup_SeriesInfo();
5039   }
5040
5041   // save score and score tape before potentially erasing tape below
5042   NewHighScore(last_level_nr, tape_saved);
5043
5044   if (setup.increment_levels &&
5045       level_nr < leveldir_current->last_level &&
5046       !network_playing)
5047   {
5048     level_nr++;         // advance to next level
5049     TapeErase();        // start with empty tape
5050
5051     if (setup.auto_play_next_level)
5052     {
5053       scores.continue_playing = TRUE;
5054       scores.next_level_nr = level_nr;
5055
5056       LoadLevel(level_nr);
5057
5058       SaveLevelSetup_SeriesInfo();
5059     }
5060   }
5061
5062   if (scores.last_added >= 0 && setup.show_scores_after_game)
5063   {
5064     SetGameStatus(GAME_MODE_SCORES);
5065
5066     DrawHallOfFame(last_level_nr);
5067   }
5068   else if (scores.continue_playing)
5069   {
5070     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5071   }
5072   else
5073   {
5074     SetGameStatus(GAME_MODE_MAIN);
5075
5076     DrawMainMenu();
5077   }
5078 }
5079
5080 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5081                          boolean one_score_entry_per_name)
5082 {
5083   int i;
5084
5085   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5086     return -1;
5087
5088   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5089   {
5090     struct ScoreEntry *entry = &list->entry[i];
5091     boolean score_is_better = (new_entry->score >  entry->score);
5092     boolean score_is_equal  = (new_entry->score == entry->score);
5093     boolean time_is_better  = (new_entry->time  <  entry->time);
5094     boolean time_is_equal   = (new_entry->time  == entry->time);
5095     boolean better_by_score = (score_is_better ||
5096                                (score_is_equal && time_is_better));
5097     boolean better_by_time  = (time_is_better ||
5098                                (time_is_equal && score_is_better));
5099     boolean is_better = (level.rate_time_over_score ? better_by_time :
5100                          better_by_score);
5101     boolean entry_is_empty = (entry->score == 0 &&
5102                               entry->time == 0);
5103
5104     // prevent adding server score entries if also existing in local score file
5105     // (special case: historic score entries have an empty tape basename entry)
5106     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5107         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5108     {
5109       // special case: use server score instead of local score value if higher
5110       // (historic scores might have been truncated to 16-bit values locally)
5111       if (score_is_better)
5112         entry->score = new_entry->score;
5113
5114       return -1;
5115     }
5116
5117     if (is_better || entry_is_empty)
5118     {
5119       // player has made it to the hall of fame
5120
5121       if (i < MAX_SCORE_ENTRIES - 1)
5122       {
5123         int m = MAX_SCORE_ENTRIES - 1;
5124         int l;
5125
5126         if (one_score_entry_per_name)
5127         {
5128           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5129             if (strEqual(list->entry[l].name, new_entry->name))
5130               m = l;
5131
5132           if (m == i)   // player's new highscore overwrites his old one
5133             goto put_into_list;
5134         }
5135
5136         for (l = m; l > i; l--)
5137           list->entry[l] = list->entry[l - 1];
5138       }
5139
5140       put_into_list:
5141
5142       *entry = *new_entry;
5143
5144       return i;
5145     }
5146     else if (one_score_entry_per_name &&
5147              strEqual(entry->name, new_entry->name))
5148     {
5149       // player already in high score list with better score or time
5150
5151       return -1;
5152     }
5153   }
5154
5155   // special case: new score is beyond the last high score list position
5156   return MAX_SCORE_ENTRIES;
5157 }
5158
5159 void NewHighScore(int level_nr, boolean tape_saved)
5160 {
5161   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5162   boolean one_per_name = FALSE;
5163
5164   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5165   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5166
5167   new_entry.score = game.score_final;
5168   new_entry.time = game.score_time_final;
5169
5170   LoadScore(level_nr);
5171
5172   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5173
5174   if (scores.last_added >= MAX_SCORE_ENTRIES)
5175   {
5176     scores.last_added = MAX_SCORE_ENTRIES - 1;
5177     scores.force_last_added = TRUE;
5178
5179     scores.entry[scores.last_added] = new_entry;
5180
5181     // store last added local score entry (before merging server scores)
5182     scores.last_added_local = scores.last_added;
5183
5184     return;
5185   }
5186
5187   if (scores.last_added < 0)
5188     return;
5189
5190   SaveScore(level_nr);
5191
5192   // store last added local score entry (before merging server scores)
5193   scores.last_added_local = scores.last_added;
5194
5195   if (!game.LevelSolved_SaveTape)
5196     return;
5197
5198   SaveScoreTape(level_nr);
5199
5200   if (setup.ask_for_using_api_server)
5201   {
5202     setup.use_api_server =
5203       Request("Upload your score and tape to the high score server?", REQ_ASK);
5204
5205     if (!setup.use_api_server)
5206       Request("Not using high score server! Use setup menu to enable again!",
5207               REQ_CONFIRM);
5208
5209     runtime.use_api_server = setup.use_api_server;
5210
5211     // after asking for using API server once, do not ask again
5212     setup.ask_for_using_api_server = FALSE;
5213
5214     SaveSetup_ServerSetup();
5215   }
5216
5217   SaveServerScore(level_nr, tape_saved);
5218 }
5219
5220 void MergeServerScore(void)
5221 {
5222   struct ScoreEntry last_added_entry;
5223   boolean one_per_name = FALSE;
5224   int i;
5225
5226   if (scores.last_added >= 0)
5227     last_added_entry = scores.entry[scores.last_added];
5228
5229   for (i = 0; i < server_scores.num_entries; i++)
5230   {
5231     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5232
5233     if (pos >= 0 && pos <= scores.last_added)
5234       scores.last_added++;
5235   }
5236
5237   if (scores.last_added >= MAX_SCORE_ENTRIES)
5238   {
5239     scores.last_added = MAX_SCORE_ENTRIES - 1;
5240     scores.force_last_added = TRUE;
5241
5242     scores.entry[scores.last_added] = last_added_entry;
5243   }
5244 }
5245
5246 static int getElementMoveStepsizeExt(int x, int y, int direction)
5247 {
5248   int element = Tile[x][y];
5249   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5250   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5251   int horiz_move = (dx != 0);
5252   int sign = (horiz_move ? dx : dy);
5253   int step = sign * element_info[element].move_stepsize;
5254
5255   // special values for move stepsize for spring and things on conveyor belt
5256   if (horiz_move)
5257   {
5258     if (CAN_FALL(element) &&
5259         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5260       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5261     else if (element == EL_SPRING)
5262       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5263   }
5264
5265   return step;
5266 }
5267
5268 static int getElementMoveStepsize(int x, int y)
5269 {
5270   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5271 }
5272
5273 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5274 {
5275   if (player->GfxAction != action || player->GfxDir != dir)
5276   {
5277     player->GfxAction = action;
5278     player->GfxDir = dir;
5279     player->Frame = 0;
5280     player->StepFrame = 0;
5281   }
5282 }
5283
5284 static void ResetGfxFrame(int x, int y)
5285 {
5286   // profiling showed that "autotest" spends 10~20% of its time in this function
5287   if (DrawingDeactivatedField())
5288     return;
5289
5290   int element = Tile[x][y];
5291   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5292
5293   if (graphic_info[graphic].anim_global_sync)
5294     GfxFrame[x][y] = FrameCounter;
5295   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5296     GfxFrame[x][y] = CustomValue[x][y];
5297   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5298     GfxFrame[x][y] = element_info[element].collect_score;
5299   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5300     GfxFrame[x][y] = ChangeDelay[x][y];
5301 }
5302
5303 static void ResetGfxAnimation(int x, int y)
5304 {
5305   GfxAction[x][y] = ACTION_DEFAULT;
5306   GfxDir[x][y] = MovDir[x][y];
5307   GfxFrame[x][y] = 0;
5308
5309   ResetGfxFrame(x, y);
5310 }
5311
5312 static void ResetRandomAnimationValue(int x, int y)
5313 {
5314   GfxRandom[x][y] = INIT_GFX_RANDOM();
5315 }
5316
5317 static void InitMovingField(int x, int y, int direction)
5318 {
5319   int element = Tile[x][y];
5320   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5321   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5322   int newx = x + dx;
5323   int newy = y + dy;
5324   boolean is_moving_before, is_moving_after;
5325
5326   // check if element was/is moving or being moved before/after mode change
5327   is_moving_before = (WasJustMoving[x][y] != 0);
5328   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5329
5330   // reset animation only for moving elements which change direction of moving
5331   // or which just started or stopped moving
5332   // (else CEs with property "can move" / "not moving" are reset each frame)
5333   if (is_moving_before != is_moving_after ||
5334       direction != MovDir[x][y])
5335     ResetGfxAnimation(x, y);
5336
5337   MovDir[x][y] = direction;
5338   GfxDir[x][y] = direction;
5339
5340   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5341                      direction == MV_DOWN && CAN_FALL(element) ?
5342                      ACTION_FALLING : ACTION_MOVING);
5343
5344   // this is needed for CEs with property "can move" / "not moving"
5345
5346   if (is_moving_after)
5347   {
5348     if (Tile[newx][newy] == EL_EMPTY)
5349       Tile[newx][newy] = EL_BLOCKED;
5350
5351     MovDir[newx][newy] = MovDir[x][y];
5352
5353     CustomValue[newx][newy] = CustomValue[x][y];
5354
5355     GfxFrame[newx][newy] = GfxFrame[x][y];
5356     GfxRandom[newx][newy] = GfxRandom[x][y];
5357     GfxAction[newx][newy] = GfxAction[x][y];
5358     GfxDir[newx][newy] = GfxDir[x][y];
5359   }
5360 }
5361
5362 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5363 {
5364   int direction = MovDir[x][y];
5365   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5366   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5367
5368   *goes_to_x = newx;
5369   *goes_to_y = newy;
5370 }
5371
5372 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5373 {
5374   int oldx = x, oldy = y;
5375   int direction = MovDir[x][y];
5376
5377   if (direction == MV_LEFT)
5378     oldx++;
5379   else if (direction == MV_RIGHT)
5380     oldx--;
5381   else if (direction == MV_UP)
5382     oldy++;
5383   else if (direction == MV_DOWN)
5384     oldy--;
5385
5386   *comes_from_x = oldx;
5387   *comes_from_y = oldy;
5388 }
5389
5390 static int MovingOrBlocked2Element(int x, int y)
5391 {
5392   int element = Tile[x][y];
5393
5394   if (element == EL_BLOCKED)
5395   {
5396     int oldx, oldy;
5397
5398     Blocked2Moving(x, y, &oldx, &oldy);
5399     return Tile[oldx][oldy];
5400   }
5401   else
5402     return element;
5403 }
5404
5405 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5406 {
5407   // like MovingOrBlocked2Element(), but if element is moving
5408   // and (x,y) is the field the moving element is just leaving,
5409   // return EL_BLOCKED instead of the element value
5410   int element = Tile[x][y];
5411
5412   if (IS_MOVING(x, y))
5413   {
5414     if (element == EL_BLOCKED)
5415     {
5416       int oldx, oldy;
5417
5418       Blocked2Moving(x, y, &oldx, &oldy);
5419       return Tile[oldx][oldy];
5420     }
5421     else
5422       return EL_BLOCKED;
5423   }
5424   else
5425     return element;
5426 }
5427
5428 static void RemoveField(int x, int y)
5429 {
5430   Tile[x][y] = EL_EMPTY;
5431
5432   MovPos[x][y] = 0;
5433   MovDir[x][y] = 0;
5434   MovDelay[x][y] = 0;
5435
5436   CustomValue[x][y] = 0;
5437
5438   AmoebaNr[x][y] = 0;
5439   ChangeDelay[x][y] = 0;
5440   ChangePage[x][y] = -1;
5441   Pushed[x][y] = FALSE;
5442
5443   GfxElement[x][y] = EL_UNDEFINED;
5444   GfxAction[x][y] = ACTION_DEFAULT;
5445   GfxDir[x][y] = MV_NONE;
5446 }
5447
5448 static void RemoveMovingField(int x, int y)
5449 {
5450   int oldx = x, oldy = y, newx = x, newy = y;
5451   int element = Tile[x][y];
5452   int next_element = EL_UNDEFINED;
5453
5454   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5455     return;
5456
5457   if (IS_MOVING(x, y))
5458   {
5459     Moving2Blocked(x, y, &newx, &newy);
5460
5461     if (Tile[newx][newy] != EL_BLOCKED)
5462     {
5463       // element is moving, but target field is not free (blocked), but
5464       // already occupied by something different (example: acid pool);
5465       // in this case, only remove the moving field, but not the target
5466
5467       RemoveField(oldx, oldy);
5468
5469       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5470
5471       TEST_DrawLevelField(oldx, oldy);
5472
5473       return;
5474     }
5475   }
5476   else if (element == EL_BLOCKED)
5477   {
5478     Blocked2Moving(x, y, &oldx, &oldy);
5479     if (!IS_MOVING(oldx, oldy))
5480       return;
5481   }
5482
5483   if (element == EL_BLOCKED &&
5484       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5485        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5486        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5487        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5488        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5489        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5490     next_element = get_next_element(Tile[oldx][oldy]);
5491
5492   RemoveField(oldx, oldy);
5493   RemoveField(newx, newy);
5494
5495   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5496
5497   if (next_element != EL_UNDEFINED)
5498     Tile[oldx][oldy] = next_element;
5499
5500   TEST_DrawLevelField(oldx, oldy);
5501   TEST_DrawLevelField(newx, newy);
5502 }
5503
5504 void DrawDynamite(int x, int y)
5505 {
5506   int sx = SCREENX(x), sy = SCREENY(y);
5507   int graphic = el2img(Tile[x][y]);
5508   int frame;
5509
5510   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5511     return;
5512
5513   if (IS_WALKABLE_INSIDE(Back[x][y]))
5514     return;
5515
5516   if (Back[x][y])
5517     DrawLevelElement(x, y, Back[x][y]);
5518   else if (Store[x][y])
5519     DrawLevelElement(x, y, Store[x][y]);
5520   else if (game.use_masked_elements)
5521     DrawLevelElement(x, y, EL_EMPTY);
5522
5523   frame = getGraphicAnimationFrameXY(graphic, x, y);
5524
5525   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5526     DrawGraphicThruMask(sx, sy, graphic, frame);
5527   else
5528     DrawGraphic(sx, sy, graphic, frame);
5529 }
5530
5531 static void CheckDynamite(int x, int y)
5532 {
5533   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5534   {
5535     MovDelay[x][y]--;
5536
5537     if (MovDelay[x][y] != 0)
5538     {
5539       DrawDynamite(x, y);
5540       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5541
5542       return;
5543     }
5544   }
5545
5546   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5547
5548   Bang(x, y);
5549 }
5550
5551 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5552 {
5553   boolean num_checked_players = 0;
5554   int i;
5555
5556   for (i = 0; i < MAX_PLAYERS; i++)
5557   {
5558     if (stored_player[i].active)
5559     {
5560       int sx = stored_player[i].jx;
5561       int sy = stored_player[i].jy;
5562
5563       if (num_checked_players == 0)
5564       {
5565         *sx1 = *sx2 = sx;
5566         *sy1 = *sy2 = sy;
5567       }
5568       else
5569       {
5570         *sx1 = MIN(*sx1, sx);
5571         *sy1 = MIN(*sy1, sy);
5572         *sx2 = MAX(*sx2, sx);
5573         *sy2 = MAX(*sy2, sy);
5574       }
5575
5576       num_checked_players++;
5577     }
5578   }
5579 }
5580
5581 static boolean checkIfAllPlayersFitToScreen_RND(void)
5582 {
5583   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5584
5585   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5586
5587   return (sx2 - sx1 < SCR_FIELDX &&
5588           sy2 - sy1 < SCR_FIELDY);
5589 }
5590
5591 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5592 {
5593   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5594
5595   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5596
5597   *sx = (sx1 + sx2) / 2;
5598   *sy = (sy1 + sy2) / 2;
5599 }
5600
5601 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5602                                boolean center_screen, boolean quick_relocation)
5603 {
5604   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5605   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5606   boolean no_delay = (tape.warp_forward);
5607   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5608   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5609   int new_scroll_x, new_scroll_y;
5610
5611   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5612   {
5613     // case 1: quick relocation inside visible screen (without scrolling)
5614
5615     RedrawPlayfield();
5616
5617     return;
5618   }
5619
5620   if (!level.shifted_relocation || center_screen)
5621   {
5622     // relocation _with_ centering of screen
5623
5624     new_scroll_x = SCROLL_POSITION_X(x);
5625     new_scroll_y = SCROLL_POSITION_Y(y);
5626   }
5627   else
5628   {
5629     // relocation _without_ centering of screen
5630
5631     int center_scroll_x = SCROLL_POSITION_X(old_x);
5632     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5633     int offset_x = x + (scroll_x - center_scroll_x);
5634     int offset_y = y + (scroll_y - center_scroll_y);
5635
5636     // for new screen position, apply previous offset to center position
5637     new_scroll_x = SCROLL_POSITION_X(offset_x);
5638     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5639   }
5640
5641   if (quick_relocation)
5642   {
5643     // case 2: quick relocation (redraw without visible scrolling)
5644
5645     scroll_x = new_scroll_x;
5646     scroll_y = new_scroll_y;
5647
5648     RedrawPlayfield();
5649
5650     return;
5651   }
5652
5653   // case 3: visible relocation (with scrolling to new position)
5654
5655   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5656
5657   SetVideoFrameDelay(wait_delay_value);
5658
5659   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5660   {
5661     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5662     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5663
5664     if (dx == 0 && dy == 0)             // no scrolling needed at all
5665       break;
5666
5667     scroll_x -= dx;
5668     scroll_y -= dy;
5669
5670     // set values for horizontal/vertical screen scrolling (half tile size)
5671     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5672     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5673     int pos_x = dx * TILEX / 2;
5674     int pos_y = dy * TILEY / 2;
5675     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5676     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5677
5678     ScrollLevel(dx, dy);
5679     DrawAllPlayers();
5680
5681     // scroll in two steps of half tile size to make things smoother
5682     BlitScreenToBitmapExt_RND(window, fx, fy);
5683
5684     // scroll second step to align at full tile size
5685     BlitScreenToBitmap(window);
5686   }
5687
5688   DrawAllPlayers();
5689   BackToFront();
5690
5691   SetVideoFrameDelay(frame_delay_value_old);
5692 }
5693
5694 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5695 {
5696   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5697   int player_nr = GET_PLAYER_NR(el_player);
5698   struct PlayerInfo *player = &stored_player[player_nr];
5699   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5700   boolean no_delay = (tape.warp_forward);
5701   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5702   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5703   int old_jx = player->jx;
5704   int old_jy = player->jy;
5705   int old_element = Tile[old_jx][old_jy];
5706   int element = Tile[jx][jy];
5707   boolean player_relocated = (old_jx != jx || old_jy != jy);
5708
5709   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5710   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5711   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5712   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5713   int leave_side_horiz = move_dir_horiz;
5714   int leave_side_vert  = move_dir_vert;
5715   int enter_side = enter_side_horiz | enter_side_vert;
5716   int leave_side = leave_side_horiz | leave_side_vert;
5717
5718   if (player->buried)           // do not reanimate dead player
5719     return;
5720
5721   if (!player_relocated)        // no need to relocate the player
5722     return;
5723
5724   if (IS_PLAYER(jx, jy))        // player already placed at new position
5725   {
5726     RemoveField(jx, jy);        // temporarily remove newly placed player
5727     DrawLevelField(jx, jy);
5728   }
5729
5730   if (player->present)
5731   {
5732     while (player->MovPos)
5733     {
5734       ScrollPlayer(player, SCROLL_GO_ON);
5735       ScrollScreen(NULL, SCROLL_GO_ON);
5736
5737       AdvanceFrameAndPlayerCounters(player->index_nr);
5738
5739       DrawPlayer(player);
5740
5741       BackToFront_WithFrameDelay(wait_delay_value);
5742     }
5743
5744     DrawPlayer(player);         // needed here only to cleanup last field
5745     DrawLevelField(player->jx, player->jy);     // remove player graphic
5746
5747     player->is_moving = FALSE;
5748   }
5749
5750   if (IS_CUSTOM_ELEMENT(old_element))
5751     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5752                                CE_LEFT_BY_PLAYER,
5753                                player->index_bit, leave_side);
5754
5755   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5756                                       CE_PLAYER_LEAVES_X,
5757                                       player->index_bit, leave_side);
5758
5759   Tile[jx][jy] = el_player;
5760   InitPlayerField(jx, jy, el_player, TRUE);
5761
5762   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5763      possible that the relocation target field did not contain a player element,
5764      but a walkable element, to which the new player was relocated -- in this
5765      case, restore that (already initialized!) element on the player field */
5766   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5767   {
5768     Tile[jx][jy] = element;     // restore previously existing element
5769   }
5770
5771   // only visually relocate centered player
5772   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5773                      FALSE, level.instant_relocation);
5774
5775   TestIfPlayerTouchesBadThing(jx, jy);
5776   TestIfPlayerTouchesCustomElement(jx, jy);
5777
5778   if (IS_CUSTOM_ELEMENT(element))
5779     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5780                                player->index_bit, enter_side);
5781
5782   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5783                                       player->index_bit, enter_side);
5784
5785   if (player->is_switching)
5786   {
5787     /* ensure that relocation while still switching an element does not cause
5788        a new element to be treated as also switched directly after relocation
5789        (this is important for teleporter switches that teleport the player to
5790        a place where another teleporter switch is in the same direction, which
5791        would then incorrectly be treated as immediately switched before the
5792        direction key that caused the switch was released) */
5793
5794     player->switch_x += jx - old_jx;
5795     player->switch_y += jy - old_jy;
5796   }
5797 }
5798
5799 static void Explode(int ex, int ey, int phase, int mode)
5800 {
5801   int x, y;
5802   int last_phase;
5803   int border_element;
5804
5805   // !!! eliminate this variable !!!
5806   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5807
5808   if (game.explosions_delayed)
5809   {
5810     ExplodeField[ex][ey] = mode;
5811     return;
5812   }
5813
5814   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5815   {
5816     int center_element = Tile[ex][ey];
5817     int artwork_element, explosion_element;     // set these values later
5818
5819     // remove things displayed in background while burning dynamite
5820     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5821       Back[ex][ey] = 0;
5822
5823     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5824     {
5825       // put moving element to center field (and let it explode there)
5826       center_element = MovingOrBlocked2Element(ex, ey);
5827       RemoveMovingField(ex, ey);
5828       Tile[ex][ey] = center_element;
5829     }
5830
5831     // now "center_element" is finally determined -- set related values now
5832     artwork_element = center_element;           // for custom player artwork
5833     explosion_element = center_element;         // for custom player artwork
5834
5835     if (IS_PLAYER(ex, ey))
5836     {
5837       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5838
5839       artwork_element = stored_player[player_nr].artwork_element;
5840
5841       if (level.use_explosion_element[player_nr])
5842       {
5843         explosion_element = level.explosion_element[player_nr];
5844         artwork_element = explosion_element;
5845       }
5846     }
5847
5848     if (mode == EX_TYPE_NORMAL ||
5849         mode == EX_TYPE_CENTER ||
5850         mode == EX_TYPE_CROSS)
5851       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5852
5853     last_phase = element_info[explosion_element].explosion_delay + 1;
5854
5855     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5856     {
5857       int xx = x - ex + 1;
5858       int yy = y - ey + 1;
5859       int element;
5860
5861       if (!IN_LEV_FIELD(x, y) ||
5862           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5863           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5864         continue;
5865
5866       element = Tile[x][y];
5867
5868       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5869       {
5870         element = MovingOrBlocked2Element(x, y);
5871
5872         if (!IS_EXPLOSION_PROOF(element))
5873           RemoveMovingField(x, y);
5874       }
5875
5876       // indestructible elements can only explode in center (but not flames)
5877       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5878                                            mode == EX_TYPE_BORDER)) ||
5879           element == EL_FLAMES)
5880         continue;
5881
5882       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5883          behaviour, for example when touching a yamyam that explodes to rocks
5884          with active deadly shield, a rock is created under the player !!! */
5885       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5886 #if 0
5887       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5888           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5889            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5890 #else
5891       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5892 #endif
5893       {
5894         if (IS_ACTIVE_BOMB(element))
5895         {
5896           // re-activate things under the bomb like gate or penguin
5897           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5898           Back[x][y] = 0;
5899         }
5900
5901         continue;
5902       }
5903
5904       // save walkable background elements while explosion on same tile
5905       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5906           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5907         Back[x][y] = element;
5908
5909       // ignite explodable elements reached by other explosion
5910       if (element == EL_EXPLOSION)
5911         element = Store2[x][y];
5912
5913       if (AmoebaNr[x][y] &&
5914           (element == EL_AMOEBA_FULL ||
5915            element == EL_BD_AMOEBA ||
5916            element == EL_AMOEBA_GROWING))
5917       {
5918         AmoebaCnt[AmoebaNr[x][y]]--;
5919         AmoebaCnt2[AmoebaNr[x][y]]--;
5920       }
5921
5922       RemoveField(x, y);
5923
5924       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5925       {
5926         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5927
5928         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5929
5930         if (PLAYERINFO(ex, ey)->use_murphy)
5931           Store[x][y] = EL_EMPTY;
5932       }
5933
5934       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5935       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5936       else if (IS_PLAYER_ELEMENT(center_element))
5937         Store[x][y] = EL_EMPTY;
5938       else if (center_element == EL_YAMYAM)
5939         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5940       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5941         Store[x][y] = element_info[center_element].content.e[xx][yy];
5942 #if 1
5943       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5944       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5945       // otherwise) -- FIX THIS !!!
5946       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5947         Store[x][y] = element_info[element].content.e[1][1];
5948 #else
5949       else if (!CAN_EXPLODE(element))
5950         Store[x][y] = element_info[element].content.e[1][1];
5951 #endif
5952       else
5953         Store[x][y] = EL_EMPTY;
5954
5955       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5956           center_element == EL_AMOEBA_TO_DIAMOND)
5957         Store2[x][y] = element;
5958
5959       Tile[x][y] = EL_EXPLOSION;
5960       GfxElement[x][y] = artwork_element;
5961
5962       ExplodePhase[x][y] = 1;
5963       ExplodeDelay[x][y] = last_phase;
5964
5965       Stop[x][y] = TRUE;
5966     }
5967
5968     if (center_element == EL_YAMYAM)
5969       game.yamyam_content_nr =
5970         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5971
5972     return;
5973   }
5974
5975   if (Stop[ex][ey])
5976     return;
5977
5978   x = ex;
5979   y = ey;
5980
5981   if (phase == 1)
5982     GfxFrame[x][y] = 0;         // restart explosion animation
5983
5984   last_phase = ExplodeDelay[x][y];
5985
5986   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5987
5988   // this can happen if the player leaves an explosion just in time
5989   if (GfxElement[x][y] == EL_UNDEFINED)
5990     GfxElement[x][y] = EL_EMPTY;
5991
5992   border_element = Store2[x][y];
5993   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5994     border_element = StorePlayer[x][y];
5995
5996   if (phase == element_info[border_element].ignition_delay ||
5997       phase == last_phase)
5998   {
5999     boolean border_explosion = FALSE;
6000
6001     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6002         !PLAYER_EXPLOSION_PROTECTED(x, y))
6003     {
6004       KillPlayerUnlessExplosionProtected(x, y);
6005       border_explosion = TRUE;
6006     }
6007     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6008     {
6009       Tile[x][y] = Store2[x][y];
6010       Store2[x][y] = 0;
6011       Bang(x, y);
6012       border_explosion = TRUE;
6013     }
6014     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6015     {
6016       AmoebaToDiamond(x, y);
6017       Store2[x][y] = 0;
6018       border_explosion = TRUE;
6019     }
6020
6021     // if an element just explodes due to another explosion (chain-reaction),
6022     // do not immediately end the new explosion when it was the last frame of
6023     // the explosion (as it would be done in the following "if"-statement!)
6024     if (border_explosion && phase == last_phase)
6025       return;
6026   }
6027
6028   // this can happen if the player was just killed by an explosion
6029   if (GfxElement[x][y] == EL_UNDEFINED)
6030     GfxElement[x][y] = EL_EMPTY;
6031
6032   if (phase == last_phase)
6033   {
6034     int element;
6035
6036     element = Tile[x][y] = Store[x][y];
6037     Store[x][y] = Store2[x][y] = 0;
6038     GfxElement[x][y] = EL_UNDEFINED;
6039
6040     // player can escape from explosions and might therefore be still alive
6041     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6042         element <= EL_PLAYER_IS_EXPLODING_4)
6043     {
6044       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6045       int explosion_element = EL_PLAYER_1 + player_nr;
6046       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6047       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6048
6049       if (level.use_explosion_element[player_nr])
6050         explosion_element = level.explosion_element[player_nr];
6051
6052       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6053                     element_info[explosion_element].content.e[xx][yy]);
6054     }
6055
6056     // restore probably existing indestructible background element
6057     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6058       element = Tile[x][y] = Back[x][y];
6059     Back[x][y] = 0;
6060
6061     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6062     GfxDir[x][y] = MV_NONE;
6063     ChangeDelay[x][y] = 0;
6064     ChangePage[x][y] = -1;
6065
6066     CustomValue[x][y] = 0;
6067
6068     InitField_WithBug2(x, y, FALSE);
6069
6070     TEST_DrawLevelField(x, y);
6071
6072     TestIfElementTouchesCustomElement(x, y);
6073
6074     if (GFX_CRUMBLED(element))
6075       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076
6077     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6078       StorePlayer[x][y] = 0;
6079
6080     if (IS_PLAYER_ELEMENT(element))
6081       RelocatePlayer(x, y, element);
6082   }
6083   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6084   {
6085     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6086     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6087
6088     if (phase == delay)
6089       TEST_DrawLevelFieldCrumbled(x, y);
6090
6091     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6092     {
6093       DrawLevelElement(x, y, Back[x][y]);
6094       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6095     }
6096     else if (IS_WALKABLE_UNDER(Back[x][y]))
6097     {
6098       DrawLevelGraphic(x, y, graphic, frame);
6099       DrawLevelElementThruMask(x, y, Back[x][y]);
6100     }
6101     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6102       DrawLevelGraphic(x, y, graphic, frame);
6103   }
6104 }
6105
6106 static void DynaExplode(int ex, int ey)
6107 {
6108   int i, j;
6109   int dynabomb_element = Tile[ex][ey];
6110   int dynabomb_size = 1;
6111   boolean dynabomb_xl = FALSE;
6112   struct PlayerInfo *player;
6113   static int xy[4][2] =
6114   {
6115     { 0, -1 },
6116     { -1, 0 },
6117     { +1, 0 },
6118     { 0, +1 }
6119   };
6120
6121   if (IS_ACTIVE_BOMB(dynabomb_element))
6122   {
6123     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6124     dynabomb_size = player->dynabomb_size;
6125     dynabomb_xl = player->dynabomb_xl;
6126     player->dynabombs_left++;
6127   }
6128
6129   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6130
6131   for (i = 0; i < NUM_DIRECTIONS; i++)
6132   {
6133     for (j = 1; j <= dynabomb_size; j++)
6134     {
6135       int x = ex + j * xy[i][0];
6136       int y = ey + j * xy[i][1];
6137       int element;
6138
6139       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6140         break;
6141
6142       element = Tile[x][y];
6143
6144       // do not restart explosions of fields with active bombs
6145       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6146         continue;
6147
6148       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6149
6150       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6151           !IS_DIGGABLE(element) && !dynabomb_xl)
6152         break;
6153     }
6154   }
6155 }
6156
6157 void Bang(int x, int y)
6158 {
6159   int element = MovingOrBlocked2Element(x, y);
6160   int explosion_type = EX_TYPE_NORMAL;
6161
6162   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6163   {
6164     struct PlayerInfo *player = PLAYERINFO(x, y);
6165
6166     element = Tile[x][y] = player->initial_element;
6167
6168     if (level.use_explosion_element[player->index_nr])
6169     {
6170       int explosion_element = level.explosion_element[player->index_nr];
6171
6172       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6173         explosion_type = EX_TYPE_CROSS;
6174       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6175         explosion_type = EX_TYPE_CENTER;
6176     }
6177   }
6178
6179   switch (element)
6180   {
6181     case EL_BUG:
6182     case EL_SPACESHIP:
6183     case EL_BD_BUTTERFLY:
6184     case EL_BD_FIREFLY:
6185     case EL_YAMYAM:
6186     case EL_DARK_YAMYAM:
6187     case EL_ROBOT:
6188     case EL_PACMAN:
6189     case EL_MOLE:
6190       RaiseScoreElement(element);
6191       break;
6192
6193     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6194     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6195     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6196     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6197     case EL_DYNABOMB_INCREASE_NUMBER:
6198     case EL_DYNABOMB_INCREASE_SIZE:
6199     case EL_DYNABOMB_INCREASE_POWER:
6200       explosion_type = EX_TYPE_DYNA;
6201       break;
6202
6203     case EL_DC_LANDMINE:
6204       explosion_type = EX_TYPE_CENTER;
6205       break;
6206
6207     case EL_PENGUIN:
6208     case EL_LAMP:
6209     case EL_LAMP_ACTIVE:
6210     case EL_AMOEBA_TO_DIAMOND:
6211       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6212         explosion_type = EX_TYPE_CENTER;
6213       break;
6214
6215     default:
6216       if (element_info[element].explosion_type == EXPLODES_CROSS)
6217         explosion_type = EX_TYPE_CROSS;
6218       else if (element_info[element].explosion_type == EXPLODES_1X1)
6219         explosion_type = EX_TYPE_CENTER;
6220       break;
6221   }
6222
6223   if (explosion_type == EX_TYPE_DYNA)
6224     DynaExplode(x, y);
6225   else
6226     Explode(x, y, EX_PHASE_START, explosion_type);
6227
6228   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6229 }
6230
6231 static void SplashAcid(int x, int y)
6232 {
6233   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6234       (!IN_LEV_FIELD(x - 1, y - 2) ||
6235        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6236     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6237
6238   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6239       (!IN_LEV_FIELD(x + 1, y - 2) ||
6240        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6241     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6242
6243   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6244 }
6245
6246 static void InitBeltMovement(void)
6247 {
6248   static int belt_base_element[4] =
6249   {
6250     EL_CONVEYOR_BELT_1_LEFT,
6251     EL_CONVEYOR_BELT_2_LEFT,
6252     EL_CONVEYOR_BELT_3_LEFT,
6253     EL_CONVEYOR_BELT_4_LEFT
6254   };
6255   static int belt_base_active_element[4] =
6256   {
6257     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6258     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6259     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6260     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6261   };
6262
6263   int x, y, i, j;
6264
6265   // set frame order for belt animation graphic according to belt direction
6266   for (i = 0; i < NUM_BELTS; i++)
6267   {
6268     int belt_nr = i;
6269
6270     for (j = 0; j < NUM_BELT_PARTS; j++)
6271     {
6272       int element = belt_base_active_element[belt_nr] + j;
6273       int graphic_1 = el2img(element);
6274       int graphic_2 = el2panelimg(element);
6275
6276       if (game.belt_dir[i] == MV_LEFT)
6277       {
6278         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6279         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6280       }
6281       else
6282       {
6283         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6284         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6285       }
6286     }
6287   }
6288
6289   SCAN_PLAYFIELD(x, y)
6290   {
6291     int element = Tile[x][y];
6292
6293     for (i = 0; i < NUM_BELTS; i++)
6294     {
6295       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6296       {
6297         int e_belt_nr = getBeltNrFromBeltElement(element);
6298         int belt_nr = i;
6299
6300         if (e_belt_nr == belt_nr)
6301         {
6302           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6303
6304           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6305         }
6306       }
6307     }
6308   }
6309 }
6310
6311 static void ToggleBeltSwitch(int x, int y)
6312 {
6313   static int belt_base_element[4] =
6314   {
6315     EL_CONVEYOR_BELT_1_LEFT,
6316     EL_CONVEYOR_BELT_2_LEFT,
6317     EL_CONVEYOR_BELT_3_LEFT,
6318     EL_CONVEYOR_BELT_4_LEFT
6319   };
6320   static int belt_base_active_element[4] =
6321   {
6322     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6323     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6324     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6325     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6326   };
6327   static int belt_base_switch_element[4] =
6328   {
6329     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6330     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6331     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6332     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6333   };
6334   static int belt_move_dir[4] =
6335   {
6336     MV_LEFT,
6337     MV_NONE,
6338     MV_RIGHT,
6339     MV_NONE,
6340   };
6341
6342   int element = Tile[x][y];
6343   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6344   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6345   int belt_dir = belt_move_dir[belt_dir_nr];
6346   int xx, yy, i;
6347
6348   if (!IS_BELT_SWITCH(element))
6349     return;
6350
6351   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6352   game.belt_dir[belt_nr] = belt_dir;
6353
6354   if (belt_dir_nr == 3)
6355     belt_dir_nr = 1;
6356
6357   // set frame order for belt animation graphic according to belt direction
6358   for (i = 0; i < NUM_BELT_PARTS; i++)
6359   {
6360     int element = belt_base_active_element[belt_nr] + i;
6361     int graphic_1 = el2img(element);
6362     int graphic_2 = el2panelimg(element);
6363
6364     if (belt_dir == MV_LEFT)
6365     {
6366       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6367       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6368     }
6369     else
6370     {
6371       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6372       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6373     }
6374   }
6375
6376   SCAN_PLAYFIELD(xx, yy)
6377   {
6378     int element = Tile[xx][yy];
6379
6380     if (IS_BELT_SWITCH(element))
6381     {
6382       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6383
6384       if (e_belt_nr == belt_nr)
6385       {
6386         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6387         TEST_DrawLevelField(xx, yy);
6388       }
6389     }
6390     else if (IS_BELT(element) && belt_dir != MV_NONE)
6391     {
6392       int e_belt_nr = getBeltNrFromBeltElement(element);
6393
6394       if (e_belt_nr == belt_nr)
6395       {
6396         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6397
6398         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6399         TEST_DrawLevelField(xx, yy);
6400       }
6401     }
6402     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6403     {
6404       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6405
6406       if (e_belt_nr == belt_nr)
6407       {
6408         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6409
6410         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6411         TEST_DrawLevelField(xx, yy);
6412       }
6413     }
6414   }
6415 }
6416
6417 static void ToggleSwitchgateSwitch(int x, int y)
6418 {
6419   int xx, yy;
6420
6421   game.switchgate_pos = !game.switchgate_pos;
6422
6423   SCAN_PLAYFIELD(xx, yy)
6424   {
6425     int element = Tile[xx][yy];
6426
6427     if (element == EL_SWITCHGATE_SWITCH_UP)
6428     {
6429       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6430       TEST_DrawLevelField(xx, yy);
6431     }
6432     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6433     {
6434       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6435       TEST_DrawLevelField(xx, yy);
6436     }
6437     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6438     {
6439       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6440       TEST_DrawLevelField(xx, yy);
6441     }
6442     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6443     {
6444       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6445       TEST_DrawLevelField(xx, yy);
6446     }
6447     else if (element == EL_SWITCHGATE_OPEN ||
6448              element == EL_SWITCHGATE_OPENING)
6449     {
6450       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6451
6452       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6453     }
6454     else if (element == EL_SWITCHGATE_CLOSED ||
6455              element == EL_SWITCHGATE_CLOSING)
6456     {
6457       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6458
6459       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6460     }
6461   }
6462 }
6463
6464 static int getInvisibleActiveFromInvisibleElement(int element)
6465 {
6466   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6467           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6468           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6469           element);
6470 }
6471
6472 static int getInvisibleFromInvisibleActiveElement(int element)
6473 {
6474   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6475           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6476           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6477           element);
6478 }
6479
6480 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6481 {
6482   int x, y;
6483
6484   SCAN_PLAYFIELD(x, y)
6485   {
6486     int element = Tile[x][y];
6487
6488     if (element == EL_LIGHT_SWITCH &&
6489         game.light_time_left > 0)
6490     {
6491       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6492       TEST_DrawLevelField(x, y);
6493     }
6494     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6495              game.light_time_left == 0)
6496     {
6497       Tile[x][y] = EL_LIGHT_SWITCH;
6498       TEST_DrawLevelField(x, y);
6499     }
6500     else if (element == EL_EMC_DRIPPER &&
6501              game.light_time_left > 0)
6502     {
6503       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6504       TEST_DrawLevelField(x, y);
6505     }
6506     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6507              game.light_time_left == 0)
6508     {
6509       Tile[x][y] = EL_EMC_DRIPPER;
6510       TEST_DrawLevelField(x, y);
6511     }
6512     else if (element == EL_INVISIBLE_STEELWALL ||
6513              element == EL_INVISIBLE_WALL ||
6514              element == EL_INVISIBLE_SAND)
6515     {
6516       if (game.light_time_left > 0)
6517         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6518
6519       TEST_DrawLevelField(x, y);
6520
6521       // uncrumble neighbour fields, if needed
6522       if (element == EL_INVISIBLE_SAND)
6523         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6526              element == EL_INVISIBLE_WALL_ACTIVE ||
6527              element == EL_INVISIBLE_SAND_ACTIVE)
6528     {
6529       if (game.light_time_left == 0)
6530         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // re-crumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538   }
6539 }
6540
6541 static void RedrawAllInvisibleElementsForLenses(void)
6542 {
6543   int x, y;
6544
6545   SCAN_PLAYFIELD(x, y)
6546   {
6547     int element = Tile[x][y];
6548
6549     if (element == EL_EMC_DRIPPER &&
6550         game.lenses_time_left > 0)
6551     {
6552       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6553       TEST_DrawLevelField(x, y);
6554     }
6555     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6556              game.lenses_time_left == 0)
6557     {
6558       Tile[x][y] = EL_EMC_DRIPPER;
6559       TEST_DrawLevelField(x, y);
6560     }
6561     else if (element == EL_INVISIBLE_STEELWALL ||
6562              element == EL_INVISIBLE_WALL ||
6563              element == EL_INVISIBLE_SAND)
6564     {
6565       if (game.lenses_time_left > 0)
6566         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6567
6568       TEST_DrawLevelField(x, y);
6569
6570       // uncrumble neighbour fields, if needed
6571       if (element == EL_INVISIBLE_SAND)
6572         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6573     }
6574     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6575              element == EL_INVISIBLE_WALL_ACTIVE ||
6576              element == EL_INVISIBLE_SAND_ACTIVE)
6577     {
6578       if (game.lenses_time_left == 0)
6579         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6580
6581       TEST_DrawLevelField(x, y);
6582
6583       // re-crumble neighbour fields, if needed
6584       if (element == EL_INVISIBLE_SAND)
6585         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6586     }
6587   }
6588 }
6589
6590 static void RedrawAllInvisibleElementsForMagnifier(void)
6591 {
6592   int x, y;
6593
6594   SCAN_PLAYFIELD(x, y)
6595   {
6596     int element = Tile[x][y];
6597
6598     if (element == EL_EMC_FAKE_GRASS &&
6599         game.magnify_time_left > 0)
6600     {
6601       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6602       TEST_DrawLevelField(x, y);
6603     }
6604     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6605              game.magnify_time_left == 0)
6606     {
6607       Tile[x][y] = EL_EMC_FAKE_GRASS;
6608       TEST_DrawLevelField(x, y);
6609     }
6610     else if (IS_GATE_GRAY(element) &&
6611              game.magnify_time_left > 0)
6612     {
6613       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6614                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6615                     IS_EM_GATE_GRAY(element) ?
6616                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6617                     IS_EMC_GATE_GRAY(element) ?
6618                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6619                     IS_DC_GATE_GRAY(element) ?
6620                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6621                     element);
6622       TEST_DrawLevelField(x, y);
6623     }
6624     else if (IS_GATE_GRAY_ACTIVE(element) &&
6625              game.magnify_time_left == 0)
6626     {
6627       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6628                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6629                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6630                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6631                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6632                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6633                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6634                     EL_DC_GATE_WHITE_GRAY :
6635                     element);
6636       TEST_DrawLevelField(x, y);
6637     }
6638   }
6639 }
6640
6641 static void ToggleLightSwitch(int x, int y)
6642 {
6643   int element = Tile[x][y];
6644
6645   game.light_time_left =
6646     (element == EL_LIGHT_SWITCH ?
6647      level.time_light * FRAMES_PER_SECOND : 0);
6648
6649   RedrawAllLightSwitchesAndInvisibleElements();
6650 }
6651
6652 static void ActivateTimegateSwitch(int x, int y)
6653 {
6654   int xx, yy;
6655
6656   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6657
6658   SCAN_PLAYFIELD(xx, yy)
6659   {
6660     int element = Tile[xx][yy];
6661
6662     if (element == EL_TIMEGATE_CLOSED ||
6663         element == EL_TIMEGATE_CLOSING)
6664     {
6665       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6666       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6667     }
6668
6669     /*
6670     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6671     {
6672       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6673       TEST_DrawLevelField(xx, yy);
6674     }
6675     */
6676
6677   }
6678
6679   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6680                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6681 }
6682
6683 static void Impact(int x, int y)
6684 {
6685   boolean last_line = (y == lev_fieldy - 1);
6686   boolean object_hit = FALSE;
6687   boolean impact = (last_line || object_hit);
6688   int element = Tile[x][y];
6689   int smashed = EL_STEELWALL;
6690
6691   if (!last_line)       // check if element below was hit
6692   {
6693     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6694       return;
6695
6696     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6697                                          MovDir[x][y + 1] != MV_DOWN ||
6698                                          MovPos[x][y + 1] <= TILEY / 2));
6699
6700     // do not smash moving elements that left the smashed field in time
6701     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6702         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6703       object_hit = FALSE;
6704
6705 #if USE_QUICKSAND_IMPACT_BUGFIX
6706     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6707     {
6708       RemoveMovingField(x, y + 1);
6709       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6710       Tile[x][y + 2] = EL_ROCK;
6711       TEST_DrawLevelField(x, y + 2);
6712
6713       object_hit = TRUE;
6714     }
6715
6716     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6717     {
6718       RemoveMovingField(x, y + 1);
6719       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6720       Tile[x][y + 2] = EL_ROCK;
6721       TEST_DrawLevelField(x, y + 2);
6722
6723       object_hit = TRUE;
6724     }
6725 #endif
6726
6727     if (object_hit)
6728       smashed = MovingOrBlocked2Element(x, y + 1);
6729
6730     impact = (last_line || object_hit);
6731   }
6732
6733   if (!last_line && smashed == EL_ACID) // element falls into acid
6734   {
6735     SplashAcid(x, y + 1);
6736     return;
6737   }
6738
6739   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6740   // only reset graphic animation if graphic really changes after impact
6741   if (impact &&
6742       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6743   {
6744     ResetGfxAnimation(x, y);
6745     TEST_DrawLevelField(x, y);
6746   }
6747
6748   if (impact && CAN_EXPLODE_IMPACT(element))
6749   {
6750     Bang(x, y);
6751     return;
6752   }
6753   else if (impact && element == EL_PEARL &&
6754            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6755   {
6756     ResetGfxAnimation(x, y);
6757
6758     Tile[x][y] = EL_PEARL_BREAKING;
6759     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6760     return;
6761   }
6762   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6763   {
6764     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6765
6766     return;
6767   }
6768
6769   if (impact && element == EL_AMOEBA_DROP)
6770   {
6771     if (object_hit && IS_PLAYER(x, y + 1))
6772       KillPlayerUnlessEnemyProtected(x, y + 1);
6773     else if (object_hit && smashed == EL_PENGUIN)
6774       Bang(x, y + 1);
6775     else
6776     {
6777       Tile[x][y] = EL_AMOEBA_GROWING;
6778       Store[x][y] = EL_AMOEBA_WET;
6779
6780       ResetRandomAnimationValue(x, y);
6781     }
6782     return;
6783   }
6784
6785   if (object_hit)               // check which object was hit
6786   {
6787     if ((CAN_PASS_MAGIC_WALL(element) && 
6788          (smashed == EL_MAGIC_WALL ||
6789           smashed == EL_BD_MAGIC_WALL)) ||
6790         (CAN_PASS_DC_MAGIC_WALL(element) &&
6791          smashed == EL_DC_MAGIC_WALL))
6792     {
6793       int xx, yy;
6794       int activated_magic_wall =
6795         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6796          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6797          EL_DC_MAGIC_WALL_ACTIVE);
6798
6799       // activate magic wall / mill
6800       SCAN_PLAYFIELD(xx, yy)
6801       {
6802         if (Tile[xx][yy] == smashed)
6803           Tile[xx][yy] = activated_magic_wall;
6804       }
6805
6806       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6807       game.magic_wall_active = TRUE;
6808
6809       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6810                             SND_MAGIC_WALL_ACTIVATING :
6811                             smashed == EL_BD_MAGIC_WALL ?
6812                             SND_BD_MAGIC_WALL_ACTIVATING :
6813                             SND_DC_MAGIC_WALL_ACTIVATING));
6814     }
6815
6816     if (IS_PLAYER(x, y + 1))
6817     {
6818       if (CAN_SMASH_PLAYER(element))
6819       {
6820         KillPlayerUnlessEnemyProtected(x, y + 1);
6821         return;
6822       }
6823     }
6824     else if (smashed == EL_PENGUIN)
6825     {
6826       if (CAN_SMASH_PLAYER(element))
6827       {
6828         Bang(x, y + 1);
6829         return;
6830       }
6831     }
6832     else if (element == EL_BD_DIAMOND)
6833     {
6834       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6835       {
6836         Bang(x, y + 1);
6837         return;
6838       }
6839     }
6840     else if (((element == EL_SP_INFOTRON ||
6841                element == EL_SP_ZONK) &&
6842               (smashed == EL_SP_SNIKSNAK ||
6843                smashed == EL_SP_ELECTRON ||
6844                smashed == EL_SP_DISK_ORANGE)) ||
6845              (element == EL_SP_INFOTRON &&
6846               smashed == EL_SP_DISK_YELLOW))
6847     {
6848       Bang(x, y + 1);
6849       return;
6850     }
6851     else if (CAN_SMASH_EVERYTHING(element))
6852     {
6853       if (IS_CLASSIC_ENEMY(smashed) ||
6854           CAN_EXPLODE_SMASHED(smashed))
6855       {
6856         Bang(x, y + 1);
6857         return;
6858       }
6859       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6860       {
6861         if (smashed == EL_LAMP ||
6862             smashed == EL_LAMP_ACTIVE)
6863         {
6864           Bang(x, y + 1);
6865           return;
6866         }
6867         else if (smashed == EL_NUT)
6868         {
6869           Tile[x][y + 1] = EL_NUT_BREAKING;
6870           PlayLevelSound(x, y, SND_NUT_BREAKING);
6871           RaiseScoreElement(EL_NUT);
6872           return;
6873         }
6874         else if (smashed == EL_PEARL)
6875         {
6876           ResetGfxAnimation(x, y);
6877
6878           Tile[x][y + 1] = EL_PEARL_BREAKING;
6879           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6880           return;
6881         }
6882         else if (smashed == EL_DIAMOND)
6883         {
6884           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6885           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6886           return;
6887         }
6888         else if (IS_BELT_SWITCH(smashed))
6889         {
6890           ToggleBeltSwitch(x, y + 1);
6891         }
6892         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6893                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6894                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6895                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6896         {
6897           ToggleSwitchgateSwitch(x, y + 1);
6898         }
6899         else if (smashed == EL_LIGHT_SWITCH ||
6900                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6901         {
6902           ToggleLightSwitch(x, y + 1);
6903         }
6904         else
6905         {
6906           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6907
6908           CheckElementChangeBySide(x, y + 1, smashed, element,
6909                                    CE_SWITCHED, CH_SIDE_TOP);
6910           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6911                                             CH_SIDE_TOP);
6912         }
6913       }
6914       else
6915       {
6916         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6917       }
6918     }
6919   }
6920
6921   // play sound of magic wall / mill
6922   if (!last_line &&
6923       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6924        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6925        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6926   {
6927     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6928       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6929     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6930       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6931     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6932       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6933
6934     return;
6935   }
6936
6937   // play sound of object that hits the ground
6938   if (last_line || object_hit)
6939     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6940 }
6941
6942 static void TurnRoundExt(int x, int y)
6943 {
6944   static struct
6945   {
6946     int dx, dy;
6947   } move_xy[] =
6948   {
6949     {  0,  0 },
6950     { -1,  0 },
6951     { +1,  0 },
6952     {  0,  0 },
6953     {  0, -1 },
6954     {  0,  0 }, { 0, 0 }, { 0, 0 },
6955     {  0, +1 }
6956   };
6957   static struct
6958   {
6959     int left, right, back;
6960   } turn[] =
6961   {
6962     { 0,        0,              0        },
6963     { MV_DOWN,  MV_UP,          MV_RIGHT },
6964     { MV_UP,    MV_DOWN,        MV_LEFT  },
6965     { 0,        0,              0        },
6966     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6967     { 0,        0,              0        },
6968     { 0,        0,              0        },
6969     { 0,        0,              0        },
6970     { MV_RIGHT, MV_LEFT,        MV_UP    }
6971   };
6972
6973   int element = Tile[x][y];
6974   int move_pattern = element_info[element].move_pattern;
6975
6976   int old_move_dir = MovDir[x][y];
6977   int left_dir  = turn[old_move_dir].left;
6978   int right_dir = turn[old_move_dir].right;
6979   int back_dir  = turn[old_move_dir].back;
6980
6981   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6982   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6983   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6984   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6985
6986   int left_x  = x + left_dx,  left_y  = y + left_dy;
6987   int right_x = x + right_dx, right_y = y + right_dy;
6988   int move_x  = x + move_dx,  move_y  = y + move_dy;
6989
6990   int xx, yy;
6991
6992   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6993   {
6994     TestIfBadThingTouchesOtherBadThing(x, y);
6995
6996     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6997       MovDir[x][y] = right_dir;
6998     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6999       MovDir[x][y] = left_dir;
7000
7001     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7002       MovDelay[x][y] = 9;
7003     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7004       MovDelay[x][y] = 1;
7005   }
7006   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7007   {
7008     TestIfBadThingTouchesOtherBadThing(x, y);
7009
7010     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7011       MovDir[x][y] = left_dir;
7012     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7013       MovDir[x][y] = right_dir;
7014
7015     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7016       MovDelay[x][y] = 9;
7017     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7018       MovDelay[x][y] = 1;
7019   }
7020   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7021   {
7022     TestIfBadThingTouchesOtherBadThing(x, y);
7023
7024     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7025       MovDir[x][y] = left_dir;
7026     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7027       MovDir[x][y] = right_dir;
7028
7029     if (MovDir[x][y] != old_move_dir)
7030       MovDelay[x][y] = 9;
7031   }
7032   else if (element == EL_YAMYAM)
7033   {
7034     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7035     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7036
7037     if (can_turn_left && can_turn_right)
7038       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7039     else if (can_turn_left)
7040       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7041     else if (can_turn_right)
7042       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7043     else
7044       MovDir[x][y] = back_dir;
7045
7046     MovDelay[x][y] = 16 + 16 * RND(3);
7047   }
7048   else if (element == EL_DARK_YAMYAM)
7049   {
7050     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7051                                                          left_x, left_y);
7052     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7053                                                          right_x, right_y);
7054
7055     if (can_turn_left && can_turn_right)
7056       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7057     else if (can_turn_left)
7058       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7059     else if (can_turn_right)
7060       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7061     else
7062       MovDir[x][y] = back_dir;
7063
7064     MovDelay[x][y] = 16 + 16 * RND(3);
7065   }
7066   else if (element == EL_PACMAN)
7067   {
7068     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7069     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7070
7071     if (can_turn_left && can_turn_right)
7072       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7073     else if (can_turn_left)
7074       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7075     else if (can_turn_right)
7076       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7077     else
7078       MovDir[x][y] = back_dir;
7079
7080     MovDelay[x][y] = 6 + RND(40);
7081   }
7082   else if (element == EL_PIG)
7083   {
7084     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7085     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7086     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7087     boolean should_turn_left, should_turn_right, should_move_on;
7088     int rnd_value = 24;
7089     int rnd = RND(rnd_value);
7090
7091     should_turn_left = (can_turn_left &&
7092                         (!can_move_on ||
7093                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7094                                                    y + back_dy + left_dy)));
7095     should_turn_right = (can_turn_right &&
7096                          (!can_move_on ||
7097                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7098                                                     y + back_dy + right_dy)));
7099     should_move_on = (can_move_on &&
7100                       (!can_turn_left ||
7101                        !can_turn_right ||
7102                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7103                                                  y + move_dy + left_dy) ||
7104                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7105                                                  y + move_dy + right_dy)));
7106
7107     if (should_turn_left || should_turn_right || should_move_on)
7108     {
7109       if (should_turn_left && should_turn_right && should_move_on)
7110         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7111                         rnd < 2 * rnd_value / 3 ? right_dir :
7112                         old_move_dir);
7113       else if (should_turn_left && should_turn_right)
7114         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115       else if (should_turn_left && should_move_on)
7116         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7117       else if (should_turn_right && should_move_on)
7118         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7119       else if (should_turn_left)
7120         MovDir[x][y] = left_dir;
7121       else if (should_turn_right)
7122         MovDir[x][y] = right_dir;
7123       else if (should_move_on)
7124         MovDir[x][y] = old_move_dir;
7125     }
7126     else if (can_move_on && rnd > rnd_value / 8)
7127       MovDir[x][y] = old_move_dir;
7128     else if (can_turn_left && can_turn_right)
7129       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7130     else if (can_turn_left && rnd > rnd_value / 8)
7131       MovDir[x][y] = left_dir;
7132     else if (can_turn_right && rnd > rnd_value/8)
7133       MovDir[x][y] = right_dir;
7134     else
7135       MovDir[x][y] = back_dir;
7136
7137     xx = x + move_xy[MovDir[x][y]].dx;
7138     yy = y + move_xy[MovDir[x][y]].dy;
7139
7140     if (!IN_LEV_FIELD(xx, yy) ||
7141         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7142       MovDir[x][y] = old_move_dir;
7143
7144     MovDelay[x][y] = 0;
7145   }
7146   else if (element == EL_DRAGON)
7147   {
7148     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7149     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7150     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7151     int rnd_value = 24;
7152     int rnd = RND(rnd_value);
7153
7154     if (can_move_on && rnd > rnd_value / 8)
7155       MovDir[x][y] = old_move_dir;
7156     else if (can_turn_left && can_turn_right)
7157       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7158     else if (can_turn_left && rnd > rnd_value / 8)
7159       MovDir[x][y] = left_dir;
7160     else if (can_turn_right && rnd > rnd_value / 8)
7161       MovDir[x][y] = right_dir;
7162     else
7163       MovDir[x][y] = back_dir;
7164
7165     xx = x + move_xy[MovDir[x][y]].dx;
7166     yy = y + move_xy[MovDir[x][y]].dy;
7167
7168     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7169       MovDir[x][y] = old_move_dir;
7170
7171     MovDelay[x][y] = 0;
7172   }
7173   else if (element == EL_MOLE)
7174   {
7175     boolean can_move_on =
7176       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7177                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7178                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7179     if (!can_move_on)
7180     {
7181       boolean can_turn_left =
7182         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7183                               IS_AMOEBOID(Tile[left_x][left_y])));
7184
7185       boolean can_turn_right =
7186         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7187                               IS_AMOEBOID(Tile[right_x][right_y])));
7188
7189       if (can_turn_left && can_turn_right)
7190         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7191       else if (can_turn_left)
7192         MovDir[x][y] = left_dir;
7193       else
7194         MovDir[x][y] = right_dir;
7195     }
7196
7197     if (MovDir[x][y] != old_move_dir)
7198       MovDelay[x][y] = 9;
7199   }
7200   else if (element == EL_BALLOON)
7201   {
7202     MovDir[x][y] = game.wind_direction;
7203     MovDelay[x][y] = 0;
7204   }
7205   else if (element == EL_SPRING)
7206   {
7207     if (MovDir[x][y] & MV_HORIZONTAL)
7208     {
7209       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7210           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7211       {
7212         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7213         ResetGfxAnimation(move_x, move_y);
7214         TEST_DrawLevelField(move_x, move_y);
7215
7216         MovDir[x][y] = back_dir;
7217       }
7218       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7219                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7220         MovDir[x][y] = MV_NONE;
7221     }
7222
7223     MovDelay[x][y] = 0;
7224   }
7225   else if (element == EL_ROBOT ||
7226            element == EL_SATELLITE ||
7227            element == EL_PENGUIN ||
7228            element == EL_EMC_ANDROID)
7229   {
7230     int attr_x = -1, attr_y = -1;
7231
7232     if (game.all_players_gone)
7233     {
7234       attr_x = game.exit_x;
7235       attr_y = game.exit_y;
7236     }
7237     else
7238     {
7239       int i;
7240
7241       for (i = 0; i < MAX_PLAYERS; i++)
7242       {
7243         struct PlayerInfo *player = &stored_player[i];
7244         int jx = player->jx, jy = player->jy;
7245
7246         if (!player->active)
7247           continue;
7248
7249         if (attr_x == -1 ||
7250             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7251         {
7252           attr_x = jx;
7253           attr_y = jy;
7254         }
7255       }
7256     }
7257
7258     if (element == EL_ROBOT &&
7259         game.robot_wheel_x >= 0 &&
7260         game.robot_wheel_y >= 0 &&
7261         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7262          game.engine_version < VERSION_IDENT(3,1,0,0)))
7263     {
7264       attr_x = game.robot_wheel_x;
7265       attr_y = game.robot_wheel_y;
7266     }
7267
7268     if (element == EL_PENGUIN)
7269     {
7270       int i;
7271       static int xy[4][2] =
7272       {
7273         { 0, -1 },
7274         { -1, 0 },
7275         { +1, 0 },
7276         { 0, +1 }
7277       };
7278
7279       for (i = 0; i < NUM_DIRECTIONS; i++)
7280       {
7281         int ex = x + xy[i][0];
7282         int ey = y + xy[i][1];
7283
7284         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7285                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7286                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7287                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7288         {
7289           attr_x = ex;
7290           attr_y = ey;
7291           break;
7292         }
7293       }
7294     }
7295
7296     MovDir[x][y] = MV_NONE;
7297     if (attr_x < x)
7298       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7299     else if (attr_x > x)
7300       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7301     if (attr_y < y)
7302       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7303     else if (attr_y > y)
7304       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7305
7306     if (element == EL_ROBOT)
7307     {
7308       int newx, newy;
7309
7310       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7311         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7312       Moving2Blocked(x, y, &newx, &newy);
7313
7314       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7315         MovDelay[x][y] = 8 + 8 * !RND(3);
7316       else
7317         MovDelay[x][y] = 16;
7318     }
7319     else if (element == EL_PENGUIN)
7320     {
7321       int newx, newy;
7322
7323       MovDelay[x][y] = 1;
7324
7325       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7326       {
7327         boolean first_horiz = RND(2);
7328         int new_move_dir = MovDir[x][y];
7329
7330         MovDir[x][y] =
7331           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7332         Moving2Blocked(x, y, &newx, &newy);
7333
7334         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7335           return;
7336
7337         MovDir[x][y] =
7338           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7339         Moving2Blocked(x, y, &newx, &newy);
7340
7341         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7342           return;
7343
7344         MovDir[x][y] = old_move_dir;
7345         return;
7346       }
7347     }
7348     else if (element == EL_SATELLITE)
7349     {
7350       int newx, newy;
7351
7352       MovDelay[x][y] = 1;
7353
7354       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7355       {
7356         boolean first_horiz = RND(2);
7357         int new_move_dir = MovDir[x][y];
7358
7359         MovDir[x][y] =
7360           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7361         Moving2Blocked(x, y, &newx, &newy);
7362
7363         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7364           return;
7365
7366         MovDir[x][y] =
7367           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368         Moving2Blocked(x, y, &newx, &newy);
7369
7370         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7371           return;
7372
7373         MovDir[x][y] = old_move_dir;
7374         return;
7375       }
7376     }
7377     else if (element == EL_EMC_ANDROID)
7378     {
7379       static int check_pos[16] =
7380       {
7381         -1,             //  0 => (invalid)
7382         7,              //  1 => MV_LEFT
7383         3,              //  2 => MV_RIGHT
7384         -1,             //  3 => (invalid)
7385         1,              //  4 =>            MV_UP
7386         0,              //  5 => MV_LEFT  | MV_UP
7387         2,              //  6 => MV_RIGHT | MV_UP
7388         -1,             //  7 => (invalid)
7389         5,              //  8 =>            MV_DOWN
7390         6,              //  9 => MV_LEFT  | MV_DOWN
7391         4,              // 10 => MV_RIGHT | MV_DOWN
7392         -1,             // 11 => (invalid)
7393         -1,             // 12 => (invalid)
7394         -1,             // 13 => (invalid)
7395         -1,             // 14 => (invalid)
7396         -1,             // 15 => (invalid)
7397       };
7398       static struct
7399       {
7400         int dx, dy;
7401         int dir;
7402       } check_xy[8] =
7403       {
7404         { -1, -1,       MV_LEFT  | MV_UP   },
7405         {  0, -1,                  MV_UP   },
7406         { +1, -1,       MV_RIGHT | MV_UP   },
7407         { +1,  0,       MV_RIGHT           },
7408         { +1, +1,       MV_RIGHT | MV_DOWN },
7409         {  0, +1,                  MV_DOWN },
7410         { -1, +1,       MV_LEFT  | MV_DOWN },
7411         { -1,  0,       MV_LEFT            },
7412       };
7413       int start_pos, check_order;
7414       boolean can_clone = FALSE;
7415       int i;
7416
7417       // check if there is any free field around current position
7418       for (i = 0; i < 8; i++)
7419       {
7420         int newx = x + check_xy[i].dx;
7421         int newy = y + check_xy[i].dy;
7422
7423         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7424         {
7425           can_clone = TRUE;
7426
7427           break;
7428         }
7429       }
7430
7431       if (can_clone)            // randomly find an element to clone
7432       {
7433         can_clone = FALSE;
7434
7435         start_pos = check_pos[RND(8)];
7436         check_order = (RND(2) ? -1 : +1);
7437
7438         for (i = 0; i < 8; i++)
7439         {
7440           int pos_raw = start_pos + i * check_order;
7441           int pos = (pos_raw + 8) % 8;
7442           int newx = x + check_xy[pos].dx;
7443           int newy = y + check_xy[pos].dy;
7444
7445           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7446           {
7447             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7448             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7449
7450             Store[x][y] = Tile[newx][newy];
7451
7452             can_clone = TRUE;
7453
7454             break;
7455           }
7456         }
7457       }
7458
7459       if (can_clone)            // randomly find a direction to move
7460       {
7461         can_clone = FALSE;
7462
7463         start_pos = check_pos[RND(8)];
7464         check_order = (RND(2) ? -1 : +1);
7465
7466         for (i = 0; i < 8; i++)
7467         {
7468           int pos_raw = start_pos + i * check_order;
7469           int pos = (pos_raw + 8) % 8;
7470           int newx = x + check_xy[pos].dx;
7471           int newy = y + check_xy[pos].dy;
7472           int new_move_dir = check_xy[pos].dir;
7473
7474           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7475           {
7476             MovDir[x][y] = new_move_dir;
7477             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7478
7479             can_clone = TRUE;
7480
7481             break;
7482           }
7483         }
7484       }
7485
7486       if (can_clone)            // cloning and moving successful
7487         return;
7488
7489       // cannot clone -- try to move towards player
7490
7491       start_pos = check_pos[MovDir[x][y] & 0x0f];
7492       check_order = (RND(2) ? -1 : +1);
7493
7494       for (i = 0; i < 3; i++)
7495       {
7496         // first check start_pos, then previous/next or (next/previous) pos
7497         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7498         int pos = (pos_raw + 8) % 8;
7499         int newx = x + check_xy[pos].dx;
7500         int newy = y + check_xy[pos].dy;
7501         int new_move_dir = check_xy[pos].dir;
7502
7503         if (IS_PLAYER(newx, newy))
7504           break;
7505
7506         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7507         {
7508           MovDir[x][y] = new_move_dir;
7509           MovDelay[x][y] = level.android_move_time * 8 + 1;
7510
7511           break;
7512         }
7513       }
7514     }
7515   }
7516   else if (move_pattern == MV_TURNING_LEFT ||
7517            move_pattern == MV_TURNING_RIGHT ||
7518            move_pattern == MV_TURNING_LEFT_RIGHT ||
7519            move_pattern == MV_TURNING_RIGHT_LEFT ||
7520            move_pattern == MV_TURNING_RANDOM ||
7521            move_pattern == MV_ALL_DIRECTIONS)
7522   {
7523     boolean can_turn_left =
7524       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7525     boolean can_turn_right =
7526       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7527
7528     if (element_info[element].move_stepsize == 0)       // "not moving"
7529       return;
7530
7531     if (move_pattern == MV_TURNING_LEFT)
7532       MovDir[x][y] = left_dir;
7533     else if (move_pattern == MV_TURNING_RIGHT)
7534       MovDir[x][y] = right_dir;
7535     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7536       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7537     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7538       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7539     else if (move_pattern == MV_TURNING_RANDOM)
7540       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7541                       can_turn_right && !can_turn_left ? right_dir :
7542                       RND(2) ? left_dir : right_dir);
7543     else if (can_turn_left && can_turn_right)
7544       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7545     else if (can_turn_left)
7546       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7547     else if (can_turn_right)
7548       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7549     else
7550       MovDir[x][y] = back_dir;
7551
7552     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7553   }
7554   else if (move_pattern == MV_HORIZONTAL ||
7555            move_pattern == MV_VERTICAL)
7556   {
7557     if (move_pattern & old_move_dir)
7558       MovDir[x][y] = back_dir;
7559     else if (move_pattern == MV_HORIZONTAL)
7560       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7561     else if (move_pattern == MV_VERTICAL)
7562       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7563
7564     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565   }
7566   else if (move_pattern & MV_ANY_DIRECTION)
7567   {
7568     MovDir[x][y] = move_pattern;
7569     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7570   }
7571   else if (move_pattern & MV_WIND_DIRECTION)
7572   {
7573     MovDir[x][y] = game.wind_direction;
7574     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575   }
7576   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7577   {
7578     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7579       MovDir[x][y] = left_dir;
7580     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581       MovDir[x][y] = right_dir;
7582
7583     if (MovDir[x][y] != old_move_dir)
7584       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7585   }
7586   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7587   {
7588     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7589       MovDir[x][y] = right_dir;
7590     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7591       MovDir[x][y] = left_dir;
7592
7593     if (MovDir[x][y] != old_move_dir)
7594       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7595   }
7596   else if (move_pattern == MV_TOWARDS_PLAYER ||
7597            move_pattern == MV_AWAY_FROM_PLAYER)
7598   {
7599     int attr_x = -1, attr_y = -1;
7600     int newx, newy;
7601     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7602
7603     if (game.all_players_gone)
7604     {
7605       attr_x = game.exit_x;
7606       attr_y = game.exit_y;
7607     }
7608     else
7609     {
7610       int i;
7611
7612       for (i = 0; i < MAX_PLAYERS; i++)
7613       {
7614         struct PlayerInfo *player = &stored_player[i];
7615         int jx = player->jx, jy = player->jy;
7616
7617         if (!player->active)
7618           continue;
7619
7620         if (attr_x == -1 ||
7621             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7622         {
7623           attr_x = jx;
7624           attr_y = jy;
7625         }
7626       }
7627     }
7628
7629     MovDir[x][y] = MV_NONE;
7630     if (attr_x < x)
7631       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7632     else if (attr_x > x)
7633       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7634     if (attr_y < y)
7635       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7636     else if (attr_y > y)
7637       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7638
7639     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7640
7641     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7642     {
7643       boolean first_horiz = RND(2);
7644       int new_move_dir = MovDir[x][y];
7645
7646       if (element_info[element].move_stepsize == 0)     // "not moving"
7647       {
7648         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7649         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7650
7651         return;
7652       }
7653
7654       MovDir[x][y] =
7655         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7656       Moving2Blocked(x, y, &newx, &newy);
7657
7658       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7659         return;
7660
7661       MovDir[x][y] =
7662         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7663       Moving2Blocked(x, y, &newx, &newy);
7664
7665       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7666         return;
7667
7668       MovDir[x][y] = old_move_dir;
7669     }
7670   }
7671   else if (move_pattern == MV_WHEN_PUSHED ||
7672            move_pattern == MV_WHEN_DROPPED)
7673   {
7674     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7675       MovDir[x][y] = MV_NONE;
7676
7677     MovDelay[x][y] = 0;
7678   }
7679   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7680   {
7681     static int test_xy[7][2] =
7682     {
7683       { 0, -1 },
7684       { -1, 0 },
7685       { +1, 0 },
7686       { 0, +1 },
7687       { 0, -1 },
7688       { -1, 0 },
7689       { +1, 0 },
7690     };
7691     static int test_dir[7] =
7692     {
7693       MV_UP,
7694       MV_LEFT,
7695       MV_RIGHT,
7696       MV_DOWN,
7697       MV_UP,
7698       MV_LEFT,
7699       MV_RIGHT,
7700     };
7701     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7702     int move_preference = -1000000;     // start with very low preference
7703     int new_move_dir = MV_NONE;
7704     int start_test = RND(4);
7705     int i;
7706
7707     for (i = 0; i < NUM_DIRECTIONS; i++)
7708     {
7709       int move_dir = test_dir[start_test + i];
7710       int move_dir_preference;
7711
7712       xx = x + test_xy[start_test + i][0];
7713       yy = y + test_xy[start_test + i][1];
7714
7715       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7716           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7717       {
7718         new_move_dir = move_dir;
7719
7720         break;
7721       }
7722
7723       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7724         continue;
7725
7726       move_dir_preference = -1 * RunnerVisit[xx][yy];
7727       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7728         move_dir_preference = PlayerVisit[xx][yy];
7729
7730       if (move_dir_preference > move_preference)
7731       {
7732         // prefer field that has not been visited for the longest time
7733         move_preference = move_dir_preference;
7734         new_move_dir = move_dir;
7735       }
7736       else if (move_dir_preference == move_preference &&
7737                move_dir == old_move_dir)
7738       {
7739         // prefer last direction when all directions are preferred equally
7740         move_preference = move_dir_preference;
7741         new_move_dir = move_dir;
7742       }
7743     }
7744
7745     MovDir[x][y] = new_move_dir;
7746     if (old_move_dir != new_move_dir)
7747       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7748   }
7749 }
7750
7751 static void TurnRound(int x, int y)
7752 {
7753   int direction = MovDir[x][y];
7754
7755   TurnRoundExt(x, y);
7756
7757   GfxDir[x][y] = MovDir[x][y];
7758
7759   if (direction != MovDir[x][y])
7760     GfxFrame[x][y] = 0;
7761
7762   if (MovDelay[x][y])
7763     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7764
7765   ResetGfxFrame(x, y);
7766 }
7767
7768 static boolean JustBeingPushed(int x, int y)
7769 {
7770   int i;
7771
7772   for (i = 0; i < MAX_PLAYERS; i++)
7773   {
7774     struct PlayerInfo *player = &stored_player[i];
7775
7776     if (player->active && player->is_pushing && player->MovPos)
7777     {
7778       int next_jx = player->jx + (player->jx - player->last_jx);
7779       int next_jy = player->jy + (player->jy - player->last_jy);
7780
7781       if (x == next_jx && y == next_jy)
7782         return TRUE;
7783     }
7784   }
7785
7786   return FALSE;
7787 }
7788
7789 static void StartMoving(int x, int y)
7790 {
7791   boolean started_moving = FALSE;       // some elements can fall _and_ move
7792   int element = Tile[x][y];
7793
7794   if (Stop[x][y])
7795     return;
7796
7797   if (MovDelay[x][y] == 0)
7798     GfxAction[x][y] = ACTION_DEFAULT;
7799
7800   if (CAN_FALL(element) && y < lev_fieldy - 1)
7801   {
7802     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7803         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7804       if (JustBeingPushed(x, y))
7805         return;
7806
7807     if (element == EL_QUICKSAND_FULL)
7808     {
7809       if (IS_FREE(x, y + 1))
7810       {
7811         InitMovingField(x, y, MV_DOWN);
7812         started_moving = TRUE;
7813
7814         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7815 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7816         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7817           Store[x][y] = EL_ROCK;
7818 #else
7819         Store[x][y] = EL_ROCK;
7820 #endif
7821
7822         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7823       }
7824       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7825       {
7826         if (!MovDelay[x][y])
7827         {
7828           MovDelay[x][y] = TILEY + 1;
7829
7830           ResetGfxAnimation(x, y);
7831           ResetGfxAnimation(x, y + 1);
7832         }
7833
7834         if (MovDelay[x][y])
7835         {
7836           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7837           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7838
7839           MovDelay[x][y]--;
7840           if (MovDelay[x][y])
7841             return;
7842         }
7843
7844         Tile[x][y] = EL_QUICKSAND_EMPTY;
7845         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7846         Store[x][y + 1] = Store[x][y];
7847         Store[x][y] = 0;
7848
7849         PlayLevelSoundAction(x, y, ACTION_FILLING);
7850       }
7851       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7852       {
7853         if (!MovDelay[x][y])
7854         {
7855           MovDelay[x][y] = TILEY + 1;
7856
7857           ResetGfxAnimation(x, y);
7858           ResetGfxAnimation(x, y + 1);
7859         }
7860
7861         if (MovDelay[x][y])
7862         {
7863           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7864           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7865
7866           MovDelay[x][y]--;
7867           if (MovDelay[x][y])
7868             return;
7869         }
7870
7871         Tile[x][y] = EL_QUICKSAND_EMPTY;
7872         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7873         Store[x][y + 1] = Store[x][y];
7874         Store[x][y] = 0;
7875
7876         PlayLevelSoundAction(x, y, ACTION_FILLING);
7877       }
7878     }
7879     else if (element == EL_QUICKSAND_FAST_FULL)
7880     {
7881       if (IS_FREE(x, y + 1))
7882       {
7883         InitMovingField(x, y, MV_DOWN);
7884         started_moving = TRUE;
7885
7886         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7887 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7888         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7889           Store[x][y] = EL_ROCK;
7890 #else
7891         Store[x][y] = EL_ROCK;
7892 #endif
7893
7894         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7895       }
7896       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7897       {
7898         if (!MovDelay[x][y])
7899         {
7900           MovDelay[x][y] = TILEY + 1;
7901
7902           ResetGfxAnimation(x, y);
7903           ResetGfxAnimation(x, y + 1);
7904         }
7905
7906         if (MovDelay[x][y])
7907         {
7908           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7909           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7910
7911           MovDelay[x][y]--;
7912           if (MovDelay[x][y])
7913             return;
7914         }
7915
7916         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7917         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7918         Store[x][y + 1] = Store[x][y];
7919         Store[x][y] = 0;
7920
7921         PlayLevelSoundAction(x, y, ACTION_FILLING);
7922       }
7923       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7924       {
7925         if (!MovDelay[x][y])
7926         {
7927           MovDelay[x][y] = TILEY + 1;
7928
7929           ResetGfxAnimation(x, y);
7930           ResetGfxAnimation(x, y + 1);
7931         }
7932
7933         if (MovDelay[x][y])
7934         {
7935           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7936           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7937
7938           MovDelay[x][y]--;
7939           if (MovDelay[x][y])
7940             return;
7941         }
7942
7943         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7944         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7945         Store[x][y + 1] = Store[x][y];
7946         Store[x][y] = 0;
7947
7948         PlayLevelSoundAction(x, y, ACTION_FILLING);
7949       }
7950     }
7951     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7952              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7953     {
7954       InitMovingField(x, y, MV_DOWN);
7955       started_moving = TRUE;
7956
7957       Tile[x][y] = EL_QUICKSAND_FILLING;
7958       Store[x][y] = element;
7959
7960       PlayLevelSoundAction(x, y, ACTION_FILLING);
7961     }
7962     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7963              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7964     {
7965       InitMovingField(x, y, MV_DOWN);
7966       started_moving = TRUE;
7967
7968       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7969       Store[x][y] = element;
7970
7971       PlayLevelSoundAction(x, y, ACTION_FILLING);
7972     }
7973     else if (element == EL_MAGIC_WALL_FULL)
7974     {
7975       if (IS_FREE(x, y + 1))
7976       {
7977         InitMovingField(x, y, MV_DOWN);
7978         started_moving = TRUE;
7979
7980         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7981         Store[x][y] = EL_CHANGED(Store[x][y]);
7982       }
7983       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7984       {
7985         if (!MovDelay[x][y])
7986           MovDelay[x][y] = TILEY / 4 + 1;
7987
7988         if (MovDelay[x][y])
7989         {
7990           MovDelay[x][y]--;
7991           if (MovDelay[x][y])
7992             return;
7993         }
7994
7995         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7996         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7997         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7998         Store[x][y] = 0;
7999       }
8000     }
8001     else if (element == EL_BD_MAGIC_WALL_FULL)
8002     {
8003       if (IS_FREE(x, y + 1))
8004       {
8005         InitMovingField(x, y, MV_DOWN);
8006         started_moving = TRUE;
8007
8008         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8009         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8010       }
8011       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8012       {
8013         if (!MovDelay[x][y])
8014           MovDelay[x][y] = TILEY / 4 + 1;
8015
8016         if (MovDelay[x][y])
8017         {
8018           MovDelay[x][y]--;
8019           if (MovDelay[x][y])
8020             return;
8021         }
8022
8023         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8024         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8025         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8026         Store[x][y] = 0;
8027       }
8028     }
8029     else if (element == EL_DC_MAGIC_WALL_FULL)
8030     {
8031       if (IS_FREE(x, y + 1))
8032       {
8033         InitMovingField(x, y, MV_DOWN);
8034         started_moving = TRUE;
8035
8036         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8037         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8038       }
8039       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8040       {
8041         if (!MovDelay[x][y])
8042           MovDelay[x][y] = TILEY / 4 + 1;
8043
8044         if (MovDelay[x][y])
8045         {
8046           MovDelay[x][y]--;
8047           if (MovDelay[x][y])
8048             return;
8049         }
8050
8051         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8052         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8053         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8054         Store[x][y] = 0;
8055       }
8056     }
8057     else if ((CAN_PASS_MAGIC_WALL(element) &&
8058               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8059                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8060              (CAN_PASS_DC_MAGIC_WALL(element) &&
8061               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8062
8063     {
8064       InitMovingField(x, y, MV_DOWN);
8065       started_moving = TRUE;
8066
8067       Tile[x][y] =
8068         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8069          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8070          EL_DC_MAGIC_WALL_FILLING);
8071       Store[x][y] = element;
8072     }
8073     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8074     {
8075       SplashAcid(x, y + 1);
8076
8077       InitMovingField(x, y, MV_DOWN);
8078       started_moving = TRUE;
8079
8080       Store[x][y] = EL_ACID;
8081     }
8082     else if (
8083              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8084               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8085              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8086               CAN_FALL(element) && WasJustFalling[x][y] &&
8087               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8088
8089              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8090               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8091               (Tile[x][y + 1] == EL_BLOCKED)))
8092     {
8093       /* this is needed for a special case not covered by calling "Impact()"
8094          from "ContinueMoving()": if an element moves to a tile directly below
8095          another element which was just falling on that tile (which was empty
8096          in the previous frame), the falling element above would just stop
8097          instead of smashing the element below (in previous version, the above
8098          element was just checked for "moving" instead of "falling", resulting
8099          in incorrect smashes caused by horizontal movement of the above
8100          element; also, the case of the player being the element to smash was
8101          simply not covered here... :-/ ) */
8102
8103       CheckCollision[x][y] = 0;
8104       CheckImpact[x][y] = 0;
8105
8106       Impact(x, y);
8107     }
8108     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8109     {
8110       if (MovDir[x][y] == MV_NONE)
8111       {
8112         InitMovingField(x, y, MV_DOWN);
8113         started_moving = TRUE;
8114       }
8115     }
8116     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8117     {
8118       if (WasJustFalling[x][y]) // prevent animation from being restarted
8119         MovDir[x][y] = MV_DOWN;
8120
8121       InitMovingField(x, y, MV_DOWN);
8122       started_moving = TRUE;
8123     }
8124     else if (element == EL_AMOEBA_DROP)
8125     {
8126       Tile[x][y] = EL_AMOEBA_GROWING;
8127       Store[x][y] = EL_AMOEBA_WET;
8128     }
8129     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8130               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8131              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8132              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8133     {
8134       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8135                                 (IS_FREE(x - 1, y + 1) ||
8136                                  Tile[x - 1][y + 1] == EL_ACID));
8137       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8138                                 (IS_FREE(x + 1, y + 1) ||
8139                                  Tile[x + 1][y + 1] == EL_ACID));
8140       boolean can_fall_any  = (can_fall_left || can_fall_right);
8141       boolean can_fall_both = (can_fall_left && can_fall_right);
8142       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8143
8144       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8145       {
8146         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8147           can_fall_right = FALSE;
8148         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8149           can_fall_left = FALSE;
8150         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8151           can_fall_right = FALSE;
8152         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8153           can_fall_left = FALSE;
8154
8155         can_fall_any  = (can_fall_left || can_fall_right);
8156         can_fall_both = FALSE;
8157       }
8158
8159       if (can_fall_both)
8160       {
8161         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8162           can_fall_right = FALSE;       // slip down on left side
8163         else
8164           can_fall_left = !(can_fall_right = RND(2));
8165
8166         can_fall_both = FALSE;
8167       }
8168
8169       if (can_fall_any)
8170       {
8171         // if not determined otherwise, prefer left side for slipping down
8172         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8173         started_moving = TRUE;
8174       }
8175     }
8176     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8177     {
8178       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8179       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8180       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8181       int belt_dir = game.belt_dir[belt_nr];
8182
8183       if ((belt_dir == MV_LEFT  && left_is_free) ||
8184           (belt_dir == MV_RIGHT && right_is_free))
8185       {
8186         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8187
8188         InitMovingField(x, y, belt_dir);
8189         started_moving = TRUE;
8190
8191         Pushed[x][y] = TRUE;
8192         Pushed[nextx][y] = TRUE;
8193
8194         GfxAction[x][y] = ACTION_DEFAULT;
8195       }
8196       else
8197       {
8198         MovDir[x][y] = 0;       // if element was moving, stop it
8199       }
8200     }
8201   }
8202
8203   // not "else if" because of elements that can fall and move (EL_SPRING)
8204   if (CAN_MOVE(element) && !started_moving)
8205   {
8206     int move_pattern = element_info[element].move_pattern;
8207     int newx, newy;
8208
8209     Moving2Blocked(x, y, &newx, &newy);
8210
8211     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8212       return;
8213
8214     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8215         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8216     {
8217       WasJustMoving[x][y] = 0;
8218       CheckCollision[x][y] = 0;
8219
8220       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8221
8222       if (Tile[x][y] != element)        // element has changed
8223         return;
8224     }
8225
8226     if (!MovDelay[x][y])        // start new movement phase
8227     {
8228       // all objects that can change their move direction after each step
8229       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8230
8231       if (element != EL_YAMYAM &&
8232           element != EL_DARK_YAMYAM &&
8233           element != EL_PACMAN &&
8234           !(move_pattern & MV_ANY_DIRECTION) &&
8235           move_pattern != MV_TURNING_LEFT &&
8236           move_pattern != MV_TURNING_RIGHT &&
8237           move_pattern != MV_TURNING_LEFT_RIGHT &&
8238           move_pattern != MV_TURNING_RIGHT_LEFT &&
8239           move_pattern != MV_TURNING_RANDOM)
8240       {
8241         TurnRound(x, y);
8242
8243         if (MovDelay[x][y] && (element == EL_BUG ||
8244                                element == EL_SPACESHIP ||
8245                                element == EL_SP_SNIKSNAK ||
8246                                element == EL_SP_ELECTRON ||
8247                                element == EL_MOLE))
8248           TEST_DrawLevelField(x, y);
8249       }
8250     }
8251
8252     if (MovDelay[x][y])         // wait some time before next movement
8253     {
8254       MovDelay[x][y]--;
8255
8256       if (element == EL_ROBOT ||
8257           element == EL_YAMYAM ||
8258           element == EL_DARK_YAMYAM)
8259       {
8260         DrawLevelElementAnimationIfNeeded(x, y, element);
8261         PlayLevelSoundAction(x, y, ACTION_WAITING);
8262       }
8263       else if (element == EL_SP_ELECTRON)
8264         DrawLevelElementAnimationIfNeeded(x, y, element);
8265       else if (element == EL_DRAGON)
8266       {
8267         int i;
8268         int dir = MovDir[x][y];
8269         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8270         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8271         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8272                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8273                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8274                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8275         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8276
8277         GfxAction[x][y] = ACTION_ATTACKING;
8278
8279         if (IS_PLAYER(x, y))
8280           DrawPlayerField(x, y);
8281         else
8282           TEST_DrawLevelField(x, y);
8283
8284         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8285
8286         for (i = 1; i <= 3; i++)
8287         {
8288           int xx = x + i * dx;
8289           int yy = y + i * dy;
8290           int sx = SCREENX(xx);
8291           int sy = SCREENY(yy);
8292           int flame_graphic = graphic + (i - 1);
8293
8294           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8295             break;
8296
8297           if (MovDelay[x][y])
8298           {
8299             int flamed = MovingOrBlocked2Element(xx, yy);
8300
8301             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8302               Bang(xx, yy);
8303             else
8304               RemoveMovingField(xx, yy);
8305
8306             ChangeDelay[xx][yy] = 0;
8307
8308             Tile[xx][yy] = EL_FLAMES;
8309
8310             if (IN_SCR_FIELD(sx, sy))
8311             {
8312               TEST_DrawLevelFieldCrumbled(xx, yy);
8313               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8314             }
8315           }
8316           else
8317           {
8318             if (Tile[xx][yy] == EL_FLAMES)
8319               Tile[xx][yy] = EL_EMPTY;
8320             TEST_DrawLevelField(xx, yy);
8321           }
8322         }
8323       }
8324
8325       if (MovDelay[x][y])       // element still has to wait some time
8326       {
8327         PlayLevelSoundAction(x, y, ACTION_WAITING);
8328
8329         return;
8330       }
8331     }
8332
8333     // now make next step
8334
8335     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8336
8337     if (DONT_COLLIDE_WITH(element) &&
8338         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8339         !PLAYER_ENEMY_PROTECTED(newx, newy))
8340     {
8341       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8342
8343       return;
8344     }
8345
8346     else if (CAN_MOVE_INTO_ACID(element) &&
8347              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8348              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8349              (MovDir[x][y] == MV_DOWN ||
8350               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8351     {
8352       SplashAcid(newx, newy);
8353       Store[x][y] = EL_ACID;
8354     }
8355     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8356     {
8357       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8358           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8359           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8360           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8361       {
8362         RemoveField(x, y);
8363         TEST_DrawLevelField(x, y);
8364
8365         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8366         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8367           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8368
8369         game.friends_still_needed--;
8370         if (!game.friends_still_needed &&
8371             !game.GameOver &&
8372             game.all_players_gone)
8373           LevelSolved();
8374
8375         return;
8376       }
8377       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8378       {
8379         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8380           TEST_DrawLevelField(newx, newy);
8381         else
8382           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8383       }
8384       else if (!IS_FREE(newx, newy))
8385       {
8386         GfxAction[x][y] = ACTION_WAITING;
8387
8388         if (IS_PLAYER(x, y))
8389           DrawPlayerField(x, y);
8390         else
8391           TEST_DrawLevelField(x, y);
8392
8393         return;
8394       }
8395     }
8396     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8397     {
8398       if (IS_FOOD_PIG(Tile[newx][newy]))
8399       {
8400         if (IS_MOVING(newx, newy))
8401           RemoveMovingField(newx, newy);
8402         else
8403         {
8404           Tile[newx][newy] = EL_EMPTY;
8405           TEST_DrawLevelField(newx, newy);
8406         }
8407
8408         PlayLevelSound(x, y, SND_PIG_DIGGING);
8409       }
8410       else if (!IS_FREE(newx, newy))
8411       {
8412         if (IS_PLAYER(x, y))
8413           DrawPlayerField(x, y);
8414         else
8415           TEST_DrawLevelField(x, y);
8416
8417         return;
8418       }
8419     }
8420     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8421     {
8422       if (Store[x][y] != EL_EMPTY)
8423       {
8424         boolean can_clone = FALSE;
8425         int xx, yy;
8426
8427         // check if element to clone is still there
8428         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8429         {
8430           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8431           {
8432             can_clone = TRUE;
8433
8434             break;
8435           }
8436         }
8437
8438         // cannot clone or target field not free anymore -- do not clone
8439         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8440           Store[x][y] = EL_EMPTY;
8441       }
8442
8443       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8444       {
8445         if (IS_MV_DIAGONAL(MovDir[x][y]))
8446         {
8447           int diagonal_move_dir = MovDir[x][y];
8448           int stored = Store[x][y];
8449           int change_delay = 8;
8450           int graphic;
8451
8452           // android is moving diagonally
8453
8454           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8455
8456           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8457           GfxElement[x][y] = EL_EMC_ANDROID;
8458           GfxAction[x][y] = ACTION_SHRINKING;
8459           GfxDir[x][y] = diagonal_move_dir;
8460           ChangeDelay[x][y] = change_delay;
8461
8462           if (Store[x][y] == EL_EMPTY)
8463             Store[x][y] = GfxElementEmpty[x][y];
8464
8465           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8466                                    GfxDir[x][y]);
8467
8468           DrawLevelGraphicAnimation(x, y, graphic);
8469           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8470
8471           if (Tile[newx][newy] == EL_ACID)
8472           {
8473             SplashAcid(newx, newy);
8474
8475             return;
8476           }
8477
8478           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8479
8480           Store[newx][newy] = EL_EMC_ANDROID;
8481           GfxElement[newx][newy] = EL_EMC_ANDROID;
8482           GfxAction[newx][newy] = ACTION_GROWING;
8483           GfxDir[newx][newy] = diagonal_move_dir;
8484           ChangeDelay[newx][newy] = change_delay;
8485
8486           graphic = el_act_dir2img(GfxElement[newx][newy],
8487                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8488
8489           DrawLevelGraphicAnimation(newx, newy, graphic);
8490           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8491
8492           return;
8493         }
8494         else
8495         {
8496           Tile[newx][newy] = EL_EMPTY;
8497           TEST_DrawLevelField(newx, newy);
8498
8499           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8500         }
8501       }
8502       else if (!IS_FREE(newx, newy))
8503       {
8504         return;
8505       }
8506     }
8507     else if (IS_CUSTOM_ELEMENT(element) &&
8508              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8509     {
8510       if (!DigFieldByCE(newx, newy, element))
8511         return;
8512
8513       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8514       {
8515         RunnerVisit[x][y] = FrameCounter;
8516         PlayerVisit[x][y] /= 8;         // expire player visit path
8517       }
8518     }
8519     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8520     {
8521       if (!IS_FREE(newx, newy))
8522       {
8523         if (IS_PLAYER(x, y))
8524           DrawPlayerField(x, y);
8525         else
8526           TEST_DrawLevelField(x, y);
8527
8528         return;
8529       }
8530       else
8531       {
8532         boolean wanna_flame = !RND(10);
8533         int dx = newx - x, dy = newy - y;
8534         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8535         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8536         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8537                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8538         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8539                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8540
8541         if ((wanna_flame ||
8542              IS_CLASSIC_ENEMY(element1) ||
8543              IS_CLASSIC_ENEMY(element2)) &&
8544             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8545             element1 != EL_FLAMES && element2 != EL_FLAMES)
8546         {
8547           ResetGfxAnimation(x, y);
8548           GfxAction[x][y] = ACTION_ATTACKING;
8549
8550           if (IS_PLAYER(x, y))
8551             DrawPlayerField(x, y);
8552           else
8553             TEST_DrawLevelField(x, y);
8554
8555           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8556
8557           MovDelay[x][y] = 50;
8558
8559           Tile[newx][newy] = EL_FLAMES;
8560           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8561             Tile[newx1][newy1] = EL_FLAMES;
8562           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8563             Tile[newx2][newy2] = EL_FLAMES;
8564
8565           return;
8566         }
8567       }
8568     }
8569     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8570              Tile[newx][newy] == EL_DIAMOND)
8571     {
8572       if (IS_MOVING(newx, newy))
8573         RemoveMovingField(newx, newy);
8574       else
8575       {
8576         Tile[newx][newy] = EL_EMPTY;
8577         TEST_DrawLevelField(newx, newy);
8578       }
8579
8580       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8581     }
8582     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8583              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8584     {
8585       if (AmoebaNr[newx][newy])
8586       {
8587         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8588         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8589             Tile[newx][newy] == EL_BD_AMOEBA)
8590           AmoebaCnt[AmoebaNr[newx][newy]]--;
8591       }
8592
8593       if (IS_MOVING(newx, newy))
8594       {
8595         RemoveMovingField(newx, newy);
8596       }
8597       else
8598       {
8599         Tile[newx][newy] = EL_EMPTY;
8600         TEST_DrawLevelField(newx, newy);
8601       }
8602
8603       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8604     }
8605     else if ((element == EL_PACMAN || element == EL_MOLE)
8606              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8607     {
8608       if (AmoebaNr[newx][newy])
8609       {
8610         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8611         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8612             Tile[newx][newy] == EL_BD_AMOEBA)
8613           AmoebaCnt[AmoebaNr[newx][newy]]--;
8614       }
8615
8616       if (element == EL_MOLE)
8617       {
8618         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8619         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8620
8621         ResetGfxAnimation(x, y);
8622         GfxAction[x][y] = ACTION_DIGGING;
8623         TEST_DrawLevelField(x, y);
8624
8625         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8626
8627         return;                         // wait for shrinking amoeba
8628       }
8629       else      // element == EL_PACMAN
8630       {
8631         Tile[newx][newy] = EL_EMPTY;
8632         TEST_DrawLevelField(newx, newy);
8633         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8634       }
8635     }
8636     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8637              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8638               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8639     {
8640       // wait for shrinking amoeba to completely disappear
8641       return;
8642     }
8643     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8644     {
8645       // object was running against a wall
8646
8647       TurnRound(x, y);
8648
8649       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8650         DrawLevelElementAnimation(x, y, element);
8651
8652       if (DONT_TOUCH(element))
8653         TestIfBadThingTouchesPlayer(x, y);
8654
8655       return;
8656     }
8657
8658     InitMovingField(x, y, MovDir[x][y]);
8659
8660     PlayLevelSoundAction(x, y, ACTION_MOVING);
8661   }
8662
8663   if (MovDir[x][y])
8664     ContinueMoving(x, y);
8665 }
8666
8667 void ContinueMoving(int x, int y)
8668 {
8669   int element = Tile[x][y];
8670   struct ElementInfo *ei = &element_info[element];
8671   int direction = MovDir[x][y];
8672   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8673   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8674   int newx = x + dx, newy = y + dy;
8675   int stored = Store[x][y];
8676   int stored_new = Store[newx][newy];
8677   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8678   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8679   boolean last_line = (newy == lev_fieldy - 1);
8680   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8681
8682   if (pushed_by_player)         // special case: moving object pushed by player
8683   {
8684     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8685   }
8686   else if (use_step_delay)      // special case: moving object has step delay
8687   {
8688     if (!MovDelay[x][y])
8689       MovPos[x][y] += getElementMoveStepsize(x, y);
8690
8691     if (MovDelay[x][y])
8692       MovDelay[x][y]--;
8693     else
8694       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8695
8696     if (MovDelay[x][y])
8697     {
8698       TEST_DrawLevelField(x, y);
8699
8700       return;   // element is still waiting
8701     }
8702   }
8703   else                          // normal case: generically moving object
8704   {
8705     MovPos[x][y] += getElementMoveStepsize(x, y);
8706   }
8707
8708   if (ABS(MovPos[x][y]) < TILEX)
8709   {
8710     TEST_DrawLevelField(x, y);
8711
8712     return;     // element is still moving
8713   }
8714
8715   // element reached destination field
8716
8717   Tile[x][y] = EL_EMPTY;
8718   Tile[newx][newy] = element;
8719   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8720
8721   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8722   {
8723     element = Tile[newx][newy] = EL_ACID;
8724   }
8725   else if (element == EL_MOLE)
8726   {
8727     Tile[x][y] = EL_SAND;
8728
8729     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8730   }
8731   else if (element == EL_QUICKSAND_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     Store[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_QUICKSAND_EMPTYING)
8737   {
8738     Tile[x][y] = get_next_element(element);
8739     element = Tile[newx][newy] = Store[x][y];
8740   }
8741   else if (element == EL_QUICKSAND_FAST_FILLING)
8742   {
8743     element = Tile[newx][newy] = get_next_element(element);
8744     Store[newx][newy] = Store[x][y];
8745   }
8746   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8747   {
8748     Tile[x][y] = get_next_element(element);
8749     element = Tile[newx][newy] = Store[x][y];
8750   }
8751   else if (element == EL_MAGIC_WALL_FILLING)
8752   {
8753     element = Tile[newx][newy] = get_next_element(element);
8754     if (!game.magic_wall_active)
8755       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8756     Store[newx][newy] = Store[x][y];
8757   }
8758   else if (element == EL_MAGIC_WALL_EMPTYING)
8759   {
8760     Tile[x][y] = get_next_element(element);
8761     if (!game.magic_wall_active)
8762       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8763     element = Tile[newx][newy] = Store[x][y];
8764
8765     InitField(newx, newy, FALSE);
8766   }
8767   else if (element == EL_BD_MAGIC_WALL_FILLING)
8768   {
8769     element = Tile[newx][newy] = get_next_element(element);
8770     if (!game.magic_wall_active)
8771       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8772     Store[newx][newy] = Store[x][y];
8773   }
8774   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8775   {
8776     Tile[x][y] = get_next_element(element);
8777     if (!game.magic_wall_active)
8778       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8779     element = Tile[newx][newy] = Store[x][y];
8780
8781     InitField(newx, newy, FALSE);
8782   }
8783   else if (element == EL_DC_MAGIC_WALL_FILLING)
8784   {
8785     element = Tile[newx][newy] = get_next_element(element);
8786     if (!game.magic_wall_active)
8787       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8788     Store[newx][newy] = Store[x][y];
8789   }
8790   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8791   {
8792     Tile[x][y] = get_next_element(element);
8793     if (!game.magic_wall_active)
8794       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8795     element = Tile[newx][newy] = Store[x][y];
8796
8797     InitField(newx, newy, FALSE);
8798   }
8799   else if (element == EL_AMOEBA_DROPPING)
8800   {
8801     Tile[x][y] = get_next_element(element);
8802     element = Tile[newx][newy] = Store[x][y];
8803   }
8804   else if (element == EL_SOKOBAN_OBJECT)
8805   {
8806     if (Back[x][y])
8807       Tile[x][y] = Back[x][y];
8808
8809     if (Back[newx][newy])
8810       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8811
8812     Back[x][y] = Back[newx][newy] = 0;
8813   }
8814
8815   Store[x][y] = EL_EMPTY;
8816   MovPos[x][y] = 0;
8817   MovDir[x][y] = 0;
8818   MovDelay[x][y] = 0;
8819
8820   MovDelay[newx][newy] = 0;
8821
8822   if (CAN_CHANGE_OR_HAS_ACTION(element))
8823   {
8824     // copy element change control values to new field
8825     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8826     ChangePage[newx][newy]  = ChangePage[x][y];
8827     ChangeCount[newx][newy] = ChangeCount[x][y];
8828     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8829   }
8830
8831   CustomValue[newx][newy] = CustomValue[x][y];
8832
8833   ChangeDelay[x][y] = 0;
8834   ChangePage[x][y] = -1;
8835   ChangeCount[x][y] = 0;
8836   ChangeEvent[x][y] = -1;
8837
8838   CustomValue[x][y] = 0;
8839
8840   // copy animation control values to new field
8841   GfxFrame[newx][newy]  = GfxFrame[x][y];
8842   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8843   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8844   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8845
8846   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8847
8848   // some elements can leave other elements behind after moving
8849   if (ei->move_leave_element != EL_EMPTY &&
8850       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8851       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8852   {
8853     int move_leave_element = ei->move_leave_element;
8854
8855     // this makes it possible to leave the removed element again
8856     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8857       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8858
8859     Tile[x][y] = move_leave_element;
8860
8861     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8862       MovDir[x][y] = direction;
8863
8864     InitField(x, y, FALSE);
8865
8866     if (GFX_CRUMBLED(Tile[x][y]))
8867       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8868
8869     if (IS_PLAYER_ELEMENT(move_leave_element))
8870       RelocatePlayer(x, y, move_leave_element);
8871   }
8872
8873   // do this after checking for left-behind element
8874   ResetGfxAnimation(x, y);      // reset animation values for old field
8875
8876   if (!CAN_MOVE(element) ||
8877       (CAN_FALL(element) && direction == MV_DOWN &&
8878        (element == EL_SPRING ||
8879         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8880         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8881     GfxDir[x][y] = MovDir[newx][newy] = 0;
8882
8883   TEST_DrawLevelField(x, y);
8884   TEST_DrawLevelField(newx, newy);
8885
8886   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8887
8888   // prevent pushed element from moving on in pushed direction
8889   if (pushed_by_player && CAN_MOVE(element) &&
8890       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8891       !(element_info[element].move_pattern & direction))
8892     TurnRound(newx, newy);
8893
8894   // prevent elements on conveyor belt from moving on in last direction
8895   if (pushed_by_conveyor && CAN_FALL(element) &&
8896       direction & MV_HORIZONTAL)
8897     MovDir[newx][newy] = 0;
8898
8899   if (!pushed_by_player)
8900   {
8901     int nextx = newx + dx, nexty = newy + dy;
8902     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8903
8904     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8905
8906     if (CAN_FALL(element) && direction == MV_DOWN)
8907       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8908
8909     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8910       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8911
8912     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8913       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8914   }
8915
8916   if (DONT_TOUCH(element))      // object may be nasty to player or others
8917   {
8918     TestIfBadThingTouchesPlayer(newx, newy);
8919     TestIfBadThingTouchesFriend(newx, newy);
8920
8921     if (!IS_CUSTOM_ELEMENT(element))
8922       TestIfBadThingTouchesOtherBadThing(newx, newy);
8923   }
8924   else if (element == EL_PENGUIN)
8925     TestIfFriendTouchesBadThing(newx, newy);
8926
8927   if (DONT_GET_HIT_BY(element))
8928   {
8929     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8930   }
8931
8932   // give the player one last chance (one more frame) to move away
8933   if (CAN_FALL(element) && direction == MV_DOWN &&
8934       (last_line || (!IS_FREE(x, newy + 1) &&
8935                      (!IS_PLAYER(x, newy + 1) ||
8936                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8937     Impact(x, newy);
8938
8939   if (pushed_by_player && !game.use_change_when_pushing_bug)
8940   {
8941     int push_side = MV_DIR_OPPOSITE(direction);
8942     struct PlayerInfo *player = PLAYERINFO(x, y);
8943
8944     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8945                                player->index_bit, push_side);
8946     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8947                                         player->index_bit, push_side);
8948   }
8949
8950   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8951     MovDelay[newx][newy] = 1;
8952
8953   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8954
8955   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8956   TestIfElementHitsCustomElement(newx, newy, direction);
8957   TestIfPlayerTouchesCustomElement(newx, newy);
8958   TestIfElementTouchesCustomElement(newx, newy);
8959
8960   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8961       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8962     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8963                              MV_DIR_OPPOSITE(direction));
8964 }
8965
8966 int AmoebaNeighbourNr(int ax, int ay)
8967 {
8968   int i;
8969   int element = Tile[ax][ay];
8970   int group_nr = 0;
8971   static int xy[4][2] =
8972   {
8973     { 0, -1 },
8974     { -1, 0 },
8975     { +1, 0 },
8976     { 0, +1 }
8977   };
8978
8979   for (i = 0; i < NUM_DIRECTIONS; i++)
8980   {
8981     int x = ax + xy[i][0];
8982     int y = ay + xy[i][1];
8983
8984     if (!IN_LEV_FIELD(x, y))
8985       continue;
8986
8987     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8988       group_nr = AmoebaNr[x][y];
8989   }
8990
8991   return group_nr;
8992 }
8993
8994 static void AmoebaMerge(int ax, int ay)
8995 {
8996   int i, x, y, xx, yy;
8997   int new_group_nr = AmoebaNr[ax][ay];
8998   static int xy[4][2] =
8999   {
9000     { 0, -1 },
9001     { -1, 0 },
9002     { +1, 0 },
9003     { 0, +1 }
9004   };
9005
9006   if (new_group_nr == 0)
9007     return;
9008
9009   for (i = 0; i < NUM_DIRECTIONS; i++)
9010   {
9011     x = ax + xy[i][0];
9012     y = ay + xy[i][1];
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     static int xy[4][2] =
9076     {
9077       { 0, -1 },
9078       { -1, 0 },
9079       { +1, 0 },
9080       { 0, +1 }
9081     };
9082
9083     for (i = 0; i < NUM_DIRECTIONS; i++)
9084     {
9085       x = ax + xy[i][0];
9086       y = ay + xy[i][1];
9087
9088       if (!IN_LEV_FIELD(x, y))
9089         continue;
9090
9091       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9092       {
9093         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9094                               SND_AMOEBA_TURNING_TO_GEM :
9095                               SND_AMOEBA_TURNING_TO_ROCK));
9096         Bang(x, y);
9097       }
9098     }
9099   }
9100 }
9101
9102 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9103 {
9104   int x, y;
9105   int group_nr = AmoebaNr[ax][ay];
9106   boolean done = FALSE;
9107
9108 #ifdef DEBUG
9109   if (group_nr == 0)
9110   {
9111     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9112     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9113
9114     return;
9115   }
9116 #endif
9117
9118   SCAN_PLAYFIELD(x, y)
9119   {
9120     if (AmoebaNr[x][y] == group_nr &&
9121         (Tile[x][y] == EL_AMOEBA_DEAD ||
9122          Tile[x][y] == EL_BD_AMOEBA ||
9123          Tile[x][y] == EL_AMOEBA_GROWING))
9124     {
9125       AmoebaNr[x][y] = 0;
9126       Tile[x][y] = new_element;
9127       InitField(x, y, FALSE);
9128       TEST_DrawLevelField(x, y);
9129       done = TRUE;
9130     }
9131   }
9132
9133   if (done)
9134     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9135                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9136                             SND_BD_AMOEBA_TURNING_TO_GEM));
9137 }
9138
9139 static void AmoebaGrowing(int x, int y)
9140 {
9141   static unsigned int sound_delay = 0;
9142   static unsigned int sound_delay_value = 0;
9143
9144   if (!MovDelay[x][y])          // start new growing cycle
9145   {
9146     MovDelay[x][y] = 7;
9147
9148     if (DelayReached(&sound_delay, sound_delay_value))
9149     {
9150       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9151       sound_delay_value = 30;
9152     }
9153   }
9154
9155   if (MovDelay[x][y])           // wait some time before growing bigger
9156   {
9157     MovDelay[x][y]--;
9158     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9159     {
9160       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9161                                            6 - MovDelay[x][y]);
9162
9163       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9164     }
9165
9166     if (!MovDelay[x][y])
9167     {
9168       Tile[x][y] = Store[x][y];
9169       Store[x][y] = 0;
9170       TEST_DrawLevelField(x, y);
9171     }
9172   }
9173 }
9174
9175 static void AmoebaShrinking(int x, int y)
9176 {
9177   static unsigned int sound_delay = 0;
9178   static unsigned int sound_delay_value = 0;
9179
9180   if (!MovDelay[x][y])          // start new shrinking cycle
9181   {
9182     MovDelay[x][y] = 7;
9183
9184     if (DelayReached(&sound_delay, sound_delay_value))
9185       sound_delay_value = 30;
9186   }
9187
9188   if (MovDelay[x][y])           // wait some time before shrinking
9189   {
9190     MovDelay[x][y]--;
9191     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9192     {
9193       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9194                                            6 - MovDelay[x][y]);
9195
9196       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9197     }
9198
9199     if (!MovDelay[x][y])
9200     {
9201       Tile[x][y] = EL_EMPTY;
9202       TEST_DrawLevelField(x, y);
9203
9204       // don't let mole enter this field in this cycle;
9205       // (give priority to objects falling to this field from above)
9206       Stop[x][y] = TRUE;
9207     }
9208   }
9209 }
9210
9211 static void AmoebaReproduce(int ax, int ay)
9212 {
9213   int i;
9214   int element = Tile[ax][ay];
9215   int graphic = el2img(element);
9216   int newax = ax, neway = ay;
9217   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9218   static int xy[4][2] =
9219   {
9220     { 0, -1 },
9221     { -1, 0 },
9222     { +1, 0 },
9223     { 0, +1 }
9224   };
9225
9226   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9227   {
9228     Tile[ax][ay] = EL_AMOEBA_DEAD;
9229     TEST_DrawLevelField(ax, ay);
9230     return;
9231   }
9232
9233   if (IS_ANIMATED(graphic))
9234     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9235
9236   if (!MovDelay[ax][ay])        // start making new amoeba field
9237     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9238
9239   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9240   {
9241     MovDelay[ax][ay]--;
9242     if (MovDelay[ax][ay])
9243       return;
9244   }
9245
9246   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9247   {
9248     int start = RND(4);
9249     int x = ax + xy[start][0];
9250     int y = ay + xy[start][1];
9251
9252     if (!IN_LEV_FIELD(x, y))
9253       return;
9254
9255     if (IS_FREE(x, y) ||
9256         CAN_GROW_INTO(Tile[x][y]) ||
9257         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9258         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9259     {
9260       newax = x;
9261       neway = y;
9262     }
9263
9264     if (newax == ax && neway == ay)
9265       return;
9266   }
9267   else                          // normal or "filled" (BD style) amoeba
9268   {
9269     int start = RND(4);
9270     boolean waiting_for_player = FALSE;
9271
9272     for (i = 0; i < NUM_DIRECTIONS; i++)
9273     {
9274       int j = (start + i) % 4;
9275       int x = ax + xy[j][0];
9276       int y = ay + xy[j][1];
9277
9278       if (!IN_LEV_FIELD(x, y))
9279         continue;
9280
9281       if (IS_FREE(x, y) ||
9282           CAN_GROW_INTO(Tile[x][y]) ||
9283           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9284           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9285       {
9286         newax = x;
9287         neway = y;
9288         break;
9289       }
9290       else if (IS_PLAYER(x, y))
9291         waiting_for_player = TRUE;
9292     }
9293
9294     if (newax == ax && neway == ay)             // amoeba cannot grow
9295     {
9296       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9297       {
9298         Tile[ax][ay] = EL_AMOEBA_DEAD;
9299         TEST_DrawLevelField(ax, ay);
9300         AmoebaCnt[AmoebaNr[ax][ay]]--;
9301
9302         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9303         {
9304           if (element == EL_AMOEBA_FULL)
9305             AmoebaToDiamond(ax, ay);
9306           else if (element == EL_BD_AMOEBA)
9307             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9308         }
9309       }
9310       return;
9311     }
9312     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9313     {
9314       // amoeba gets larger by growing in some direction
9315
9316       int new_group_nr = AmoebaNr[ax][ay];
9317
9318 #ifdef DEBUG
9319   if (new_group_nr == 0)
9320   {
9321     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9322           newax, neway);
9323     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9324
9325     return;
9326   }
9327 #endif
9328
9329       AmoebaNr[newax][neway] = new_group_nr;
9330       AmoebaCnt[new_group_nr]++;
9331       AmoebaCnt2[new_group_nr]++;
9332
9333       // if amoeba touches other amoeba(s) after growing, unify them
9334       AmoebaMerge(newax, neway);
9335
9336       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9337       {
9338         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9339         return;
9340       }
9341     }
9342   }
9343
9344   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9345       (neway == lev_fieldy - 1 && newax != ax))
9346   {
9347     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9348     Store[newax][neway] = element;
9349   }
9350   else if (neway == ay || element == EL_EMC_DRIPPER)
9351   {
9352     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9353
9354     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9355   }
9356   else
9357   {
9358     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9359     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9360     Store[ax][ay] = EL_AMOEBA_DROP;
9361     ContinueMoving(ax, ay);
9362     return;
9363   }
9364
9365   TEST_DrawLevelField(newax, neway);
9366 }
9367
9368 static void Life(int ax, int ay)
9369 {
9370   int x1, y1, x2, y2;
9371   int life_time = 40;
9372   int element = Tile[ax][ay];
9373   int graphic = el2img(element);
9374   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9375                          level.biomaze);
9376   boolean changed = FALSE;
9377
9378   if (IS_ANIMATED(graphic))
9379     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380
9381   if (Stop[ax][ay])
9382     return;
9383
9384   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9385     MovDelay[ax][ay] = life_time;
9386
9387   if (MovDelay[ax][ay])         // wait some time before next cycle
9388   {
9389     MovDelay[ax][ay]--;
9390     if (MovDelay[ax][ay])
9391       return;
9392   }
9393
9394   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9395   {
9396     int xx = ax+x1, yy = ay+y1;
9397     int old_element = Tile[xx][yy];
9398     int num_neighbours = 0;
9399
9400     if (!IN_LEV_FIELD(xx, yy))
9401       continue;
9402
9403     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9404     {
9405       int x = xx+x2, y = yy+y2;
9406
9407       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9408         continue;
9409
9410       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9411       boolean is_neighbour = FALSE;
9412
9413       if (level.use_life_bugs)
9414         is_neighbour =
9415           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9416            (IS_FREE(x, y)                             &&  Stop[x][y]));
9417       else
9418         is_neighbour =
9419           (Last[x][y] == element || is_player_cell);
9420
9421       if (is_neighbour)
9422         num_neighbours++;
9423     }
9424
9425     boolean is_free = FALSE;
9426
9427     if (level.use_life_bugs)
9428       is_free = (IS_FREE(xx, yy));
9429     else
9430       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9431
9432     if (xx == ax && yy == ay)           // field in the middle
9433     {
9434       if (num_neighbours < life_parameter[0] ||
9435           num_neighbours > life_parameter[1])
9436       {
9437         Tile[xx][yy] = EL_EMPTY;
9438         if (Tile[xx][yy] != old_element)
9439           TEST_DrawLevelField(xx, yy);
9440         Stop[xx][yy] = TRUE;
9441         changed = TRUE;
9442       }
9443     }
9444     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9445     {                                   // free border field
9446       if (num_neighbours >= life_parameter[2] &&
9447           num_neighbours <= life_parameter[3])
9448       {
9449         Tile[xx][yy] = element;
9450         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9451         if (Tile[xx][yy] != old_element)
9452           TEST_DrawLevelField(xx, yy);
9453         Stop[xx][yy] = TRUE;
9454         changed = TRUE;
9455       }
9456     }
9457   }
9458
9459   if (changed)
9460     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9461                    SND_GAME_OF_LIFE_GROWING);
9462 }
9463
9464 static void InitRobotWheel(int x, int y)
9465 {
9466   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9467 }
9468
9469 static void RunRobotWheel(int x, int y)
9470 {
9471   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9472 }
9473
9474 static void StopRobotWheel(int x, int y)
9475 {
9476   if (game.robot_wheel_x == x &&
9477       game.robot_wheel_y == y)
9478   {
9479     game.robot_wheel_x = -1;
9480     game.robot_wheel_y = -1;
9481     game.robot_wheel_active = FALSE;
9482   }
9483 }
9484
9485 static void InitTimegateWheel(int x, int y)
9486 {
9487   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9488 }
9489
9490 static void RunTimegateWheel(int x, int y)
9491 {
9492   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9493 }
9494
9495 static void InitMagicBallDelay(int x, int y)
9496 {
9497   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9498 }
9499
9500 static void ActivateMagicBall(int bx, int by)
9501 {
9502   int x, y;
9503
9504   if (level.ball_random)
9505   {
9506     int pos_border = RND(8);    // select one of the eight border elements
9507     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9508     int xx = pos_content % 3;
9509     int yy = pos_content / 3;
9510
9511     x = bx - 1 + xx;
9512     y = by - 1 + yy;
9513
9514     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9515       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9516   }
9517   else
9518   {
9519     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9520     {
9521       int xx = x - bx + 1;
9522       int yy = y - by + 1;
9523
9524       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9525         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9526     }
9527   }
9528
9529   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9530 }
9531
9532 static void CheckExit(int x, int y)
9533 {
9534   if (game.gems_still_needed > 0 ||
9535       game.sokoban_fields_still_needed > 0 ||
9536       game.sokoban_objects_still_needed > 0 ||
9537       game.lights_still_needed > 0)
9538   {
9539     int element = Tile[x][y];
9540     int graphic = el2img(element);
9541
9542     if (IS_ANIMATED(graphic))
9543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544
9545     return;
9546   }
9547
9548   // do not re-open exit door closed after last player
9549   if (game.all_players_gone)
9550     return;
9551
9552   Tile[x][y] = EL_EXIT_OPENING;
9553
9554   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9555 }
9556
9557 static void CheckExitEM(int x, int y)
9558 {
9559   if (game.gems_still_needed > 0 ||
9560       game.sokoban_fields_still_needed > 0 ||
9561       game.sokoban_objects_still_needed > 0 ||
9562       game.lights_still_needed > 0)
9563   {
9564     int element = Tile[x][y];
9565     int graphic = el2img(element);
9566
9567     if (IS_ANIMATED(graphic))
9568       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9569
9570     return;
9571   }
9572
9573   // do not re-open exit door closed after last player
9574   if (game.all_players_gone)
9575     return;
9576
9577   Tile[x][y] = EL_EM_EXIT_OPENING;
9578
9579   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9580 }
9581
9582 static void CheckExitSteel(int x, int y)
9583 {
9584   if (game.gems_still_needed > 0 ||
9585       game.sokoban_fields_still_needed > 0 ||
9586       game.sokoban_objects_still_needed > 0 ||
9587       game.lights_still_needed > 0)
9588   {
9589     int element = Tile[x][y];
9590     int graphic = el2img(element);
9591
9592     if (IS_ANIMATED(graphic))
9593       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594
9595     return;
9596   }
9597
9598   // do not re-open exit door closed after last player
9599   if (game.all_players_gone)
9600     return;
9601
9602   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9605 }
9606
9607 static void CheckExitSteelEM(int x, int y)
9608 {
9609   if (game.gems_still_needed > 0 ||
9610       game.sokoban_fields_still_needed > 0 ||
9611       game.sokoban_objects_still_needed > 0 ||
9612       game.lights_still_needed > 0)
9613   {
9614     int element = Tile[x][y];
9615     int graphic = el2img(element);
9616
9617     if (IS_ANIMATED(graphic))
9618       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9619
9620     return;
9621   }
9622
9623   // do not re-open exit door closed after last player
9624   if (game.all_players_gone)
9625     return;
9626
9627   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9628
9629   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9630 }
9631
9632 static void CheckExitSP(int x, int y)
9633 {
9634   if (game.gems_still_needed > 0)
9635   {
9636     int element = Tile[x][y];
9637     int graphic = el2img(element);
9638
9639     if (IS_ANIMATED(graphic))
9640       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9641
9642     return;
9643   }
9644
9645   // do not re-open exit door closed after last player
9646   if (game.all_players_gone)
9647     return;
9648
9649   Tile[x][y] = EL_SP_EXIT_OPENING;
9650
9651   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9652 }
9653
9654 static void CloseAllOpenTimegates(void)
9655 {
9656   int x, y;
9657
9658   SCAN_PLAYFIELD(x, y)
9659   {
9660     int element = Tile[x][y];
9661
9662     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9663     {
9664       Tile[x][y] = EL_TIMEGATE_CLOSING;
9665
9666       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9667     }
9668   }
9669 }
9670
9671 static void DrawTwinkleOnField(int x, int y)
9672 {
9673   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9674     return;
9675
9676   if (Tile[x][y] == EL_BD_DIAMOND)
9677     return;
9678
9679   if (MovDelay[x][y] == 0)      // next animation frame
9680     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9681
9682   if (MovDelay[x][y] != 0)      // wait some time before next frame
9683   {
9684     MovDelay[x][y]--;
9685
9686     DrawLevelElementAnimation(x, y, Tile[x][y]);
9687
9688     if (MovDelay[x][y] != 0)
9689     {
9690       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9691                                            10 - MovDelay[x][y]);
9692
9693       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9694     }
9695   }
9696 }
9697
9698 static void MauerWaechst(int x, int y)
9699 {
9700   int delay = 6;
9701
9702   if (!MovDelay[x][y])          // next animation frame
9703     MovDelay[x][y] = 3 * delay;
9704
9705   if (MovDelay[x][y])           // wait some time before next frame
9706   {
9707     MovDelay[x][y]--;
9708
9709     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9710     {
9711       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9712       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9713
9714       DrawLevelGraphic(x, y, graphic, frame);
9715     }
9716
9717     if (!MovDelay[x][y])
9718     {
9719       if (MovDir[x][y] == MV_LEFT)
9720       {
9721         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9722           TEST_DrawLevelField(x - 1, y);
9723       }
9724       else if (MovDir[x][y] == MV_RIGHT)
9725       {
9726         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9727           TEST_DrawLevelField(x + 1, y);
9728       }
9729       else if (MovDir[x][y] == MV_UP)
9730       {
9731         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9732           TEST_DrawLevelField(x, y - 1);
9733       }
9734       else
9735       {
9736         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9737           TEST_DrawLevelField(x, y + 1);
9738       }
9739
9740       Tile[x][y] = Store[x][y];
9741       Store[x][y] = 0;
9742       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9743       TEST_DrawLevelField(x, y);
9744     }
9745   }
9746 }
9747
9748 static void MauerAbleger(int ax, int ay)
9749 {
9750   int element = Tile[ax][ay];
9751   int graphic = el2img(element);
9752   boolean oben_frei = FALSE, unten_frei = FALSE;
9753   boolean links_frei = FALSE, rechts_frei = FALSE;
9754   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9755   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9756   boolean new_wall = FALSE;
9757
9758   if (IS_ANIMATED(graphic))
9759     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9760
9761   if (!MovDelay[ax][ay])        // start building new wall
9762     MovDelay[ax][ay] = 6;
9763
9764   if (MovDelay[ax][ay])         // wait some time before building new wall
9765   {
9766     MovDelay[ax][ay]--;
9767     if (MovDelay[ax][ay])
9768       return;
9769   }
9770
9771   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9772     oben_frei = TRUE;
9773   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9774     unten_frei = TRUE;
9775   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9776     links_frei = TRUE;
9777   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9778     rechts_frei = TRUE;
9779
9780   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9781       element == EL_EXPANDABLE_WALL_ANY)
9782   {
9783     if (oben_frei)
9784     {
9785       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9786       Store[ax][ay - 1] = element;
9787       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9788       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9789         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9790       new_wall = TRUE;
9791     }
9792     if (unten_frei)
9793     {
9794       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9795       Store[ax][ay + 1] = element;
9796       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9797       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9798         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9799       new_wall = TRUE;
9800     }
9801   }
9802
9803   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9804       element == EL_EXPANDABLE_WALL_ANY ||
9805       element == EL_EXPANDABLE_WALL ||
9806       element == EL_BD_EXPANDABLE_WALL)
9807   {
9808     if (links_frei)
9809     {
9810       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9811       Store[ax - 1][ay] = element;
9812       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9813       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9814         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9815       new_wall = TRUE;
9816     }
9817
9818     if (rechts_frei)
9819     {
9820       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9821       Store[ax + 1][ay] = element;
9822       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9823       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9824         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9825       new_wall = TRUE;
9826     }
9827   }
9828
9829   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9830     TEST_DrawLevelField(ax, ay);
9831
9832   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9833     oben_massiv = TRUE;
9834   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9835     unten_massiv = TRUE;
9836   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9837     links_massiv = TRUE;
9838   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9839     rechts_massiv = TRUE;
9840
9841   if (((oben_massiv && unten_massiv) ||
9842        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9843        element == EL_EXPANDABLE_WALL) &&
9844       ((links_massiv && rechts_massiv) ||
9845        element == EL_EXPANDABLE_WALL_VERTICAL))
9846     Tile[ax][ay] = EL_WALL;
9847
9848   if (new_wall)
9849     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9850 }
9851
9852 static void MauerAblegerStahl(int ax, int ay)
9853 {
9854   int element = Tile[ax][ay];
9855   int graphic = el2img(element);
9856   boolean oben_frei = FALSE, unten_frei = FALSE;
9857   boolean links_frei = FALSE, rechts_frei = FALSE;
9858   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9859   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9860   boolean new_wall = FALSE;
9861
9862   if (IS_ANIMATED(graphic))
9863     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9864
9865   if (!MovDelay[ax][ay])        // start building new wall
9866     MovDelay[ax][ay] = 6;
9867
9868   if (MovDelay[ax][ay])         // wait some time before building new wall
9869   {
9870     MovDelay[ax][ay]--;
9871     if (MovDelay[ax][ay])
9872       return;
9873   }
9874
9875   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9876     oben_frei = TRUE;
9877   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9878     unten_frei = TRUE;
9879   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9880     links_frei = TRUE;
9881   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9882     rechts_frei = TRUE;
9883
9884   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9885       element == EL_EXPANDABLE_STEELWALL_ANY)
9886   {
9887     if (oben_frei)
9888     {
9889       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9890       Store[ax][ay - 1] = element;
9891       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9892       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9893         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9894       new_wall = TRUE;
9895     }
9896     if (unten_frei)
9897     {
9898       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9899       Store[ax][ay + 1] = element;
9900       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9901       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9902         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9903       new_wall = TRUE;
9904     }
9905   }
9906
9907   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9908       element == EL_EXPANDABLE_STEELWALL_ANY)
9909   {
9910     if (links_frei)
9911     {
9912       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9913       Store[ax - 1][ay] = element;
9914       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9915       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9916         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9917       new_wall = TRUE;
9918     }
9919
9920     if (rechts_frei)
9921     {
9922       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9923       Store[ax + 1][ay] = element;
9924       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9925       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9926         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9927       new_wall = TRUE;
9928     }
9929   }
9930
9931   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9932     oben_massiv = TRUE;
9933   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9934     unten_massiv = TRUE;
9935   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9936     links_massiv = TRUE;
9937   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9938     rechts_massiv = TRUE;
9939
9940   if (((oben_massiv && unten_massiv) ||
9941        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9942       ((links_massiv && rechts_massiv) ||
9943        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9944     Tile[ax][ay] = EL_STEELWALL;
9945
9946   if (new_wall)
9947     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9948 }
9949
9950 static void CheckForDragon(int x, int y)
9951 {
9952   int i, j;
9953   boolean dragon_found = FALSE;
9954   static int xy[4][2] =
9955   {
9956     { 0, -1 },
9957     { -1, 0 },
9958     { +1, 0 },
9959     { 0, +1 }
9960   };
9961
9962   for (i = 0; i < NUM_DIRECTIONS; i++)
9963   {
9964     for (j = 0; j < 4; j++)
9965     {
9966       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9967
9968       if (IN_LEV_FIELD(xx, yy) &&
9969           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9970       {
9971         if (Tile[xx][yy] == EL_DRAGON)
9972           dragon_found = TRUE;
9973       }
9974       else
9975         break;
9976     }
9977   }
9978
9979   if (!dragon_found)
9980   {
9981     for (i = 0; i < NUM_DIRECTIONS; i++)
9982     {
9983       for (j = 0; j < 3; j++)
9984       {
9985         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9986   
9987         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9988         {
9989           Tile[xx][yy] = EL_EMPTY;
9990           TEST_DrawLevelField(xx, yy);
9991         }
9992         else
9993           break;
9994       }
9995     }
9996   }
9997 }
9998
9999 static void InitBuggyBase(int x, int y)
10000 {
10001   int element = Tile[x][y];
10002   int activating_delay = FRAMES_PER_SECOND / 4;
10003
10004   ChangeDelay[x][y] =
10005     (element == EL_SP_BUGGY_BASE ?
10006      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10007      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10008      activating_delay :
10009      element == EL_SP_BUGGY_BASE_ACTIVE ?
10010      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10011 }
10012
10013 static void WarnBuggyBase(int x, int y)
10014 {
10015   int i;
10016   static int xy[4][2] =
10017   {
10018     { 0, -1 },
10019     { -1, 0 },
10020     { +1, 0 },
10021     { 0, +1 }
10022   };
10023
10024   for (i = 0; i < NUM_DIRECTIONS; i++)
10025   {
10026     int xx = x + xy[i][0];
10027     int yy = y + xy[i][1];
10028
10029     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10030     {
10031       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10032
10033       break;
10034     }
10035   }
10036 }
10037
10038 static void InitTrap(int x, int y)
10039 {
10040   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10041 }
10042
10043 static void ActivateTrap(int x, int y)
10044 {
10045   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10046 }
10047
10048 static void ChangeActiveTrap(int x, int y)
10049 {
10050   int graphic = IMG_TRAP_ACTIVE;
10051
10052   // if new animation frame was drawn, correct crumbled sand border
10053   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10054     TEST_DrawLevelFieldCrumbled(x, y);
10055 }
10056
10057 static int getSpecialActionElement(int element, int number, int base_element)
10058 {
10059   return (element != EL_EMPTY ? element :
10060           number != -1 ? base_element + number - 1 :
10061           EL_EMPTY);
10062 }
10063
10064 static int getModifiedActionNumber(int value_old, int operator, int operand,
10065                                    int value_min, int value_max)
10066 {
10067   int value_new = (operator == CA_MODE_SET      ? operand :
10068                    operator == CA_MODE_ADD      ? value_old + operand :
10069                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10070                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10071                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10072                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10073                    value_old);
10074
10075   return (value_new < value_min ? value_min :
10076           value_new > value_max ? value_max :
10077           value_new);
10078 }
10079
10080 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10081 {
10082   struct ElementInfo *ei = &element_info[element];
10083   struct ElementChangeInfo *change = &ei->change_page[page];
10084   int target_element = change->target_element;
10085   int action_type = change->action_type;
10086   int action_mode = change->action_mode;
10087   int action_arg = change->action_arg;
10088   int action_element = change->action_element;
10089   int i;
10090
10091   if (!change->has_action)
10092     return;
10093
10094   // ---------- determine action paramater values -----------------------------
10095
10096   int level_time_value =
10097     (level.time > 0 ? TimeLeft :
10098      TimePlayed);
10099
10100   int action_arg_element_raw =
10101     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10102      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10103      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10104      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10105      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10106      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10107      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10108      EL_EMPTY);
10109   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10110
10111   int action_arg_direction =
10112     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10113      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10114      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10115      change->actual_trigger_side :
10116      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10117      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10118      MV_NONE);
10119
10120   int action_arg_number_min =
10121     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10122      CA_ARG_MIN);
10123
10124   int action_arg_number_max =
10125     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10126      action_type == CA_SET_LEVEL_GEMS ? 999 :
10127      action_type == CA_SET_LEVEL_TIME ? 9999 :
10128      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10129      action_type == CA_SET_CE_VALUE ? 9999 :
10130      action_type == CA_SET_CE_SCORE ? 9999 :
10131      CA_ARG_MAX);
10132
10133   int action_arg_number_reset =
10134     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10135      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10136      action_type == CA_SET_LEVEL_TIME ? level.time :
10137      action_type == CA_SET_LEVEL_SCORE ? 0 :
10138      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10139      action_type == CA_SET_CE_SCORE ? 0 :
10140      0);
10141
10142   int action_arg_number =
10143     (action_arg <= CA_ARG_MAX ? action_arg :
10144      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10145      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10146      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10147      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10148      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10149      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10150      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10151      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10152      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10153      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10154      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10155      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10156      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10157      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10158      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10159      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10160      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10161      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10162      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10163      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10164      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10165      -1);
10166
10167   int action_arg_number_old =
10168     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10169      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10170      action_type == CA_SET_LEVEL_SCORE ? game.score :
10171      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10172      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10173      0);
10174
10175   int action_arg_number_new =
10176     getModifiedActionNumber(action_arg_number_old,
10177                             action_mode, action_arg_number,
10178                             action_arg_number_min, action_arg_number_max);
10179
10180   int trigger_player_bits =
10181     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10182      change->actual_trigger_player_bits : change->trigger_player);
10183
10184   int action_arg_player_bits =
10185     (action_arg >= CA_ARG_PLAYER_1 &&
10186      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10187      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10188      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10189      PLAYER_BITS_ANY);
10190
10191   // ---------- execute action  -----------------------------------------------
10192
10193   switch (action_type)
10194   {
10195     case CA_NO_ACTION:
10196     {
10197       return;
10198     }
10199
10200     // ---------- level actions  ----------------------------------------------
10201
10202     case CA_RESTART_LEVEL:
10203     {
10204       game.restart_level = TRUE;
10205
10206       break;
10207     }
10208
10209     case CA_SHOW_ENVELOPE:
10210     {
10211       int element = getSpecialActionElement(action_arg_element,
10212                                             action_arg_number, EL_ENVELOPE_1);
10213
10214       if (IS_ENVELOPE(element))
10215         local_player->show_envelope = element;
10216
10217       break;
10218     }
10219
10220     case CA_SET_LEVEL_TIME:
10221     {
10222       if (level.time > 0)       // only modify limited time value
10223       {
10224         TimeLeft = action_arg_number_new;
10225
10226         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10227
10228         DisplayGameControlValues();
10229
10230         if (!TimeLeft && setup.time_limit)
10231           for (i = 0; i < MAX_PLAYERS; i++)
10232             KillPlayer(&stored_player[i]);
10233       }
10234
10235       break;
10236     }
10237
10238     case CA_SET_LEVEL_SCORE:
10239     {
10240       game.score = action_arg_number_new;
10241
10242       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10243
10244       DisplayGameControlValues();
10245
10246       break;
10247     }
10248
10249     case CA_SET_LEVEL_GEMS:
10250     {
10251       game.gems_still_needed = action_arg_number_new;
10252
10253       game.snapshot.collected_item = TRUE;
10254
10255       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10256
10257       DisplayGameControlValues();
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_WIND:
10263     {
10264       game.wind_direction = action_arg_direction;
10265
10266       break;
10267     }
10268
10269     case CA_SET_LEVEL_RANDOM_SEED:
10270     {
10271       // ensure that setting a new random seed while playing is predictable
10272       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10273
10274       break;
10275     }
10276
10277     // ---------- player actions  ---------------------------------------------
10278
10279     case CA_MOVE_PLAYER:
10280     case CA_MOVE_PLAYER_NEW:
10281     {
10282       // automatically move to the next field in specified direction
10283       for (i = 0; i < MAX_PLAYERS; i++)
10284         if (trigger_player_bits & (1 << i))
10285           if (action_type == CA_MOVE_PLAYER ||
10286               stored_player[i].MovPos == 0)
10287             stored_player[i].programmed_action = action_arg_direction;
10288
10289       break;
10290     }
10291
10292     case CA_EXIT_PLAYER:
10293     {
10294       for (i = 0; i < MAX_PLAYERS; i++)
10295         if (action_arg_player_bits & (1 << i))
10296           ExitPlayer(&stored_player[i]);
10297
10298       if (game.players_still_needed == 0)
10299         LevelSolved();
10300
10301       break;
10302     }
10303
10304     case CA_KILL_PLAYER:
10305     {
10306       for (i = 0; i < MAX_PLAYERS; i++)
10307         if (action_arg_player_bits & (1 << i))
10308           KillPlayer(&stored_player[i]);
10309
10310       break;
10311     }
10312
10313     case CA_SET_PLAYER_KEYS:
10314     {
10315       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10316       int element = getSpecialActionElement(action_arg_element,
10317                                             action_arg_number, EL_KEY_1);
10318
10319       if (IS_KEY(element))
10320       {
10321         for (i = 0; i < MAX_PLAYERS; i++)
10322         {
10323           if (trigger_player_bits & (1 << i))
10324           {
10325             stored_player[i].key[KEY_NR(element)] = key_state;
10326
10327             DrawGameDoorValues();
10328           }
10329         }
10330       }
10331
10332       break;
10333     }
10334
10335     case CA_SET_PLAYER_SPEED:
10336     {
10337       for (i = 0; i < MAX_PLAYERS; i++)
10338       {
10339         if (trigger_player_bits & (1 << i))
10340         {
10341           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10342
10343           if (action_arg == CA_ARG_SPEED_FASTER &&
10344               stored_player[i].cannot_move)
10345           {
10346             action_arg_number = STEPSIZE_VERY_SLOW;
10347           }
10348           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10349                    action_arg == CA_ARG_SPEED_FASTER)
10350           {
10351             action_arg_number = 2;
10352             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10353                            CA_MODE_MULTIPLY);
10354           }
10355           else if (action_arg == CA_ARG_NUMBER_RESET)
10356           {
10357             action_arg_number = level.initial_player_stepsize[i];
10358           }
10359
10360           move_stepsize =
10361             getModifiedActionNumber(move_stepsize,
10362                                     action_mode,
10363                                     action_arg_number,
10364                                     action_arg_number_min,
10365                                     action_arg_number_max);
10366
10367           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_PLAYER_SHIELD:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377       {
10378         if (trigger_player_bits & (1 << i))
10379         {
10380           if (action_arg == CA_ARG_SHIELD_OFF)
10381           {
10382             stored_player[i].shield_normal_time_left = 0;
10383             stored_player[i].shield_deadly_time_left = 0;
10384           }
10385           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10386           {
10387             stored_player[i].shield_normal_time_left = 999999;
10388           }
10389           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10390           {
10391             stored_player[i].shield_normal_time_left = 999999;
10392             stored_player[i].shield_deadly_time_left = 999999;
10393           }
10394         }
10395       }
10396
10397       break;
10398     }
10399
10400     case CA_SET_PLAYER_GRAVITY:
10401     {
10402       for (i = 0; i < MAX_PLAYERS; i++)
10403       {
10404         if (trigger_player_bits & (1 << i))
10405         {
10406           stored_player[i].gravity =
10407             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10408              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10409              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10410              stored_player[i].gravity);
10411         }
10412       }
10413
10414       break;
10415     }
10416
10417     case CA_SET_PLAYER_ARTWORK:
10418     {
10419       for (i = 0; i < MAX_PLAYERS; i++)
10420       {
10421         if (trigger_player_bits & (1 << i))
10422         {
10423           int artwork_element = action_arg_element;
10424
10425           if (action_arg == CA_ARG_ELEMENT_RESET)
10426             artwork_element =
10427               (level.use_artwork_element[i] ? level.artwork_element[i] :
10428                stored_player[i].element_nr);
10429
10430           if (stored_player[i].artwork_element != artwork_element)
10431             stored_player[i].Frame = 0;
10432
10433           stored_player[i].artwork_element = artwork_element;
10434
10435           SetPlayerWaiting(&stored_player[i], FALSE);
10436
10437           // set number of special actions for bored and sleeping animation
10438           stored_player[i].num_special_action_bored =
10439             get_num_special_action(artwork_element,
10440                                    ACTION_BORING_1, ACTION_BORING_LAST);
10441           stored_player[i].num_special_action_sleeping =
10442             get_num_special_action(artwork_element,
10443                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10444         }
10445       }
10446
10447       break;
10448     }
10449
10450     case CA_SET_PLAYER_INVENTORY:
10451     {
10452       for (i = 0; i < MAX_PLAYERS; i++)
10453       {
10454         struct PlayerInfo *player = &stored_player[i];
10455         int j, k;
10456
10457         if (trigger_player_bits & (1 << i))
10458         {
10459           int inventory_element = action_arg_element;
10460
10461           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10462               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10463               action_arg == CA_ARG_ELEMENT_ACTION)
10464           {
10465             int element = inventory_element;
10466             int collect_count = element_info[element].collect_count_initial;
10467
10468             if (!IS_CUSTOM_ELEMENT(element))
10469               collect_count = 1;
10470
10471             if (collect_count == 0)
10472               player->inventory_infinite_element = element;
10473             else
10474               for (k = 0; k < collect_count; k++)
10475                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10476                   player->inventory_element[player->inventory_size++] =
10477                     element;
10478           }
10479           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10480                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10481                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10482           {
10483             if (player->inventory_infinite_element != EL_UNDEFINED &&
10484                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10485                                      action_arg_element_raw))
10486               player->inventory_infinite_element = EL_UNDEFINED;
10487
10488             for (k = 0, j = 0; j < player->inventory_size; j++)
10489             {
10490               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10491                                         action_arg_element_raw))
10492                 player->inventory_element[k++] = player->inventory_element[j];
10493             }
10494
10495             player->inventory_size = k;
10496           }
10497           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10498           {
10499             if (player->inventory_size > 0)
10500             {
10501               for (j = 0; j < player->inventory_size - 1; j++)
10502                 player->inventory_element[j] = player->inventory_element[j + 1];
10503
10504               player->inventory_size--;
10505             }
10506           }
10507           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10508           {
10509             if (player->inventory_size > 0)
10510               player->inventory_size--;
10511           }
10512           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10513           {
10514             player->inventory_infinite_element = EL_UNDEFINED;
10515             player->inventory_size = 0;
10516           }
10517           else if (action_arg == CA_ARG_INVENTORY_RESET)
10518           {
10519             player->inventory_infinite_element = EL_UNDEFINED;
10520             player->inventory_size = 0;
10521
10522             if (level.use_initial_inventory[i])
10523             {
10524               for (j = 0; j < level.initial_inventory_size[i]; j++)
10525               {
10526                 int element = level.initial_inventory_content[i][j];
10527                 int collect_count = element_info[element].collect_count_initial;
10528
10529                 if (!IS_CUSTOM_ELEMENT(element))
10530                   collect_count = 1;
10531
10532                 if (collect_count == 0)
10533                   player->inventory_infinite_element = element;
10534                 else
10535                   for (k = 0; k < collect_count; k++)
10536                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10537                       player->inventory_element[player->inventory_size++] =
10538                         element;
10539               }
10540             }
10541           }
10542         }
10543       }
10544
10545       break;
10546     }
10547
10548     // ---------- CE actions  -------------------------------------------------
10549
10550     case CA_SET_CE_VALUE:
10551     {
10552       int last_ce_value = CustomValue[x][y];
10553
10554       CustomValue[x][y] = action_arg_number_new;
10555
10556       if (CustomValue[x][y] != last_ce_value)
10557       {
10558         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10559         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10560
10561         if (CustomValue[x][y] == 0)
10562         {
10563           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10564           ChangeCount[x][y] = 0;        // allow at least one more change
10565
10566           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10567           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10568         }
10569       }
10570
10571       break;
10572     }
10573
10574     case CA_SET_CE_SCORE:
10575     {
10576       int last_ce_score = ei->collect_score;
10577
10578       ei->collect_score = action_arg_number_new;
10579
10580       if (ei->collect_score != last_ce_score)
10581       {
10582         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10583         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10584
10585         if (ei->collect_score == 0)
10586         {
10587           int xx, yy;
10588
10589           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10590           ChangeCount[x][y] = 0;        // allow at least one more change
10591
10592           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10593           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10594
10595           /*
10596             This is a very special case that seems to be a mixture between
10597             CheckElementChange() and CheckTriggeredElementChange(): while
10598             the first one only affects single elements that are triggered
10599             directly, the second one affects multiple elements in the playfield
10600             that are triggered indirectly by another element. This is a third
10601             case: Changing the CE score always affects multiple identical CEs,
10602             so every affected CE must be checked, not only the single CE for
10603             which the CE score was changed in the first place (as every instance
10604             of that CE shares the same CE score, and therefore also can change)!
10605           */
10606           SCAN_PLAYFIELD(xx, yy)
10607           {
10608             if (Tile[xx][yy] == element)
10609               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10610                                  CE_SCORE_GETS_ZERO);
10611           }
10612         }
10613       }
10614
10615       break;
10616     }
10617
10618     case CA_SET_CE_ARTWORK:
10619     {
10620       int artwork_element = action_arg_element;
10621       boolean reset_frame = FALSE;
10622       int xx, yy;
10623
10624       if (action_arg == CA_ARG_ELEMENT_RESET)
10625         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10626                            element);
10627
10628       if (ei->gfx_element != artwork_element)
10629         reset_frame = TRUE;
10630
10631       ei->gfx_element = artwork_element;
10632
10633       SCAN_PLAYFIELD(xx, yy)
10634       {
10635         if (Tile[xx][yy] == element)
10636         {
10637           if (reset_frame)
10638           {
10639             ResetGfxAnimation(xx, yy);
10640             ResetRandomAnimationValue(xx, yy);
10641           }
10642
10643           TEST_DrawLevelField(xx, yy);
10644         }
10645       }
10646
10647       break;
10648     }
10649
10650     // ---------- engine actions  ---------------------------------------------
10651
10652     case CA_SET_ENGINE_SCAN_MODE:
10653     {
10654       InitPlayfieldScanMode(action_arg);
10655
10656       break;
10657     }
10658
10659     default:
10660       break;
10661   }
10662 }
10663
10664 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10665 {
10666   int old_element = Tile[x][y];
10667   int new_element = GetElementFromGroupElement(element);
10668   int previous_move_direction = MovDir[x][y];
10669   int last_ce_value = CustomValue[x][y];
10670   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10671   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10672   boolean add_player_onto_element = (new_element_is_player &&
10673                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10674                                      IS_WALKABLE(old_element));
10675
10676   if (!add_player_onto_element)
10677   {
10678     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10679       RemoveMovingField(x, y);
10680     else
10681       RemoveField(x, y);
10682
10683     Tile[x][y] = new_element;
10684
10685     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10686       MovDir[x][y] = previous_move_direction;
10687
10688     if (element_info[new_element].use_last_ce_value)
10689       CustomValue[x][y] = last_ce_value;
10690
10691     InitField_WithBug1(x, y, FALSE);
10692
10693     new_element = Tile[x][y];   // element may have changed
10694
10695     ResetGfxAnimation(x, y);
10696     ResetRandomAnimationValue(x, y);
10697
10698     TEST_DrawLevelField(x, y);
10699
10700     if (GFX_CRUMBLED(new_element))
10701       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10702   }
10703
10704   // check if element under the player changes from accessible to unaccessible
10705   // (needed for special case of dropping element which then changes)
10706   // (must be checked after creating new element for walkable group elements)
10707   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10708       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10709   {
10710     Bang(x, y);
10711
10712     return;
10713   }
10714
10715   // "ChangeCount" not set yet to allow "entered by player" change one time
10716   if (new_element_is_player)
10717     RelocatePlayer(x, y, new_element);
10718
10719   if (is_change)
10720     ChangeCount[x][y]++;        // count number of changes in the same frame
10721
10722   TestIfBadThingTouchesPlayer(x, y);
10723   TestIfPlayerTouchesCustomElement(x, y);
10724   TestIfElementTouchesCustomElement(x, y);
10725 }
10726
10727 static void CreateField(int x, int y, int element)
10728 {
10729   CreateFieldExt(x, y, element, FALSE);
10730 }
10731
10732 static void CreateElementFromChange(int x, int y, int element)
10733 {
10734   element = GET_VALID_RUNTIME_ELEMENT(element);
10735
10736   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10737   {
10738     int old_element = Tile[x][y];
10739
10740     // prevent changed element from moving in same engine frame
10741     // unless both old and new element can either fall or move
10742     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10743         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10744       Stop[x][y] = TRUE;
10745   }
10746
10747   CreateFieldExt(x, y, element, TRUE);
10748 }
10749
10750 static boolean ChangeElement(int x, int y, int element, int page)
10751 {
10752   struct ElementInfo *ei = &element_info[element];
10753   struct ElementChangeInfo *change = &ei->change_page[page];
10754   int ce_value = CustomValue[x][y];
10755   int ce_score = ei->collect_score;
10756   int target_element;
10757   int old_element = Tile[x][y];
10758
10759   // always use default change event to prevent running into a loop
10760   if (ChangeEvent[x][y] == -1)
10761     ChangeEvent[x][y] = CE_DELAY;
10762
10763   if (ChangeEvent[x][y] == CE_DELAY)
10764   {
10765     // reset actual trigger element, trigger player and action element
10766     change->actual_trigger_element = EL_EMPTY;
10767     change->actual_trigger_player = EL_EMPTY;
10768     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10769     change->actual_trigger_side = CH_SIDE_NONE;
10770     change->actual_trigger_ce_value = 0;
10771     change->actual_trigger_ce_score = 0;
10772   }
10773
10774   // do not change elements more than a specified maximum number of changes
10775   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10776     return FALSE;
10777
10778   ChangeCount[x][y]++;          // count number of changes in the same frame
10779
10780   if (change->explode)
10781   {
10782     Bang(x, y);
10783
10784     return TRUE;
10785   }
10786
10787   if (change->use_target_content)
10788   {
10789     boolean complete_replace = TRUE;
10790     boolean can_replace[3][3];
10791     int xx, yy;
10792
10793     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10794     {
10795       boolean is_empty;
10796       boolean is_walkable;
10797       boolean is_diggable;
10798       boolean is_collectible;
10799       boolean is_removable;
10800       boolean is_destructible;
10801       int ex = x + xx - 1;
10802       int ey = y + yy - 1;
10803       int content_element = change->target_content.e[xx][yy];
10804       int e;
10805
10806       can_replace[xx][yy] = TRUE;
10807
10808       if (ex == x && ey == y)   // do not check changing element itself
10809         continue;
10810
10811       if (content_element == EL_EMPTY_SPACE)
10812       {
10813         can_replace[xx][yy] = FALSE;    // do not replace border with space
10814
10815         continue;
10816       }
10817
10818       if (!IN_LEV_FIELD(ex, ey))
10819       {
10820         can_replace[xx][yy] = FALSE;
10821         complete_replace = FALSE;
10822
10823         continue;
10824       }
10825
10826       e = Tile[ex][ey];
10827
10828       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10829         e = MovingOrBlocked2Element(ex, ey);
10830
10831       is_empty = (IS_FREE(ex, ey) ||
10832                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10833
10834       is_walkable     = (is_empty || IS_WALKABLE(e));
10835       is_diggable     = (is_empty || IS_DIGGABLE(e));
10836       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10837       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10838       is_removable    = (is_diggable || is_collectible);
10839
10840       can_replace[xx][yy] =
10841         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10842           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10843           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10844           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10845           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10846           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10847          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10848
10849       if (!can_replace[xx][yy])
10850         complete_replace = FALSE;
10851     }
10852
10853     if (!change->only_if_complete || complete_replace)
10854     {
10855       boolean something_has_changed = FALSE;
10856
10857       if (change->only_if_complete && change->use_random_replace &&
10858           RND(100) < change->random_percentage)
10859         return FALSE;
10860
10861       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10862       {
10863         int ex = x + xx - 1;
10864         int ey = y + yy - 1;
10865         int content_element;
10866
10867         if (can_replace[xx][yy] && (!change->use_random_replace ||
10868                                     RND(100) < change->random_percentage))
10869         {
10870           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10871             RemoveMovingField(ex, ey);
10872
10873           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10874
10875           content_element = change->target_content.e[xx][yy];
10876           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10877                                               ce_value, ce_score);
10878
10879           CreateElementFromChange(ex, ey, target_element);
10880
10881           something_has_changed = TRUE;
10882
10883           // for symmetry reasons, freeze newly created border elements
10884           if (ex != x || ey != y)
10885             Stop[ex][ey] = TRUE;        // no more moving in this frame
10886         }
10887       }
10888
10889       if (something_has_changed)
10890       {
10891         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10892         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10893       }
10894     }
10895   }
10896   else
10897   {
10898     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10899                                         ce_value, ce_score);
10900
10901     if (element == EL_DIAGONAL_GROWING ||
10902         element == EL_DIAGONAL_SHRINKING)
10903     {
10904       target_element = Store[x][y];
10905
10906       Store[x][y] = EL_EMPTY;
10907     }
10908
10909     // special case: element changes to player (and may be kept if walkable)
10910     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10911       CreateElementFromChange(x, y, EL_EMPTY);
10912
10913     CreateElementFromChange(x, y, target_element);
10914
10915     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10916     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10917   }
10918
10919   // this uses direct change before indirect change
10920   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10921
10922   return TRUE;
10923 }
10924
10925 static void HandleElementChange(int x, int y, int page)
10926 {
10927   int element = MovingOrBlocked2Element(x, y);
10928   struct ElementInfo *ei = &element_info[element];
10929   struct ElementChangeInfo *change = &ei->change_page[page];
10930   boolean handle_action_before_change = FALSE;
10931
10932 #ifdef DEBUG
10933   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10934       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10935   {
10936     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10937           x, y, element, element_info[element].token_name);
10938     Debug("game:playing:HandleElementChange", "This should never happen!");
10939   }
10940 #endif
10941
10942   // this can happen with classic bombs on walkable, changing elements
10943   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10944   {
10945     return;
10946   }
10947
10948   if (ChangeDelay[x][y] == 0)           // initialize element change
10949   {
10950     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10951
10952     if (change->can_change)
10953     {
10954       // !!! not clear why graphic animation should be reset at all here !!!
10955       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10956       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10957
10958       /*
10959         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10960
10961         When using an animation frame delay of 1 (this only happens with
10962         "sp_zonk.moving.left/right" in the classic graphics), the default
10963         (non-moving) animation shows wrong animation frames (while the
10964         moving animation, like "sp_zonk.moving.left/right", is correct,
10965         so this graphical bug never shows up with the classic graphics).
10966         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10967         be drawn instead of the correct frames 0,1,2,3. This is caused by
10968         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10969         an element change: First when the change delay ("ChangeDelay[][]")
10970         counter has reached zero after decrementing, then a second time in
10971         the next frame (after "GfxFrame[][]" was already incremented) when
10972         "ChangeDelay[][]" is reset to the initial delay value again.
10973
10974         This causes frame 0 to be drawn twice, while the last frame won't
10975         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10976
10977         As some animations may already be cleverly designed around this bug
10978         (at least the "Snake Bite" snake tail animation does this), it cannot
10979         simply be fixed here without breaking such existing animations.
10980         Unfortunately, it cannot easily be detected if a graphics set was
10981         designed "before" or "after" the bug was fixed. As a workaround,
10982         a new graphics set option "game.graphics_engine_version" was added
10983         to be able to specify the game's major release version for which the
10984         graphics set was designed, which can then be used to decide if the
10985         bugfix should be used (version 4 and above) or not (version 3 or
10986         below, or if no version was specified at all, as with old sets).
10987
10988         (The wrong/fixed animation frames can be tested with the test level set
10989         "test_gfxframe" and level "000", which contains a specially prepared
10990         custom element at level position (x/y) == (11/9) which uses the zonk
10991         animation mentioned above. Using "game.graphics_engine_version: 4"
10992         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10993         This can also be seen from the debug output for this test element.)
10994       */
10995
10996       // when a custom element is about to change (for example by change delay),
10997       // do not reset graphic animation when the custom element is moving
10998       if (game.graphics_engine_version < 4 &&
10999           !IS_MOVING(x, y))
11000       {
11001         ResetGfxAnimation(x, y);
11002         ResetRandomAnimationValue(x, y);
11003       }
11004
11005       if (change->pre_change_function)
11006         change->pre_change_function(x, y);
11007     }
11008   }
11009
11010   ChangeDelay[x][y]--;
11011
11012   if (ChangeDelay[x][y] != 0)           // continue element change
11013   {
11014     if (change->can_change)
11015     {
11016       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11017
11018       if (IS_ANIMATED(graphic))
11019         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11020
11021       if (change->change_function)
11022         change->change_function(x, y);
11023     }
11024   }
11025   else                                  // finish element change
11026   {
11027     if (ChangePage[x][y] != -1)         // remember page from delayed change
11028     {
11029       page = ChangePage[x][y];
11030       ChangePage[x][y] = -1;
11031
11032       change = &ei->change_page[page];
11033     }
11034
11035     if (IS_MOVING(x, y))                // never change a running system ;-)
11036     {
11037       ChangeDelay[x][y] = 1;            // try change after next move step
11038       ChangePage[x][y] = page;          // remember page to use for change
11039
11040       return;
11041     }
11042
11043     // special case: set new level random seed before changing element
11044     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11045       handle_action_before_change = TRUE;
11046
11047     if (change->has_action && handle_action_before_change)
11048       ExecuteCustomElementAction(x, y, element, page);
11049
11050     if (change->can_change)
11051     {
11052       if (ChangeElement(x, y, element, page))
11053       {
11054         if (change->post_change_function)
11055           change->post_change_function(x, y);
11056       }
11057     }
11058
11059     if (change->has_action && !handle_action_before_change)
11060       ExecuteCustomElementAction(x, y, element, page);
11061   }
11062 }
11063
11064 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11065                                               int trigger_element,
11066                                               int trigger_event,
11067                                               int trigger_player,
11068                                               int trigger_side,
11069                                               int trigger_page)
11070 {
11071   boolean change_done_any = FALSE;
11072   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11073   int i;
11074
11075   if (!(trigger_events[trigger_element][trigger_event]))
11076     return FALSE;
11077
11078   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11079
11080   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11081   {
11082     int element = EL_CUSTOM_START + i;
11083     boolean change_done = FALSE;
11084     int p;
11085
11086     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11087         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11088       continue;
11089
11090     for (p = 0; p < element_info[element].num_change_pages; p++)
11091     {
11092       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11093
11094       if (change->can_change_or_has_action &&
11095           change->has_event[trigger_event] &&
11096           change->trigger_side & trigger_side &&
11097           change->trigger_player & trigger_player &&
11098           change->trigger_page & trigger_page_bits &&
11099           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11100       {
11101         change->actual_trigger_element = trigger_element;
11102         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11103         change->actual_trigger_player_bits = trigger_player;
11104         change->actual_trigger_side = trigger_side;
11105         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11106         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11107
11108         if ((change->can_change && !change_done) || change->has_action)
11109         {
11110           int x, y;
11111
11112           SCAN_PLAYFIELD(x, y)
11113           {
11114             if (Tile[x][y] == element)
11115             {
11116               if (change->can_change && !change_done)
11117               {
11118                 // if element already changed in this frame, not only prevent
11119                 // another element change (checked in ChangeElement()), but
11120                 // also prevent additional element actions for this element
11121
11122                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11123                     !level.use_action_after_change_bug)
11124                   continue;
11125
11126                 ChangeDelay[x][y] = 1;
11127                 ChangeEvent[x][y] = trigger_event;
11128
11129                 HandleElementChange(x, y, p);
11130               }
11131               else if (change->has_action)
11132               {
11133                 // if element already changed in this frame, not only prevent
11134                 // another element change (checked in ChangeElement()), but
11135                 // also prevent additional element actions for this element
11136
11137                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11138                     !level.use_action_after_change_bug)
11139                   continue;
11140
11141                 ExecuteCustomElementAction(x, y, element, p);
11142                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11143               }
11144             }
11145           }
11146
11147           if (change->can_change)
11148           {
11149             change_done = TRUE;
11150             change_done_any = TRUE;
11151           }
11152         }
11153       }
11154     }
11155   }
11156
11157   RECURSION_LOOP_DETECTION_END();
11158
11159   return change_done_any;
11160 }
11161
11162 static boolean CheckElementChangeExt(int x, int y,
11163                                      int element,
11164                                      int trigger_element,
11165                                      int trigger_event,
11166                                      int trigger_player,
11167                                      int trigger_side)
11168 {
11169   boolean change_done = FALSE;
11170   int p;
11171
11172   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11173       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11174     return FALSE;
11175
11176   if (Tile[x][y] == EL_BLOCKED)
11177   {
11178     Blocked2Moving(x, y, &x, &y);
11179     element = Tile[x][y];
11180   }
11181
11182   // check if element has already changed or is about to change after moving
11183   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11184        Tile[x][y] != element) ||
11185
11186       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11187        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11188         ChangePage[x][y] != -1)))
11189     return FALSE;
11190
11191   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11192
11193   for (p = 0; p < element_info[element].num_change_pages; p++)
11194   {
11195     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11196
11197     /* check trigger element for all events where the element that is checked
11198        for changing interacts with a directly adjacent element -- this is
11199        different to element changes that affect other elements to change on the
11200        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11201     boolean check_trigger_element =
11202       (trigger_event == CE_NEXT_TO_X ||
11203        trigger_event == CE_TOUCHING_X ||
11204        trigger_event == CE_HITTING_X ||
11205        trigger_event == CE_HIT_BY_X ||
11206        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11207
11208     if (change->can_change_or_has_action &&
11209         change->has_event[trigger_event] &&
11210         change->trigger_side & trigger_side &&
11211         change->trigger_player & trigger_player &&
11212         (!check_trigger_element ||
11213          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11214     {
11215       change->actual_trigger_element = trigger_element;
11216       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11217       change->actual_trigger_player_bits = trigger_player;
11218       change->actual_trigger_side = trigger_side;
11219       change->actual_trigger_ce_value = CustomValue[x][y];
11220       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11221
11222       // special case: trigger element not at (x,y) position for some events
11223       if (check_trigger_element)
11224       {
11225         static struct
11226         {
11227           int dx, dy;
11228         } move_xy[] =
11229           {
11230             {  0,  0 },
11231             { -1,  0 },
11232             { +1,  0 },
11233             {  0,  0 },
11234             {  0, -1 },
11235             {  0,  0 }, { 0, 0 }, { 0, 0 },
11236             {  0, +1 }
11237           };
11238
11239         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11240         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11241
11242         change->actual_trigger_ce_value = CustomValue[xx][yy];
11243         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11244       }
11245
11246       if (change->can_change && !change_done)
11247       {
11248         ChangeDelay[x][y] = 1;
11249         ChangeEvent[x][y] = trigger_event;
11250
11251         HandleElementChange(x, y, p);
11252
11253         change_done = TRUE;
11254       }
11255       else if (change->has_action)
11256       {
11257         ExecuteCustomElementAction(x, y, element, p);
11258         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11259       }
11260     }
11261   }
11262
11263   RECURSION_LOOP_DETECTION_END();
11264
11265   return change_done;
11266 }
11267
11268 static void PlayPlayerSound(struct PlayerInfo *player)
11269 {
11270   int jx = player->jx, jy = player->jy;
11271   int sound_element = player->artwork_element;
11272   int last_action = player->last_action_waiting;
11273   int action = player->action_waiting;
11274
11275   if (player->is_waiting)
11276   {
11277     if (action != last_action)
11278       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11279     else
11280       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11281   }
11282   else
11283   {
11284     if (action != last_action)
11285       StopSound(element_info[sound_element].sound[last_action]);
11286
11287     if (last_action == ACTION_SLEEPING)
11288       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11289   }
11290 }
11291
11292 static void PlayAllPlayersSound(void)
11293 {
11294   int i;
11295
11296   for (i = 0; i < MAX_PLAYERS; i++)
11297     if (stored_player[i].active)
11298       PlayPlayerSound(&stored_player[i]);
11299 }
11300
11301 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11302 {
11303   boolean last_waiting = player->is_waiting;
11304   int move_dir = player->MovDir;
11305
11306   player->dir_waiting = move_dir;
11307   player->last_action_waiting = player->action_waiting;
11308
11309   if (is_waiting)
11310   {
11311     if (!last_waiting)          // not waiting -> waiting
11312     {
11313       player->is_waiting = TRUE;
11314
11315       player->frame_counter_bored =
11316         FrameCounter +
11317         game.player_boring_delay_fixed +
11318         GetSimpleRandom(game.player_boring_delay_random);
11319       player->frame_counter_sleeping =
11320         FrameCounter +
11321         game.player_sleeping_delay_fixed +
11322         GetSimpleRandom(game.player_sleeping_delay_random);
11323
11324       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11325     }
11326
11327     if (game.player_sleeping_delay_fixed +
11328         game.player_sleeping_delay_random > 0 &&
11329         player->anim_delay_counter == 0 &&
11330         player->post_delay_counter == 0 &&
11331         FrameCounter >= player->frame_counter_sleeping)
11332       player->is_sleeping = TRUE;
11333     else if (game.player_boring_delay_fixed +
11334              game.player_boring_delay_random > 0 &&
11335              FrameCounter >= player->frame_counter_bored)
11336       player->is_bored = TRUE;
11337
11338     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11339                               player->is_bored ? ACTION_BORING :
11340                               ACTION_WAITING);
11341
11342     if (player->is_sleeping && player->use_murphy)
11343     {
11344       // special case for sleeping Murphy when leaning against non-free tile
11345
11346       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11347           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11348            !IS_MOVING(player->jx - 1, player->jy)))
11349         move_dir = MV_LEFT;
11350       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11351                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11352                 !IS_MOVING(player->jx + 1, player->jy)))
11353         move_dir = MV_RIGHT;
11354       else
11355         player->is_sleeping = FALSE;
11356
11357       player->dir_waiting = move_dir;
11358     }
11359
11360     if (player->is_sleeping)
11361     {
11362       if (player->num_special_action_sleeping > 0)
11363       {
11364         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11365         {
11366           int last_special_action = player->special_action_sleeping;
11367           int num_special_action = player->num_special_action_sleeping;
11368           int special_action =
11369             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11370              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11371              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11372              last_special_action + 1 : ACTION_SLEEPING);
11373           int special_graphic =
11374             el_act_dir2img(player->artwork_element, special_action, move_dir);
11375
11376           player->anim_delay_counter =
11377             graphic_info[special_graphic].anim_delay_fixed +
11378             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11379           player->post_delay_counter =
11380             graphic_info[special_graphic].post_delay_fixed +
11381             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11382
11383           player->special_action_sleeping = special_action;
11384         }
11385
11386         if (player->anim_delay_counter > 0)
11387         {
11388           player->action_waiting = player->special_action_sleeping;
11389           player->anim_delay_counter--;
11390         }
11391         else if (player->post_delay_counter > 0)
11392         {
11393           player->post_delay_counter--;
11394         }
11395       }
11396     }
11397     else if (player->is_bored)
11398     {
11399       if (player->num_special_action_bored > 0)
11400       {
11401         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11402         {
11403           int special_action =
11404             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11405           int special_graphic =
11406             el_act_dir2img(player->artwork_element, special_action, move_dir);
11407
11408           player->anim_delay_counter =
11409             graphic_info[special_graphic].anim_delay_fixed +
11410             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11411           player->post_delay_counter =
11412             graphic_info[special_graphic].post_delay_fixed +
11413             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11414
11415           player->special_action_bored = special_action;
11416         }
11417
11418         if (player->anim_delay_counter > 0)
11419         {
11420           player->action_waiting = player->special_action_bored;
11421           player->anim_delay_counter--;
11422         }
11423         else if (player->post_delay_counter > 0)
11424         {
11425           player->post_delay_counter--;
11426         }
11427       }
11428     }
11429   }
11430   else if (last_waiting)        // waiting -> not waiting
11431   {
11432     player->is_waiting = FALSE;
11433     player->is_bored = FALSE;
11434     player->is_sleeping = FALSE;
11435
11436     player->frame_counter_bored = -1;
11437     player->frame_counter_sleeping = -1;
11438
11439     player->anim_delay_counter = 0;
11440     player->post_delay_counter = 0;
11441
11442     player->dir_waiting = player->MovDir;
11443     player->action_waiting = ACTION_DEFAULT;
11444
11445     player->special_action_bored = ACTION_DEFAULT;
11446     player->special_action_sleeping = ACTION_DEFAULT;
11447   }
11448 }
11449
11450 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11451 {
11452   if ((!player->is_moving  && player->was_moving) ||
11453       (player->MovPos == 0 && player->was_moving) ||
11454       (player->is_snapping && !player->was_snapping) ||
11455       (player->is_dropping && !player->was_dropping))
11456   {
11457     if (!CheckSaveEngineSnapshotToList())
11458       return;
11459
11460     player->was_moving = FALSE;
11461     player->was_snapping = TRUE;
11462     player->was_dropping = TRUE;
11463   }
11464   else
11465   {
11466     if (player->is_moving)
11467       player->was_moving = TRUE;
11468
11469     if (!player->is_snapping)
11470       player->was_snapping = FALSE;
11471
11472     if (!player->is_dropping)
11473       player->was_dropping = FALSE;
11474   }
11475
11476   static struct MouseActionInfo mouse_action_last = { 0 };
11477   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11478   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11479
11480   if (new_released)
11481     CheckSaveEngineSnapshotToList();
11482
11483   mouse_action_last = mouse_action;
11484 }
11485
11486 static void CheckSingleStepMode(struct PlayerInfo *player)
11487 {
11488   if (tape.single_step && tape.recording && !tape.pausing)
11489   {
11490     // as it is called "single step mode", just return to pause mode when the
11491     // player stopped moving after one tile (or never starts moving at all)
11492     // (reverse logic needed here in case single step mode used in team mode)
11493     if (player->is_moving ||
11494         player->is_pushing ||
11495         player->is_dropping_pressed ||
11496         player->effective_mouse_action.button)
11497       game.enter_single_step_mode = FALSE;
11498   }
11499
11500   CheckSaveEngineSnapshot(player);
11501 }
11502
11503 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11504 {
11505   int left      = player_action & JOY_LEFT;
11506   int right     = player_action & JOY_RIGHT;
11507   int up        = player_action & JOY_UP;
11508   int down      = player_action & JOY_DOWN;
11509   int button1   = player_action & JOY_BUTTON_1;
11510   int button2   = player_action & JOY_BUTTON_2;
11511   int dx        = (left ? -1 : right ? 1 : 0);
11512   int dy        = (up   ? -1 : down  ? 1 : 0);
11513
11514   if (!player->active || tape.pausing)
11515     return 0;
11516
11517   if (player_action)
11518   {
11519     if (button1)
11520       SnapField(player, dx, dy);
11521     else
11522     {
11523       if (button2)
11524         DropElement(player);
11525
11526       MovePlayer(player, dx, dy);
11527     }
11528
11529     CheckSingleStepMode(player);
11530
11531     SetPlayerWaiting(player, FALSE);
11532
11533     return player_action;
11534   }
11535   else
11536   {
11537     // no actions for this player (no input at player's configured device)
11538
11539     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11540     SnapField(player, 0, 0);
11541     CheckGravityMovementWhenNotMoving(player);
11542
11543     if (player->MovPos == 0)
11544       SetPlayerWaiting(player, TRUE);
11545
11546     if (player->MovPos == 0)    // needed for tape.playing
11547       player->is_moving = FALSE;
11548
11549     player->is_dropping = FALSE;
11550     player->is_dropping_pressed = FALSE;
11551     player->drop_pressed_delay = 0;
11552
11553     CheckSingleStepMode(player);
11554
11555     return 0;
11556   }
11557 }
11558
11559 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11560                                          byte *tape_action)
11561 {
11562   if (!tape.use_mouse_actions)
11563     return;
11564
11565   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11566   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11567   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11568 }
11569
11570 static void SetTapeActionFromMouseAction(byte *tape_action,
11571                                          struct MouseActionInfo *mouse_action)
11572 {
11573   if (!tape.use_mouse_actions)
11574     return;
11575
11576   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11577   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11578   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11579 }
11580
11581 static void CheckLevelSolved(void)
11582 {
11583   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11584   {
11585     if (game_em.level_solved &&
11586         !game_em.game_over)                             // game won
11587     {
11588       LevelSolved();
11589
11590       game_em.game_over = TRUE;
11591
11592       game.all_players_gone = TRUE;
11593     }
11594
11595     if (game_em.game_over)                              // game lost
11596       game.all_players_gone = TRUE;
11597   }
11598   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11599   {
11600     if (game_sp.level_solved &&
11601         !game_sp.game_over)                             // game won
11602     {
11603       LevelSolved();
11604
11605       game_sp.game_over = TRUE;
11606
11607       game.all_players_gone = TRUE;
11608     }
11609
11610     if (game_sp.game_over)                              // game lost
11611       game.all_players_gone = TRUE;
11612   }
11613   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11614   {
11615     if (game_mm.level_solved &&
11616         !game_mm.game_over)                             // game won
11617     {
11618       LevelSolved();
11619
11620       game_mm.game_over = TRUE;
11621
11622       game.all_players_gone = TRUE;
11623     }
11624
11625     if (game_mm.game_over)                              // game lost
11626       game.all_players_gone = TRUE;
11627   }
11628 }
11629
11630 static void CheckLevelTime_StepCounter(void)
11631 {
11632   int i;
11633
11634   TimePlayed++;
11635
11636   if (TimeLeft > 0)
11637   {
11638     TimeLeft--;
11639
11640     if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
11641       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11642
11643     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11644
11645     DisplayGameControlValues();
11646
11647     if (!TimeLeft && setup.time_limit && !game.LevelSolved)
11648       for (i = 0; i < MAX_PLAYERS; i++)
11649         KillPlayer(&stored_player[i]);
11650   }
11651   else if (game.no_time_limit && !game.all_players_gone)
11652   {
11653     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11654
11655     DisplayGameControlValues();
11656   }
11657 }
11658
11659 static void CheckLevelTime(void)
11660 {
11661   int i;
11662
11663   if (TimeFrames >= FRAMES_PER_SECOND)
11664   {
11665     TimeFrames = 0;
11666     TapeTime++;
11667
11668     for (i = 0; i < MAX_PLAYERS; i++)
11669     {
11670       struct PlayerInfo *player = &stored_player[i];
11671
11672       if (SHIELD_ON(player))
11673       {
11674         player->shield_normal_time_left--;
11675
11676         if (player->shield_deadly_time_left > 0)
11677           player->shield_deadly_time_left--;
11678       }
11679     }
11680
11681     if (!game.LevelSolved && !level.use_step_counter)
11682     {
11683       TimePlayed++;
11684
11685       if (TimeLeft > 0)
11686       {
11687         TimeLeft--;
11688
11689         if (TimeLeft <= 10 && setup.time_limit)
11690           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11691
11692         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11693            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11694
11695         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11696
11697         if (!TimeLeft && setup.time_limit)
11698         {
11699           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11700             game_em.lev->killed_out_of_time = TRUE;
11701           else
11702             for (i = 0; i < MAX_PLAYERS; i++)
11703               KillPlayer(&stored_player[i]);
11704         }
11705       }
11706       else if (game.no_time_limit && !game.all_players_gone)
11707       {
11708         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11709       }
11710
11711       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11712     }
11713
11714     if (tape.recording || tape.playing)
11715       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11716   }
11717
11718   if (tape.recording || tape.playing)
11719     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11720
11721   UpdateAndDisplayGameControlValues();
11722 }
11723
11724 void AdvanceFrameAndPlayerCounters(int player_nr)
11725 {
11726   int i;
11727
11728   // advance frame counters (global frame counter and time frame counter)
11729   FrameCounter++;
11730   TimeFrames++;
11731
11732   // advance player counters (counters for move delay, move animation etc.)
11733   for (i = 0; i < MAX_PLAYERS; i++)
11734   {
11735     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11736     int move_delay_value = stored_player[i].move_delay_value;
11737     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11738
11739     if (!advance_player_counters)       // not all players may be affected
11740       continue;
11741
11742     if (move_frames == 0)       // less than one move per game frame
11743     {
11744       int stepsize = TILEX / move_delay_value;
11745       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11746       int count = (stored_player[i].is_moving ?
11747                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11748
11749       if (count % delay == 0)
11750         move_frames = 1;
11751     }
11752
11753     stored_player[i].Frame += move_frames;
11754
11755     if (stored_player[i].MovPos != 0)
11756       stored_player[i].StepFrame += move_frames;
11757
11758     if (stored_player[i].move_delay > 0)
11759       stored_player[i].move_delay--;
11760
11761     // due to bugs in previous versions, counter must count up, not down
11762     if (stored_player[i].push_delay != -1)
11763       stored_player[i].push_delay++;
11764
11765     if (stored_player[i].drop_delay > 0)
11766       stored_player[i].drop_delay--;
11767
11768     if (stored_player[i].is_dropping_pressed)
11769       stored_player[i].drop_pressed_delay++;
11770   }
11771 }
11772
11773 void StartGameActions(boolean init_network_game, boolean record_tape,
11774                       int random_seed)
11775 {
11776   unsigned int new_random_seed = InitRND(random_seed);
11777
11778   if (record_tape)
11779     TapeStartRecording(new_random_seed);
11780
11781   if (setup.auto_pause_on_start && !tape.pausing)
11782     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11783
11784   if (init_network_game)
11785   {
11786     SendToServer_LevelFile();
11787     SendToServer_StartPlaying();
11788
11789     return;
11790   }
11791
11792   InitGame();
11793 }
11794
11795 static void GameActionsExt(void)
11796 {
11797 #if 0
11798   static unsigned int game_frame_delay = 0;
11799 #endif
11800   unsigned int game_frame_delay_value;
11801   byte *recorded_player_action;
11802   byte summarized_player_action = 0;
11803   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11804   int i;
11805
11806   // detect endless loops, caused by custom element programming
11807   if (recursion_loop_detected && recursion_loop_depth == 0)
11808   {
11809     char *message = getStringCat3("Internal Error! Element ",
11810                                   EL_NAME(recursion_loop_element),
11811                                   " caused endless loop! Quit the game?");
11812
11813     Warn("element '%s' caused endless loop in game engine",
11814          EL_NAME(recursion_loop_element));
11815
11816     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11817
11818     recursion_loop_detected = FALSE;    // if game should be continued
11819
11820     free(message);
11821
11822     return;
11823   }
11824
11825   if (game.restart_level)
11826     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11827
11828   CheckLevelSolved();
11829
11830   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11831     GameWon();
11832
11833   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11834     TapeStop();
11835
11836   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11837     return;
11838
11839   game_frame_delay_value =
11840     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11841
11842   if (tape.playing && tape.warp_forward && !tape.pausing)
11843     game_frame_delay_value = 0;
11844
11845   SetVideoFrameDelay(game_frame_delay_value);
11846
11847   // (de)activate virtual buttons depending on current game status
11848   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11849   {
11850     if (game.all_players_gone)  // if no players there to be controlled anymore
11851       SetOverlayActive(FALSE);
11852     else if (!tape.playing)     // if game continues after tape stopped playing
11853       SetOverlayActive(TRUE);
11854   }
11855
11856 #if 0
11857 #if 0
11858   // ---------- main game synchronization point ----------
11859
11860   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11861
11862   Debug("game:playing:skip", "skip == %d", skip);
11863
11864 #else
11865   // ---------- main game synchronization point ----------
11866
11867   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11868 #endif
11869 #endif
11870
11871   if (network_playing && !network_player_action_received)
11872   {
11873     // try to get network player actions in time
11874
11875     // last chance to get network player actions without main loop delay
11876     HandleNetworking();
11877
11878     // game was quit by network peer
11879     if (game_status != GAME_MODE_PLAYING)
11880       return;
11881
11882     // check if network player actions still missing and game still running
11883     if (!network_player_action_received && !checkGameEnded())
11884       return;           // failed to get network player actions in time
11885
11886     // do not yet reset "network_player_action_received" (for tape.pausing)
11887   }
11888
11889   if (tape.pausing)
11890     return;
11891
11892   // at this point we know that we really continue executing the game
11893
11894   network_player_action_received = FALSE;
11895
11896   // when playing tape, read previously recorded player input from tape data
11897   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11898
11899   local_player->effective_mouse_action = local_player->mouse_action;
11900
11901   if (recorded_player_action != NULL)
11902     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11903                                  recorded_player_action);
11904
11905   // TapePlayAction() may return NULL when toggling to "pause before death"
11906   if (tape.pausing)
11907     return;
11908
11909   if (tape.set_centered_player)
11910   {
11911     game.centered_player_nr_next = tape.centered_player_nr_next;
11912     game.set_centered_player = TRUE;
11913   }
11914
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916   {
11917     summarized_player_action |= stored_player[i].action;
11918
11919     if (!network_playing && (game.team_mode || tape.playing))
11920       stored_player[i].effective_action = stored_player[i].action;
11921   }
11922
11923   if (network_playing && !checkGameEnded())
11924     SendToServer_MovePlayer(summarized_player_action);
11925
11926   // summarize all actions at local players mapped input device position
11927   // (this allows using different input devices in single player mode)
11928   if (!network.enabled && !game.team_mode)
11929     stored_player[map_player_action[local_player->index_nr]].effective_action =
11930       summarized_player_action;
11931
11932   // summarize all actions at centered player in local team mode
11933   if (tape.recording &&
11934       setup.team_mode && !network.enabled &&
11935       setup.input_on_focus &&
11936       game.centered_player_nr != -1)
11937   {
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       stored_player[map_player_action[i]].effective_action =
11940         (i == game.centered_player_nr ? summarized_player_action : 0);
11941   }
11942
11943   if (recorded_player_action != NULL)
11944     for (i = 0; i < MAX_PLAYERS; i++)
11945       stored_player[i].effective_action = recorded_player_action[i];
11946
11947   for (i = 0; i < MAX_PLAYERS; i++)
11948   {
11949     tape_action[i] = stored_player[i].effective_action;
11950
11951     /* (this may happen in the RND game engine if a player was not present on
11952        the playfield on level start, but appeared later from a custom element */
11953     if (setup.team_mode &&
11954         tape.recording &&
11955         tape_action[i] &&
11956         !tape.player_participates[i])
11957       tape.player_participates[i] = TRUE;
11958   }
11959
11960   SetTapeActionFromMouseAction(tape_action,
11961                                &local_player->effective_mouse_action);
11962
11963   // only record actions from input devices, but not programmed actions
11964   if (tape.recording)
11965     TapeRecordAction(tape_action);
11966
11967   // remember if game was played (especially after tape stopped playing)
11968   if (!tape.playing && summarized_player_action)
11969     game.GamePlayed = TRUE;
11970
11971 #if USE_NEW_PLAYER_ASSIGNMENTS
11972   // !!! also map player actions in single player mode !!!
11973   // if (game.team_mode)
11974   if (1)
11975   {
11976     byte mapped_action[MAX_PLAYERS];
11977
11978 #if DEBUG_PLAYER_ACTIONS
11979     for (i = 0; i < MAX_PLAYERS; i++)
11980       DebugContinued("", "%d, ", stored_player[i].effective_action);
11981 #endif
11982
11983     for (i = 0; i < MAX_PLAYERS; i++)
11984       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11985
11986     for (i = 0; i < MAX_PLAYERS; i++)
11987       stored_player[i].effective_action = mapped_action[i];
11988
11989 #if DEBUG_PLAYER_ACTIONS
11990     DebugContinued("", "=> ");
11991     for (i = 0; i < MAX_PLAYERS; i++)
11992       DebugContinued("", "%d, ", stored_player[i].effective_action);
11993     DebugContinued("game:playing:player", "\n");
11994 #endif
11995   }
11996 #if DEBUG_PLAYER_ACTIONS
11997   else
11998   {
11999     for (i = 0; i < MAX_PLAYERS; i++)
12000       DebugContinued("", "%d, ", stored_player[i].effective_action);
12001     DebugContinued("game:playing:player", "\n");
12002   }
12003 #endif
12004 #endif
12005
12006   for (i = 0; i < MAX_PLAYERS; i++)
12007   {
12008     // allow engine snapshot in case of changed movement attempt
12009     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12010         (stored_player[i].effective_action & KEY_MOTION))
12011       game.snapshot.changed_action = TRUE;
12012
12013     // allow engine snapshot in case of snapping/dropping attempt
12014     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12015         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12016       game.snapshot.changed_action = TRUE;
12017
12018     game.snapshot.last_action[i] = stored_player[i].effective_action;
12019   }
12020
12021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12022   {
12023     GameActions_EM_Main();
12024   }
12025   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12026   {
12027     GameActions_SP_Main();
12028   }
12029   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12030   {
12031     GameActions_MM_Main();
12032   }
12033   else
12034   {
12035     GameActions_RND_Main();
12036   }
12037
12038   BlitScreenToBitmap(backbuffer);
12039
12040   CheckLevelSolved();
12041   CheckLevelTime();
12042
12043   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12044
12045   if (global.show_frames_per_second)
12046   {
12047     static unsigned int fps_counter = 0;
12048     static int fps_frames = 0;
12049     unsigned int fps_delay_ms = Counter() - fps_counter;
12050
12051     fps_frames++;
12052
12053     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12054     {
12055       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12056
12057       fps_frames = 0;
12058       fps_counter = Counter();
12059
12060       // always draw FPS to screen after FPS value was updated
12061       redraw_mask |= REDRAW_FPS;
12062     }
12063
12064     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12065     if (GetDrawDeactivationMask() == REDRAW_NONE)
12066       redraw_mask |= REDRAW_FPS;
12067   }
12068 }
12069
12070 static void GameActions_CheckSaveEngineSnapshot(void)
12071 {
12072   if (!game.snapshot.save_snapshot)
12073     return;
12074
12075   // clear flag for saving snapshot _before_ saving snapshot
12076   game.snapshot.save_snapshot = FALSE;
12077
12078   SaveEngineSnapshotToList();
12079 }
12080
12081 void GameActions(void)
12082 {
12083   GameActionsExt();
12084
12085   GameActions_CheckSaveEngineSnapshot();
12086 }
12087
12088 void GameActions_EM_Main(void)
12089 {
12090   byte effective_action[MAX_PLAYERS];
12091   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12092   int i;
12093
12094   for (i = 0; i < MAX_PLAYERS; i++)
12095     effective_action[i] = stored_player[i].effective_action;
12096
12097   GameActions_EM(effective_action, warp_mode);
12098 }
12099
12100 void GameActions_SP_Main(void)
12101 {
12102   byte effective_action[MAX_PLAYERS];
12103   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12104   int i;
12105
12106   for (i = 0; i < MAX_PLAYERS; i++)
12107     effective_action[i] = stored_player[i].effective_action;
12108
12109   GameActions_SP(effective_action, warp_mode);
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112   {
12113     if (stored_player[i].force_dropping)
12114       stored_player[i].action |= KEY_BUTTON_DROP;
12115
12116     stored_player[i].force_dropping = FALSE;
12117   }
12118 }
12119
12120 void GameActions_MM_Main(void)
12121 {
12122   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12123
12124   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12125 }
12126
12127 void GameActions_RND_Main(void)
12128 {
12129   GameActions_RND();
12130 }
12131
12132 void GameActions_RND(void)
12133 {
12134   static struct MouseActionInfo mouse_action_last = { 0 };
12135   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12136   int magic_wall_x = 0, magic_wall_y = 0;
12137   int i, x, y, element, graphic, last_gfx_frame;
12138
12139   InitPlayfieldScanModeVars();
12140
12141   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12142   {
12143     SCAN_PLAYFIELD(x, y)
12144     {
12145       ChangeCount[x][y] = 0;
12146       ChangeEvent[x][y] = -1;
12147     }
12148   }
12149
12150   if (game.set_centered_player)
12151   {
12152     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12153
12154     // switching to "all players" only possible if all players fit to screen
12155     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12156     {
12157       game.centered_player_nr_next = game.centered_player_nr;
12158       game.set_centered_player = FALSE;
12159     }
12160
12161     // do not switch focus to non-existing (or non-active) player
12162     if (game.centered_player_nr_next >= 0 &&
12163         !stored_player[game.centered_player_nr_next].active)
12164     {
12165       game.centered_player_nr_next = game.centered_player_nr;
12166       game.set_centered_player = FALSE;
12167     }
12168   }
12169
12170   if (game.set_centered_player &&
12171       ScreenMovPos == 0)        // screen currently aligned at tile position
12172   {
12173     int sx, sy;
12174
12175     if (game.centered_player_nr_next == -1)
12176     {
12177       setScreenCenteredToAllPlayers(&sx, &sy);
12178     }
12179     else
12180     {
12181       sx = stored_player[game.centered_player_nr_next].jx;
12182       sy = stored_player[game.centered_player_nr_next].jy;
12183     }
12184
12185     game.centered_player_nr = game.centered_player_nr_next;
12186     game.set_centered_player = FALSE;
12187
12188     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12189     DrawGameDoorValues();
12190   }
12191
12192   // check single step mode (set flag and clear again if any player is active)
12193   game.enter_single_step_mode =
12194     (tape.single_step && tape.recording && !tape.pausing);
12195
12196   for (i = 0; i < MAX_PLAYERS; i++)
12197   {
12198     int actual_player_action = stored_player[i].effective_action;
12199
12200 #if 1
12201     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12202        - rnd_equinox_tetrachloride 048
12203        - rnd_equinox_tetrachloride_ii 096
12204        - rnd_emanuel_schmieg 002
12205        - doctor_sloan_ww 001, 020
12206     */
12207     if (stored_player[i].MovPos == 0)
12208       CheckGravityMovement(&stored_player[i]);
12209 #endif
12210
12211     // overwrite programmed action with tape action
12212     if (stored_player[i].programmed_action)
12213       actual_player_action = stored_player[i].programmed_action;
12214
12215     PlayerActions(&stored_player[i], actual_player_action);
12216
12217     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12218   }
12219
12220   // single step pause mode may already have been toggled by "ScrollPlayer()"
12221   if (game.enter_single_step_mode && !tape.pausing)
12222     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12223
12224   ScrollScreen(NULL, SCROLL_GO_ON);
12225
12226   /* for backwards compatibility, the following code emulates a fixed bug that
12227      occured when pushing elements (causing elements that just made their last
12228      pushing step to already (if possible) make their first falling step in the
12229      same game frame, which is bad); this code is also needed to use the famous
12230      "spring push bug" which is used in older levels and might be wanted to be
12231      used also in newer levels, but in this case the buggy pushing code is only
12232      affecting the "spring" element and no other elements */
12233
12234   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12235   {
12236     for (i = 0; i < MAX_PLAYERS; i++)
12237     {
12238       struct PlayerInfo *player = &stored_player[i];
12239       int x = player->jx;
12240       int y = player->jy;
12241
12242       if (player->active && player->is_pushing && player->is_moving &&
12243           IS_MOVING(x, y) &&
12244           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12245            Tile[x][y] == EL_SPRING))
12246       {
12247         ContinueMoving(x, y);
12248
12249         // continue moving after pushing (this is actually a bug)
12250         if (!IS_MOVING(x, y))
12251           Stop[x][y] = FALSE;
12252       }
12253     }
12254   }
12255
12256   SCAN_PLAYFIELD(x, y)
12257   {
12258     Last[x][y] = Tile[x][y];
12259
12260     ChangeCount[x][y] = 0;
12261     ChangeEvent[x][y] = -1;
12262
12263     // this must be handled before main playfield loop
12264     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12265     {
12266       MovDelay[x][y]--;
12267       if (MovDelay[x][y] <= 0)
12268         RemoveField(x, y);
12269     }
12270
12271     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12272     {
12273       MovDelay[x][y]--;
12274       if (MovDelay[x][y] <= 0)
12275       {
12276         int element = Store[x][y];
12277         int move_direction = MovDir[x][y];
12278         int player_index_bit = Store2[x][y];
12279
12280         Store[x][y] = 0;
12281         Store2[x][y] = 0;
12282
12283         RemoveField(x, y);
12284         TEST_DrawLevelField(x, y);
12285
12286         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12287
12288         if (IS_ENVELOPE(element))
12289           local_player->show_envelope = element;
12290       }
12291     }
12292
12293 #if DEBUG
12294     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12295     {
12296       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12297             x, y);
12298       Debug("game:playing:GameActions_RND", "This should never happen!");
12299
12300       ChangePage[x][y] = -1;
12301     }
12302 #endif
12303
12304     Stop[x][y] = FALSE;
12305     if (WasJustMoving[x][y] > 0)
12306       WasJustMoving[x][y]--;
12307     if (WasJustFalling[x][y] > 0)
12308       WasJustFalling[x][y]--;
12309     if (CheckCollision[x][y] > 0)
12310       CheckCollision[x][y]--;
12311     if (CheckImpact[x][y] > 0)
12312       CheckImpact[x][y]--;
12313
12314     GfxFrame[x][y]++;
12315
12316     /* reset finished pushing action (not done in ContinueMoving() to allow
12317        continuous pushing animation for elements with zero push delay) */
12318     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12319     {
12320       ResetGfxAnimation(x, y);
12321       TEST_DrawLevelField(x, y);
12322     }
12323
12324 #if DEBUG
12325     if (IS_BLOCKED(x, y))
12326     {
12327       int oldx, oldy;
12328
12329       Blocked2Moving(x, y, &oldx, &oldy);
12330       if (!IS_MOVING(oldx, oldy))
12331       {
12332         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12333         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12334         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12335         Debug("game:playing:GameActions_RND", "This should never happen!");
12336       }
12337     }
12338 #endif
12339   }
12340
12341   if (mouse_action.button)
12342   {
12343     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12344     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12345
12346     x = mouse_action.lx;
12347     y = mouse_action.ly;
12348     element = Tile[x][y];
12349
12350     if (new_button)
12351     {
12352       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12353       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12354                                          ch_button);
12355     }
12356
12357     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12358     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12359                                        ch_button);
12360
12361     if (level.use_step_counter)
12362     {
12363       boolean counted_click = FALSE;
12364
12365       // element clicked that can change when clicked/pressed
12366       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12367           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12368            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12369         counted_click = TRUE;
12370
12371       // element clicked that can trigger change when clicked/pressed
12372       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12373           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12374         counted_click = TRUE;
12375
12376       if (new_button && counted_click)
12377         CheckLevelTime_StepCounter();
12378     }
12379   }
12380
12381   SCAN_PLAYFIELD(x, y)
12382   {
12383     element = Tile[x][y];
12384     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12385     last_gfx_frame = GfxFrame[x][y];
12386
12387     if (element == EL_EMPTY)
12388       graphic = el2img(GfxElementEmpty[x][y]);
12389
12390     ResetGfxFrame(x, y);
12391
12392     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12393       DrawLevelGraphicAnimation(x, y, graphic);
12394
12395     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12396         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12397       ResetRandomAnimationValue(x, y);
12398
12399     SetRandomAnimationValue(x, y);
12400
12401     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12402
12403     if (IS_INACTIVE(element))
12404     {
12405       if (IS_ANIMATED(graphic))
12406         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12407
12408       continue;
12409     }
12410
12411     // this may take place after moving, so 'element' may have changed
12412     if (IS_CHANGING(x, y) &&
12413         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12414     {
12415       int page = element_info[element].event_page_nr[CE_DELAY];
12416
12417       HandleElementChange(x, y, page);
12418
12419       element = Tile[x][y];
12420       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12421     }
12422
12423     CheckNextToConditions(x, y);
12424
12425     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12426     {
12427       StartMoving(x, y);
12428
12429       element = Tile[x][y];
12430       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12431
12432       if (IS_ANIMATED(graphic) &&
12433           !IS_MOVING(x, y) &&
12434           !Stop[x][y])
12435         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12436
12437       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12438         TEST_DrawTwinkleOnField(x, y);
12439     }
12440     else if (element == EL_ACID)
12441     {
12442       if (!Stop[x][y])
12443         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12444     }
12445     else if ((element == EL_EXIT_OPEN ||
12446               element == EL_EM_EXIT_OPEN ||
12447               element == EL_SP_EXIT_OPEN ||
12448               element == EL_STEEL_EXIT_OPEN ||
12449               element == EL_EM_STEEL_EXIT_OPEN ||
12450               element == EL_SP_TERMINAL ||
12451               element == EL_SP_TERMINAL_ACTIVE ||
12452               element == EL_EXTRA_TIME ||
12453               element == EL_SHIELD_NORMAL ||
12454               element == EL_SHIELD_DEADLY) &&
12455              IS_ANIMATED(graphic))
12456       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12457     else if (IS_MOVING(x, y))
12458       ContinueMoving(x, y);
12459     else if (IS_ACTIVE_BOMB(element))
12460       CheckDynamite(x, y);
12461     else if (element == EL_AMOEBA_GROWING)
12462       AmoebaGrowing(x, y);
12463     else if (element == EL_AMOEBA_SHRINKING)
12464       AmoebaShrinking(x, y);
12465
12466 #if !USE_NEW_AMOEBA_CODE
12467     else if (IS_AMOEBALIVE(element))
12468       AmoebaReproduce(x, y);
12469 #endif
12470
12471     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12472       Life(x, y);
12473     else if (element == EL_EXIT_CLOSED)
12474       CheckExit(x, y);
12475     else if (element == EL_EM_EXIT_CLOSED)
12476       CheckExitEM(x, y);
12477     else if (element == EL_STEEL_EXIT_CLOSED)
12478       CheckExitSteel(x, y);
12479     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12480       CheckExitSteelEM(x, y);
12481     else if (element == EL_SP_EXIT_CLOSED)
12482       CheckExitSP(x, y);
12483     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12484              element == EL_EXPANDABLE_STEELWALL_GROWING)
12485       MauerWaechst(x, y);
12486     else if (element == EL_EXPANDABLE_WALL ||
12487              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12488              element == EL_EXPANDABLE_WALL_VERTICAL ||
12489              element == EL_EXPANDABLE_WALL_ANY ||
12490              element == EL_BD_EXPANDABLE_WALL)
12491       MauerAbleger(x, y);
12492     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12493              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12494              element == EL_EXPANDABLE_STEELWALL_ANY)
12495       MauerAblegerStahl(x, y);
12496     else if (element == EL_FLAMES)
12497       CheckForDragon(x, y);
12498     else if (element == EL_EXPLOSION)
12499       ; // drawing of correct explosion animation is handled separately
12500     else if (element == EL_ELEMENT_SNAPPING ||
12501              element == EL_DIAGONAL_SHRINKING ||
12502              element == EL_DIAGONAL_GROWING)
12503     {
12504       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12505
12506       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12507     }
12508     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12509       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12510
12511     if (IS_BELT_ACTIVE(element))
12512       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12513
12514     if (game.magic_wall_active)
12515     {
12516       int jx = local_player->jx, jy = local_player->jy;
12517
12518       // play the element sound at the position nearest to the player
12519       if ((element == EL_MAGIC_WALL_FULL ||
12520            element == EL_MAGIC_WALL_ACTIVE ||
12521            element == EL_MAGIC_WALL_EMPTYING ||
12522            element == EL_BD_MAGIC_WALL_FULL ||
12523            element == EL_BD_MAGIC_WALL_ACTIVE ||
12524            element == EL_BD_MAGIC_WALL_EMPTYING ||
12525            element == EL_DC_MAGIC_WALL_FULL ||
12526            element == EL_DC_MAGIC_WALL_ACTIVE ||
12527            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12528           ABS(x - jx) + ABS(y - jy) <
12529           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12530       {
12531         magic_wall_x = x;
12532         magic_wall_y = y;
12533       }
12534     }
12535   }
12536
12537 #if USE_NEW_AMOEBA_CODE
12538   // new experimental amoeba growth stuff
12539   if (!(FrameCounter % 8))
12540   {
12541     static unsigned int random = 1684108901;
12542
12543     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12544     {
12545       x = RND(lev_fieldx);
12546       y = RND(lev_fieldy);
12547       element = Tile[x][y];
12548
12549       if (!IS_PLAYER(x,y) &&
12550           (element == EL_EMPTY ||
12551            CAN_GROW_INTO(element) ||
12552            element == EL_QUICKSAND_EMPTY ||
12553            element == EL_QUICKSAND_FAST_EMPTY ||
12554            element == EL_ACID_SPLASH_LEFT ||
12555            element == EL_ACID_SPLASH_RIGHT))
12556       {
12557         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12558             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12559             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12560             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12561           Tile[x][y] = EL_AMOEBA_DROP;
12562       }
12563
12564       random = random * 129 + 1;
12565     }
12566   }
12567 #endif
12568
12569   game.explosions_delayed = FALSE;
12570
12571   SCAN_PLAYFIELD(x, y)
12572   {
12573     element = Tile[x][y];
12574
12575     if (ExplodeField[x][y])
12576       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12577     else if (element == EL_EXPLOSION)
12578       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12579
12580     ExplodeField[x][y] = EX_TYPE_NONE;
12581   }
12582
12583   game.explosions_delayed = TRUE;
12584
12585   if (game.magic_wall_active)
12586   {
12587     if (!(game.magic_wall_time_left % 4))
12588     {
12589       int element = Tile[magic_wall_x][magic_wall_y];
12590
12591       if (element == EL_BD_MAGIC_WALL_FULL ||
12592           element == EL_BD_MAGIC_WALL_ACTIVE ||
12593           element == EL_BD_MAGIC_WALL_EMPTYING)
12594         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12595       else if (element == EL_DC_MAGIC_WALL_FULL ||
12596                element == EL_DC_MAGIC_WALL_ACTIVE ||
12597                element == EL_DC_MAGIC_WALL_EMPTYING)
12598         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12599       else
12600         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12601     }
12602
12603     if (game.magic_wall_time_left > 0)
12604     {
12605       game.magic_wall_time_left--;
12606
12607       if (!game.magic_wall_time_left)
12608       {
12609         SCAN_PLAYFIELD(x, y)
12610         {
12611           element = Tile[x][y];
12612
12613           if (element == EL_MAGIC_WALL_ACTIVE ||
12614               element == EL_MAGIC_WALL_FULL)
12615           {
12616             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12617             TEST_DrawLevelField(x, y);
12618           }
12619           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12620                    element == EL_BD_MAGIC_WALL_FULL)
12621           {
12622             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12623             TEST_DrawLevelField(x, y);
12624           }
12625           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12626                    element == EL_DC_MAGIC_WALL_FULL)
12627           {
12628             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12629             TEST_DrawLevelField(x, y);
12630           }
12631         }
12632
12633         game.magic_wall_active = FALSE;
12634       }
12635     }
12636   }
12637
12638   if (game.light_time_left > 0)
12639   {
12640     game.light_time_left--;
12641
12642     if (game.light_time_left == 0)
12643       RedrawAllLightSwitchesAndInvisibleElements();
12644   }
12645
12646   if (game.timegate_time_left > 0)
12647   {
12648     game.timegate_time_left--;
12649
12650     if (game.timegate_time_left == 0)
12651       CloseAllOpenTimegates();
12652   }
12653
12654   if (game.lenses_time_left > 0)
12655   {
12656     game.lenses_time_left--;
12657
12658     if (game.lenses_time_left == 0)
12659       RedrawAllInvisibleElementsForLenses();
12660   }
12661
12662   if (game.magnify_time_left > 0)
12663   {
12664     game.magnify_time_left--;
12665
12666     if (game.magnify_time_left == 0)
12667       RedrawAllInvisibleElementsForMagnifier();
12668   }
12669
12670   for (i = 0; i < MAX_PLAYERS; i++)
12671   {
12672     struct PlayerInfo *player = &stored_player[i];
12673
12674     if (SHIELD_ON(player))
12675     {
12676       if (player->shield_deadly_time_left)
12677         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12678       else if (player->shield_normal_time_left)
12679         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12680     }
12681   }
12682
12683 #if USE_DELAYED_GFX_REDRAW
12684   SCAN_PLAYFIELD(x, y)
12685   {
12686     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12687     {
12688       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12689          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12690
12691       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12692         DrawLevelField(x, y);
12693
12694       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12695         DrawLevelFieldCrumbled(x, y);
12696
12697       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12698         DrawLevelFieldCrumbledNeighbours(x, y);
12699
12700       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12701         DrawTwinkleOnField(x, y);
12702     }
12703
12704     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12705   }
12706 #endif
12707
12708   DrawAllPlayers();
12709   PlayAllPlayersSound();
12710
12711   for (i = 0; i < MAX_PLAYERS; i++)
12712   {
12713     struct PlayerInfo *player = &stored_player[i];
12714
12715     if (player->show_envelope != 0 && (!player->active ||
12716                                        player->MovPos == 0))
12717     {
12718       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12719
12720       player->show_envelope = 0;
12721     }
12722   }
12723
12724   // use random number generator in every frame to make it less predictable
12725   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12726     RND(1);
12727
12728   mouse_action_last = mouse_action;
12729 }
12730
12731 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12732 {
12733   int min_x = x, min_y = y, max_x = x, max_y = y;
12734   int scr_fieldx = getScreenFieldSizeX();
12735   int scr_fieldy = getScreenFieldSizeY();
12736   int i;
12737
12738   for (i = 0; i < MAX_PLAYERS; i++)
12739   {
12740     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12741
12742     if (!stored_player[i].active || &stored_player[i] == player)
12743       continue;
12744
12745     min_x = MIN(min_x, jx);
12746     min_y = MIN(min_y, jy);
12747     max_x = MAX(max_x, jx);
12748     max_y = MAX(max_y, jy);
12749   }
12750
12751   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12752 }
12753
12754 static boolean AllPlayersInVisibleScreen(void)
12755 {
12756   int i;
12757
12758   for (i = 0; i < MAX_PLAYERS; i++)
12759   {
12760     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12761
12762     if (!stored_player[i].active)
12763       continue;
12764
12765     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12766       return FALSE;
12767   }
12768
12769   return TRUE;
12770 }
12771
12772 void ScrollLevel(int dx, int dy)
12773 {
12774   int scroll_offset = 2 * TILEX_VAR;
12775   int x, y;
12776
12777   BlitBitmap(drawto_field, drawto_field,
12778              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12779              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12780              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12781              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12782              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12783              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12784
12785   if (dx != 0)
12786   {
12787     x = (dx == 1 ? BX1 : BX2);
12788     for (y = BY1; y <= BY2; y++)
12789       DrawScreenField(x, y);
12790   }
12791
12792   if (dy != 0)
12793   {
12794     y = (dy == 1 ? BY1 : BY2);
12795     for (x = BX1; x <= BX2; x++)
12796       DrawScreenField(x, y);
12797   }
12798
12799   redraw_mask |= REDRAW_FIELD;
12800 }
12801
12802 static boolean canFallDown(struct PlayerInfo *player)
12803 {
12804   int jx = player->jx, jy = player->jy;
12805
12806   return (IN_LEV_FIELD(jx, jy + 1) &&
12807           (IS_FREE(jx, jy + 1) ||
12808            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12809           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12810           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12811 }
12812
12813 static boolean canPassField(int x, int y, int move_dir)
12814 {
12815   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12816   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12817   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12818   int nextx = x + dx;
12819   int nexty = y + dy;
12820   int element = Tile[x][y];
12821
12822   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12823           !CAN_MOVE(element) &&
12824           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12825           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12826           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12827 }
12828
12829 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12830 {
12831   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12832   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12833   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12834   int newx = x + dx;
12835   int newy = y + dy;
12836
12837   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12838           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12839           (IS_DIGGABLE(Tile[newx][newy]) ||
12840            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12841            canPassField(newx, newy, move_dir)));
12842 }
12843
12844 static void CheckGravityMovement(struct PlayerInfo *player)
12845 {
12846   if (player->gravity && !player->programmed_action)
12847   {
12848     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12849     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12850     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12851     int jx = player->jx, jy = player->jy;
12852     boolean player_is_moving_to_valid_field =
12853       (!player_is_snapping &&
12854        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12855         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12856     boolean player_can_fall_down = canFallDown(player);
12857
12858     if (player_can_fall_down &&
12859         !player_is_moving_to_valid_field)
12860       player->programmed_action = MV_DOWN;
12861   }
12862 }
12863
12864 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12865 {
12866   return CheckGravityMovement(player);
12867
12868   if (player->gravity && !player->programmed_action)
12869   {
12870     int jx = player->jx, jy = player->jy;
12871     boolean field_under_player_is_free =
12872       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12873     boolean player_is_standing_on_valid_field =
12874       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12875        (IS_WALKABLE(Tile[jx][jy]) &&
12876         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12877
12878     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12879       player->programmed_action = MV_DOWN;
12880   }
12881 }
12882
12883 /*
12884   MovePlayerOneStep()
12885   -----------------------------------------------------------------------------
12886   dx, dy:               direction (non-diagonal) to try to move the player to
12887   real_dx, real_dy:     direction as read from input device (can be diagonal)
12888 */
12889
12890 boolean MovePlayerOneStep(struct PlayerInfo *player,
12891                           int dx, int dy, int real_dx, int real_dy)
12892 {
12893   int jx = player->jx, jy = player->jy;
12894   int new_jx = jx + dx, new_jy = jy + dy;
12895   int can_move;
12896   boolean player_can_move = !player->cannot_move;
12897
12898   if (!player->active || (!dx && !dy))
12899     return MP_NO_ACTION;
12900
12901   player->MovDir = (dx < 0 ? MV_LEFT :
12902                     dx > 0 ? MV_RIGHT :
12903                     dy < 0 ? MV_UP :
12904                     dy > 0 ? MV_DOWN :  MV_NONE);
12905
12906   if (!IN_LEV_FIELD(new_jx, new_jy))
12907     return MP_NO_ACTION;
12908
12909   if (!player_can_move)
12910   {
12911     if (player->MovPos == 0)
12912     {
12913       player->is_moving = FALSE;
12914       player->is_digging = FALSE;
12915       player->is_collecting = FALSE;
12916       player->is_snapping = FALSE;
12917       player->is_pushing = FALSE;
12918     }
12919   }
12920
12921   if (!network.enabled && game.centered_player_nr == -1 &&
12922       !AllPlayersInSight(player, new_jx, new_jy))
12923     return MP_NO_ACTION;
12924
12925   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12926   if (can_move != MP_MOVING)
12927     return can_move;
12928
12929   // check if DigField() has caused relocation of the player
12930   if (player->jx != jx || player->jy != jy)
12931     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12932
12933   StorePlayer[jx][jy] = 0;
12934   player->last_jx = jx;
12935   player->last_jy = jy;
12936   player->jx = new_jx;
12937   player->jy = new_jy;
12938   StorePlayer[new_jx][new_jy] = player->element_nr;
12939
12940   if (player->move_delay_value_next != -1)
12941   {
12942     player->move_delay_value = player->move_delay_value_next;
12943     player->move_delay_value_next = -1;
12944   }
12945
12946   player->MovPos =
12947     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12948
12949   player->step_counter++;
12950
12951   PlayerVisit[jx][jy] = FrameCounter;
12952
12953   player->is_moving = TRUE;
12954
12955 #if 1
12956   // should better be called in MovePlayer(), but this breaks some tapes
12957   ScrollPlayer(player, SCROLL_INIT);
12958 #endif
12959
12960   return MP_MOVING;
12961 }
12962
12963 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12964 {
12965   int jx = player->jx, jy = player->jy;
12966   int old_jx = jx, old_jy = jy;
12967   int moved = MP_NO_ACTION;
12968
12969   if (!player->active)
12970     return FALSE;
12971
12972   if (!dx && !dy)
12973   {
12974     if (player->MovPos == 0)
12975     {
12976       player->is_moving = FALSE;
12977       player->is_digging = FALSE;
12978       player->is_collecting = FALSE;
12979       player->is_snapping = FALSE;
12980       player->is_pushing = FALSE;
12981     }
12982
12983     return FALSE;
12984   }
12985
12986   if (player->move_delay > 0)
12987     return FALSE;
12988
12989   player->move_delay = -1;              // set to "uninitialized" value
12990
12991   // store if player is automatically moved to next field
12992   player->is_auto_moving = (player->programmed_action != MV_NONE);
12993
12994   // remove the last programmed player action
12995   player->programmed_action = 0;
12996
12997   if (player->MovPos)
12998   {
12999     // should only happen if pre-1.2 tape recordings are played
13000     // this is only for backward compatibility
13001
13002     int original_move_delay_value = player->move_delay_value;
13003
13004 #if DEBUG
13005     Debug("game:playing:MovePlayer",
13006           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13007           tape.counter);
13008 #endif
13009
13010     // scroll remaining steps with finest movement resolution
13011     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13012
13013     while (player->MovPos)
13014     {
13015       ScrollPlayer(player, SCROLL_GO_ON);
13016       ScrollScreen(NULL, SCROLL_GO_ON);
13017
13018       AdvanceFrameAndPlayerCounters(player->index_nr);
13019
13020       DrawAllPlayers();
13021       BackToFront_WithFrameDelay(0);
13022     }
13023
13024     player->move_delay_value = original_move_delay_value;
13025   }
13026
13027   player->is_active = FALSE;
13028
13029   if (player->last_move_dir & MV_HORIZONTAL)
13030   {
13031     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13032       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13033   }
13034   else
13035   {
13036     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13037       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13038   }
13039
13040   if (!moved && !player->is_active)
13041   {
13042     player->is_moving = FALSE;
13043     player->is_digging = FALSE;
13044     player->is_collecting = FALSE;
13045     player->is_snapping = FALSE;
13046     player->is_pushing = FALSE;
13047   }
13048
13049   jx = player->jx;
13050   jy = player->jy;
13051
13052   if (moved & MP_MOVING && !ScreenMovPos &&
13053       (player->index_nr == game.centered_player_nr ||
13054        game.centered_player_nr == -1))
13055   {
13056     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13057
13058     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13059     {
13060       // actual player has left the screen -- scroll in that direction
13061       if (jx != old_jx)         // player has moved horizontally
13062         scroll_x += (jx - old_jx);
13063       else                      // player has moved vertically
13064         scroll_y += (jy - old_jy);
13065     }
13066     else
13067     {
13068       int offset_raw = game.scroll_delay_value;
13069
13070       if (jx != old_jx)         // player has moved horizontally
13071       {
13072         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13073         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13074         int new_scroll_x = jx - MIDPOSX + offset_x;
13075
13076         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13077             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13078           scroll_x = new_scroll_x;
13079
13080         // don't scroll over playfield boundaries
13081         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13082
13083         // don't scroll more than one field at a time
13084         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13085
13086         // don't scroll against the player's moving direction
13087         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13088             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13089           scroll_x = old_scroll_x;
13090       }
13091       else                      // player has moved vertically
13092       {
13093         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13094         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13095         int new_scroll_y = jy - MIDPOSY + offset_y;
13096
13097         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13098             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13099           scroll_y = new_scroll_y;
13100
13101         // don't scroll over playfield boundaries
13102         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13103
13104         // don't scroll more than one field at a time
13105         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13106
13107         // don't scroll against the player's moving direction
13108         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13109             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13110           scroll_y = old_scroll_y;
13111       }
13112     }
13113
13114     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13115     {
13116       if (!network.enabled && game.centered_player_nr == -1 &&
13117           !AllPlayersInVisibleScreen())
13118       {
13119         scroll_x = old_scroll_x;
13120         scroll_y = old_scroll_y;
13121       }
13122       else
13123       {
13124         ScrollScreen(player, SCROLL_INIT);
13125         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13126       }
13127     }
13128   }
13129
13130   player->StepFrame = 0;
13131
13132   if (moved & MP_MOVING)
13133   {
13134     if (old_jx != jx && old_jy == jy)
13135       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13136     else if (old_jx == jx && old_jy != jy)
13137       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13138
13139     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13140
13141     player->last_move_dir = player->MovDir;
13142     player->is_moving = TRUE;
13143     player->is_snapping = FALSE;
13144     player->is_switching = FALSE;
13145     player->is_dropping = FALSE;
13146     player->is_dropping_pressed = FALSE;
13147     player->drop_pressed_delay = 0;
13148
13149 #if 0
13150     // should better be called here than above, but this breaks some tapes
13151     ScrollPlayer(player, SCROLL_INIT);
13152 #endif
13153   }
13154   else
13155   {
13156     CheckGravityMovementWhenNotMoving(player);
13157
13158     player->is_moving = FALSE;
13159
13160     /* at this point, the player is allowed to move, but cannot move right now
13161        (e.g. because of something blocking the way) -- ensure that the player
13162        is also allowed to move in the next frame (in old versions before 3.1.1,
13163        the player was forced to wait again for eight frames before next try) */
13164
13165     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13166       player->move_delay = 0;   // allow direct movement in the next frame
13167   }
13168
13169   if (player->move_delay == -1)         // not yet initialized by DigField()
13170     player->move_delay = player->move_delay_value;
13171
13172   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13173   {
13174     TestIfPlayerTouchesBadThing(jx, jy);
13175     TestIfPlayerTouchesCustomElement(jx, jy);
13176   }
13177
13178   if (!player->active)
13179     RemovePlayer(player);
13180
13181   return moved;
13182 }
13183
13184 void ScrollPlayer(struct PlayerInfo *player, int mode)
13185 {
13186   int jx = player->jx, jy = player->jy;
13187   int last_jx = player->last_jx, last_jy = player->last_jy;
13188   int move_stepsize = TILEX / player->move_delay_value;
13189
13190   if (!player->active)
13191     return;
13192
13193   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13194     return;
13195
13196   if (mode == SCROLL_INIT)
13197   {
13198     player->actual_frame_counter = FrameCounter;
13199     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13200
13201     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13202         Tile[last_jx][last_jy] == EL_EMPTY)
13203     {
13204       int last_field_block_delay = 0;   // start with no blocking at all
13205       int block_delay_adjustment = player->block_delay_adjustment;
13206
13207       // if player blocks last field, add delay for exactly one move
13208       if (player->block_last_field)
13209       {
13210         last_field_block_delay += player->move_delay_value;
13211
13212         // when blocking enabled, prevent moving up despite gravity
13213         if (player->gravity && player->MovDir == MV_UP)
13214           block_delay_adjustment = -1;
13215       }
13216
13217       // add block delay adjustment (also possible when not blocking)
13218       last_field_block_delay += block_delay_adjustment;
13219
13220       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13221       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13222     }
13223
13224     if (player->MovPos != 0)    // player has not yet reached destination
13225       return;
13226   }
13227   else if (!FrameReached(&player->actual_frame_counter, 1))
13228     return;
13229
13230   if (player->MovPos != 0)
13231   {
13232     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13233     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13234
13235     // before DrawPlayer() to draw correct player graphic for this case
13236     if (player->MovPos == 0)
13237       CheckGravityMovement(player);
13238   }
13239
13240   if (player->MovPos == 0)      // player reached destination field
13241   {
13242     if (player->move_delay_reset_counter > 0)
13243     {
13244       player->move_delay_reset_counter--;
13245
13246       if (player->move_delay_reset_counter == 0)
13247       {
13248         // continue with normal speed after quickly moving through gate
13249         HALVE_PLAYER_SPEED(player);
13250
13251         // be able to make the next move without delay
13252         player->move_delay = 0;
13253       }
13254     }
13255
13256     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13257         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13258         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13259         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13260         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13261         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13262         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13263         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13264     {
13265       ExitPlayer(player);
13266
13267       if (game.players_still_needed == 0 &&
13268           (game.friends_still_needed == 0 ||
13269            IS_SP_ELEMENT(Tile[jx][jy])))
13270         LevelSolved();
13271     }
13272
13273     player->last_jx = jx;
13274     player->last_jy = jy;
13275
13276     // this breaks one level: "machine", level 000
13277     {
13278       int move_direction = player->MovDir;
13279       int enter_side = MV_DIR_OPPOSITE(move_direction);
13280       int leave_side = move_direction;
13281       int old_jx = last_jx;
13282       int old_jy = last_jy;
13283       int old_element = Tile[old_jx][old_jy];
13284       int new_element = Tile[jx][jy];
13285
13286       if (IS_CUSTOM_ELEMENT(old_element))
13287         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13288                                    CE_LEFT_BY_PLAYER,
13289                                    player->index_bit, leave_side);
13290
13291       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13292                                           CE_PLAYER_LEAVES_X,
13293                                           player->index_bit, leave_side);
13294
13295       if (IS_CUSTOM_ELEMENT(new_element))
13296         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13297                                    player->index_bit, enter_side);
13298
13299       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13300                                           CE_PLAYER_ENTERS_X,
13301                                           player->index_bit, enter_side);
13302
13303       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13304                                         CE_MOVE_OF_X, move_direction);
13305     }
13306
13307     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13308     {
13309       TestIfPlayerTouchesBadThing(jx, jy);
13310       TestIfPlayerTouchesCustomElement(jx, jy);
13311
13312       /* needed because pushed element has not yet reached its destination,
13313          so it would trigger a change event at its previous field location */
13314       if (!player->is_pushing)
13315         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13316
13317       if (level.finish_dig_collect &&
13318           (player->is_digging || player->is_collecting))
13319       {
13320         int last_element = player->last_removed_element;
13321         int move_direction = player->MovDir;
13322         int enter_side = MV_DIR_OPPOSITE(move_direction);
13323         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13324                             CE_PLAYER_COLLECTS_X);
13325
13326         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13327                                             player->index_bit, enter_side);
13328
13329         player->last_removed_element = EL_UNDEFINED;
13330       }
13331
13332       if (!player->active)
13333         RemovePlayer(player);
13334     }
13335
13336     if (level.use_step_counter)
13337       CheckLevelTime_StepCounter();
13338
13339     if (tape.single_step && tape.recording && !tape.pausing &&
13340         !player->programmed_action)
13341       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13342
13343     if (!player->programmed_action)
13344       CheckSaveEngineSnapshot(player);
13345   }
13346 }
13347
13348 void ScrollScreen(struct PlayerInfo *player, int mode)
13349 {
13350   static unsigned int screen_frame_counter = 0;
13351
13352   if (mode == SCROLL_INIT)
13353   {
13354     // set scrolling step size according to actual player's moving speed
13355     ScrollStepSize = TILEX / player->move_delay_value;
13356
13357     screen_frame_counter = FrameCounter;
13358     ScreenMovDir = player->MovDir;
13359     ScreenMovPos = player->MovPos;
13360     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13361     return;
13362   }
13363   else if (!FrameReached(&screen_frame_counter, 1))
13364     return;
13365
13366   if (ScreenMovPos)
13367   {
13368     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13369     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13370     redraw_mask |= REDRAW_FIELD;
13371   }
13372   else
13373     ScreenMovDir = MV_NONE;
13374 }
13375
13376 void CheckNextToConditions(int x, int y)
13377 {
13378   int element = Tile[x][y];
13379
13380   if (IS_PLAYER(x, y))
13381     TestIfPlayerNextToCustomElement(x, y);
13382
13383   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13384       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13385     TestIfElementNextToCustomElement(x, y);
13386 }
13387
13388 void TestIfPlayerNextToCustomElement(int x, int y)
13389 {
13390   static int xy[4][2] =
13391   {
13392     { 0, -1 },
13393     { -1, 0 },
13394     { +1, 0 },
13395     { 0, +1 }
13396   };
13397   static int trigger_sides[4][2] =
13398   {
13399     // center side       border side
13400     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13401     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13402     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13403     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13404   };
13405   int i;
13406
13407   if (!IS_PLAYER(x, y))
13408     return;
13409
13410   struct PlayerInfo *player = PLAYERINFO(x, y);
13411
13412   if (player->is_moving)
13413     return;
13414
13415   for (i = 0; i < NUM_DIRECTIONS; i++)
13416   {
13417     int xx = x + xy[i][0];
13418     int yy = y + xy[i][1];
13419     int border_side = trigger_sides[i][1];
13420     int border_element;
13421
13422     if (!IN_LEV_FIELD(xx, yy))
13423       continue;
13424
13425     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13426       continue;         // center and border element not connected
13427
13428     border_element = Tile[xx][yy];
13429
13430     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13431                                player->index_bit, border_side);
13432     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13433                                         CE_PLAYER_NEXT_TO_X,
13434                                         player->index_bit, border_side);
13435
13436     /* use player element that is initially defined in the level playfield,
13437        not the player element that corresponds to the runtime player number
13438        (example: a level that contains EL_PLAYER_3 as the only player would
13439        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13440
13441     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13442                              CE_NEXT_TO_X, border_side);
13443   }
13444 }
13445
13446 void TestIfPlayerTouchesCustomElement(int x, int y)
13447 {
13448   static int xy[4][2] =
13449   {
13450     { 0, -1 },
13451     { -1, 0 },
13452     { +1, 0 },
13453     { 0, +1 }
13454   };
13455   static int trigger_sides[4][2] =
13456   {
13457     // center side       border side
13458     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13459     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13460     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13461     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13462   };
13463   static int touch_dir[4] =
13464   {
13465     MV_LEFT | MV_RIGHT,
13466     MV_UP   | MV_DOWN,
13467     MV_UP   | MV_DOWN,
13468     MV_LEFT | MV_RIGHT
13469   };
13470   int center_element = Tile[x][y];      // should always be non-moving!
13471   int i;
13472
13473   for (i = 0; i < NUM_DIRECTIONS; i++)
13474   {
13475     int xx = x + xy[i][0];
13476     int yy = y + xy[i][1];
13477     int center_side = trigger_sides[i][0];
13478     int border_side = trigger_sides[i][1];
13479     int border_element;
13480
13481     if (!IN_LEV_FIELD(xx, yy))
13482       continue;
13483
13484     if (IS_PLAYER(x, y))                // player found at center element
13485     {
13486       struct PlayerInfo *player = PLAYERINFO(x, y);
13487
13488       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13489         border_element = Tile[xx][yy];          // may be moving!
13490       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13491         border_element = Tile[xx][yy];
13492       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13493         border_element = MovingOrBlocked2Element(xx, yy);
13494       else
13495         continue;               // center and border element do not touch
13496
13497       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13498                                  player->index_bit, border_side);
13499       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13500                                           CE_PLAYER_TOUCHES_X,
13501                                           player->index_bit, border_side);
13502
13503       {
13504         /* use player element that is initially defined in the level playfield,
13505            not the player element that corresponds to the runtime player number
13506            (example: a level that contains EL_PLAYER_3 as the only player would
13507            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13508         int player_element = PLAYERINFO(x, y)->initial_element;
13509
13510         CheckElementChangeBySide(xx, yy, border_element, player_element,
13511                                  CE_TOUCHING_X, border_side);
13512       }
13513     }
13514     else if (IS_PLAYER(xx, yy))         // player found at border element
13515     {
13516       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13517
13518       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13519       {
13520         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13521           continue;             // center and border element do not touch
13522       }
13523
13524       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13525                                  player->index_bit, center_side);
13526       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13527                                           CE_PLAYER_TOUCHES_X,
13528                                           player->index_bit, center_side);
13529
13530       {
13531         /* use player element that is initially defined in the level playfield,
13532            not the player element that corresponds to the runtime player number
13533            (example: a level that contains EL_PLAYER_3 as the only player would
13534            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13535         int player_element = PLAYERINFO(xx, yy)->initial_element;
13536
13537         CheckElementChangeBySide(x, y, center_element, player_element,
13538                                  CE_TOUCHING_X, center_side);
13539       }
13540
13541       break;
13542     }
13543   }
13544 }
13545
13546 void TestIfElementNextToCustomElement(int x, int y)
13547 {
13548   static int xy[4][2] =
13549   {
13550     { 0, -1 },
13551     { -1, 0 },
13552     { +1, 0 },
13553     { 0, +1 }
13554   };
13555   static int trigger_sides[4][2] =
13556   {
13557     // center side      border side
13558     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13559     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13560     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13561     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13562   };
13563   int center_element = Tile[x][y];      // should always be non-moving!
13564   int i;
13565
13566   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13567     return;
13568
13569   for (i = 0; i < NUM_DIRECTIONS; i++)
13570   {
13571     int xx = x + xy[i][0];
13572     int yy = y + xy[i][1];
13573     int border_side = trigger_sides[i][1];
13574     int border_element;
13575
13576     if (!IN_LEV_FIELD(xx, yy))
13577       continue;
13578
13579     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13580       continue;                 // center and border element not connected
13581
13582     border_element = Tile[xx][yy];
13583
13584     // check for change of center element (but change it only once)
13585     if (CheckElementChangeBySide(x, y, center_element, border_element,
13586                                  CE_NEXT_TO_X, border_side))
13587       break;
13588   }
13589 }
13590
13591 void TestIfElementTouchesCustomElement(int x, int y)
13592 {
13593   static int xy[4][2] =
13594   {
13595     { 0, -1 },
13596     { -1, 0 },
13597     { +1, 0 },
13598     { 0, +1 }
13599   };
13600   static int trigger_sides[4][2] =
13601   {
13602     // center side      border side
13603     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13604     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13605     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13606     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13607   };
13608   static int touch_dir[4] =
13609   {
13610     MV_LEFT | MV_RIGHT,
13611     MV_UP   | MV_DOWN,
13612     MV_UP   | MV_DOWN,
13613     MV_LEFT | MV_RIGHT
13614   };
13615   boolean change_center_element = FALSE;
13616   int center_element = Tile[x][y];      // should always be non-moving!
13617   int border_element_old[NUM_DIRECTIONS];
13618   int i;
13619
13620   for (i = 0; i < NUM_DIRECTIONS; i++)
13621   {
13622     int xx = x + xy[i][0];
13623     int yy = y + xy[i][1];
13624     int border_element;
13625
13626     border_element_old[i] = -1;
13627
13628     if (!IN_LEV_FIELD(xx, yy))
13629       continue;
13630
13631     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13632       border_element = Tile[xx][yy];    // may be moving!
13633     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13634       border_element = Tile[xx][yy];
13635     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13636       border_element = MovingOrBlocked2Element(xx, yy);
13637     else
13638       continue;                 // center and border element do not touch
13639
13640     border_element_old[i] = border_element;
13641   }
13642
13643   for (i = 0; i < NUM_DIRECTIONS; i++)
13644   {
13645     int xx = x + xy[i][0];
13646     int yy = y + xy[i][1];
13647     int center_side = trigger_sides[i][0];
13648     int border_element = border_element_old[i];
13649
13650     if (border_element == -1)
13651       continue;
13652
13653     // check for change of border element
13654     CheckElementChangeBySide(xx, yy, border_element, center_element,
13655                              CE_TOUCHING_X, center_side);
13656
13657     // (center element cannot be player, so we dont have to check this here)
13658   }
13659
13660   for (i = 0; i < NUM_DIRECTIONS; i++)
13661   {
13662     int xx = x + xy[i][0];
13663     int yy = y + xy[i][1];
13664     int border_side = trigger_sides[i][1];
13665     int border_element = border_element_old[i];
13666
13667     if (border_element == -1)
13668       continue;
13669
13670     // check for change of center element (but change it only once)
13671     if (!change_center_element)
13672       change_center_element =
13673         CheckElementChangeBySide(x, y, center_element, border_element,
13674                                  CE_TOUCHING_X, border_side);
13675
13676     if (IS_PLAYER(xx, yy))
13677     {
13678       /* use player element that is initially defined in the level playfield,
13679          not the player element that corresponds to the runtime player number
13680          (example: a level that contains EL_PLAYER_3 as the only player would
13681          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13682       int player_element = PLAYERINFO(xx, yy)->initial_element;
13683
13684       CheckElementChangeBySide(x, y, center_element, player_element,
13685                                CE_TOUCHING_X, border_side);
13686     }
13687   }
13688 }
13689
13690 void TestIfElementHitsCustomElement(int x, int y, int direction)
13691 {
13692   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13693   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13694   int hitx = x + dx, hity = y + dy;
13695   int hitting_element = Tile[x][y];
13696   int touched_element;
13697
13698   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13699     return;
13700
13701   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13702                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13703
13704   if (IN_LEV_FIELD(hitx, hity))
13705   {
13706     int opposite_direction = MV_DIR_OPPOSITE(direction);
13707     int hitting_side = direction;
13708     int touched_side = opposite_direction;
13709     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13710                           MovDir[hitx][hity] != direction ||
13711                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13712
13713     object_hit = TRUE;
13714
13715     if (object_hit)
13716     {
13717       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13718                                CE_HITTING_X, touched_side);
13719
13720       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13721                                CE_HIT_BY_X, hitting_side);
13722
13723       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13724                                CE_HIT_BY_SOMETHING, opposite_direction);
13725
13726       if (IS_PLAYER(hitx, hity))
13727       {
13728         /* use player element that is initially defined in the level playfield,
13729            not the player element that corresponds to the runtime player number
13730            (example: a level that contains EL_PLAYER_3 as the only player would
13731            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13732         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13733
13734         CheckElementChangeBySide(x, y, hitting_element, player_element,
13735                                  CE_HITTING_X, touched_side);
13736       }
13737     }
13738   }
13739
13740   // "hitting something" is also true when hitting the playfield border
13741   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13742                            CE_HITTING_SOMETHING, direction);
13743 }
13744
13745 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13746 {
13747   int i, kill_x = -1, kill_y = -1;
13748
13749   int bad_element = -1;
13750   static int test_xy[4][2] =
13751   {
13752     { 0, -1 },
13753     { -1, 0 },
13754     { +1, 0 },
13755     { 0, +1 }
13756   };
13757   static int test_dir[4] =
13758   {
13759     MV_UP,
13760     MV_LEFT,
13761     MV_RIGHT,
13762     MV_DOWN
13763   };
13764
13765   for (i = 0; i < NUM_DIRECTIONS; i++)
13766   {
13767     int test_x, test_y, test_move_dir, test_element;
13768
13769     test_x = good_x + test_xy[i][0];
13770     test_y = good_y + test_xy[i][1];
13771
13772     if (!IN_LEV_FIELD(test_x, test_y))
13773       continue;
13774
13775     test_move_dir =
13776       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13777
13778     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13779
13780     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13781        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13782     */
13783     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13784         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13785     {
13786       kill_x = test_x;
13787       kill_y = test_y;
13788       bad_element = test_element;
13789
13790       break;
13791     }
13792   }
13793
13794   if (kill_x != -1 || kill_y != -1)
13795   {
13796     if (IS_PLAYER(good_x, good_y))
13797     {
13798       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13799
13800       if (player->shield_deadly_time_left > 0 &&
13801           !IS_INDESTRUCTIBLE(bad_element))
13802         Bang(kill_x, kill_y);
13803       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13804         KillPlayer(player);
13805     }
13806     else
13807       Bang(good_x, good_y);
13808   }
13809 }
13810
13811 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13812 {
13813   int i, kill_x = -1, kill_y = -1;
13814   int bad_element = Tile[bad_x][bad_y];
13815   static int test_xy[4][2] =
13816   {
13817     { 0, -1 },
13818     { -1, 0 },
13819     { +1, 0 },
13820     { 0, +1 }
13821   };
13822   static int touch_dir[4] =
13823   {
13824     MV_LEFT | MV_RIGHT,
13825     MV_UP   | MV_DOWN,
13826     MV_UP   | MV_DOWN,
13827     MV_LEFT | MV_RIGHT
13828   };
13829   static int test_dir[4] =
13830   {
13831     MV_UP,
13832     MV_LEFT,
13833     MV_RIGHT,
13834     MV_DOWN
13835   };
13836
13837   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13838     return;
13839
13840   for (i = 0; i < NUM_DIRECTIONS; i++)
13841   {
13842     int test_x, test_y, test_move_dir, test_element;
13843
13844     test_x = bad_x + test_xy[i][0];
13845     test_y = bad_y + test_xy[i][1];
13846
13847     if (!IN_LEV_FIELD(test_x, test_y))
13848       continue;
13849
13850     test_move_dir =
13851       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13852
13853     test_element = Tile[test_x][test_y];
13854
13855     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13856        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13857     */
13858     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13859         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13860     {
13861       // good thing is player or penguin that does not move away
13862       if (IS_PLAYER(test_x, test_y))
13863       {
13864         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13865
13866         if (bad_element == EL_ROBOT && player->is_moving)
13867           continue;     // robot does not kill player if he is moving
13868
13869         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13870         {
13871           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13872             continue;           // center and border element do not touch
13873         }
13874
13875         kill_x = test_x;
13876         kill_y = test_y;
13877
13878         break;
13879       }
13880       else if (test_element == EL_PENGUIN)
13881       {
13882         kill_x = test_x;
13883         kill_y = test_y;
13884
13885         break;
13886       }
13887     }
13888   }
13889
13890   if (kill_x != -1 || kill_y != -1)
13891   {
13892     if (IS_PLAYER(kill_x, kill_y))
13893     {
13894       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13895
13896       if (player->shield_deadly_time_left > 0 &&
13897           !IS_INDESTRUCTIBLE(bad_element))
13898         Bang(bad_x, bad_y);
13899       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13900         KillPlayer(player);
13901     }
13902     else
13903       Bang(kill_x, kill_y);
13904   }
13905 }
13906
13907 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13908 {
13909   int bad_element = Tile[bad_x][bad_y];
13910   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13911   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13912   int test_x = bad_x + dx, test_y = bad_y + dy;
13913   int test_move_dir, test_element;
13914   int kill_x = -1, kill_y = -1;
13915
13916   if (!IN_LEV_FIELD(test_x, test_y))
13917     return;
13918
13919   test_move_dir =
13920     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13921
13922   test_element = Tile[test_x][test_y];
13923
13924   if (test_move_dir != bad_move_dir)
13925   {
13926     // good thing can be player or penguin that does not move away
13927     if (IS_PLAYER(test_x, test_y))
13928     {
13929       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13930
13931       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13932          player as being hit when he is moving towards the bad thing, because
13933          the "get hit by" condition would be lost after the player stops) */
13934       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13935         return;         // player moves away from bad thing
13936
13937       kill_x = test_x;
13938       kill_y = test_y;
13939     }
13940     else if (test_element == EL_PENGUIN)
13941     {
13942       kill_x = test_x;
13943       kill_y = test_y;
13944     }
13945   }
13946
13947   if (kill_x != -1 || kill_y != -1)
13948   {
13949     if (IS_PLAYER(kill_x, kill_y))
13950     {
13951       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13952
13953       if (player->shield_deadly_time_left > 0 &&
13954           !IS_INDESTRUCTIBLE(bad_element))
13955         Bang(bad_x, bad_y);
13956       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13957         KillPlayer(player);
13958     }
13959     else
13960       Bang(kill_x, kill_y);
13961   }
13962 }
13963
13964 void TestIfPlayerTouchesBadThing(int x, int y)
13965 {
13966   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13967 }
13968
13969 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13970 {
13971   TestIfGoodThingHitsBadThing(x, y, move_dir);
13972 }
13973
13974 void TestIfBadThingTouchesPlayer(int x, int y)
13975 {
13976   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13977 }
13978
13979 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13980 {
13981   TestIfBadThingHitsGoodThing(x, y, move_dir);
13982 }
13983
13984 void TestIfFriendTouchesBadThing(int x, int y)
13985 {
13986   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13987 }
13988
13989 void TestIfBadThingTouchesFriend(int x, int y)
13990 {
13991   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13992 }
13993
13994 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13995 {
13996   int i, kill_x = bad_x, kill_y = bad_y;
13997   static int xy[4][2] =
13998   {
13999     { 0, -1 },
14000     { -1, 0 },
14001     { +1, 0 },
14002     { 0, +1 }
14003   };
14004
14005   for (i = 0; i < NUM_DIRECTIONS; i++)
14006   {
14007     int x, y, element;
14008
14009     x = bad_x + xy[i][0];
14010     y = bad_y + xy[i][1];
14011     if (!IN_LEV_FIELD(x, y))
14012       continue;
14013
14014     element = Tile[x][y];
14015     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14016         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14017     {
14018       kill_x = x;
14019       kill_y = y;
14020       break;
14021     }
14022   }
14023
14024   if (kill_x != bad_x || kill_y != bad_y)
14025     Bang(bad_x, bad_y);
14026 }
14027
14028 void KillPlayer(struct PlayerInfo *player)
14029 {
14030   int jx = player->jx, jy = player->jy;
14031
14032   if (!player->active)
14033     return;
14034
14035 #if 0
14036   Debug("game:playing:KillPlayer",
14037         "0: killed == %d, active == %d, reanimated == %d",
14038         player->killed, player->active, player->reanimated);
14039 #endif
14040
14041   /* the following code was introduced to prevent an infinite loop when calling
14042      -> Bang()
14043      -> CheckTriggeredElementChangeExt()
14044      -> ExecuteCustomElementAction()
14045      -> KillPlayer()
14046      -> (infinitely repeating the above sequence of function calls)
14047      which occurs when killing the player while having a CE with the setting
14048      "kill player X when explosion of <player X>"; the solution using a new
14049      field "player->killed" was chosen for backwards compatibility, although
14050      clever use of the fields "player->active" etc. would probably also work */
14051 #if 1
14052   if (player->killed)
14053     return;
14054 #endif
14055
14056   player->killed = TRUE;
14057
14058   // remove accessible field at the player's position
14059   Tile[jx][jy] = EL_EMPTY;
14060
14061   // deactivate shield (else Bang()/Explode() would not work right)
14062   player->shield_normal_time_left = 0;
14063   player->shield_deadly_time_left = 0;
14064
14065 #if 0
14066   Debug("game:playing:KillPlayer",
14067         "1: killed == %d, active == %d, reanimated == %d",
14068         player->killed, player->active, player->reanimated);
14069 #endif
14070
14071   Bang(jx, jy);
14072
14073 #if 0
14074   Debug("game:playing:KillPlayer",
14075         "2: killed == %d, active == %d, reanimated == %d",
14076         player->killed, player->active, player->reanimated);
14077 #endif
14078
14079   if (player->reanimated)       // killed player may have been reanimated
14080     player->killed = player->reanimated = FALSE;
14081   else
14082     BuryPlayer(player);
14083 }
14084
14085 static void KillPlayerUnlessEnemyProtected(int x, int y)
14086 {
14087   if (!PLAYER_ENEMY_PROTECTED(x, y))
14088     KillPlayer(PLAYERINFO(x, y));
14089 }
14090
14091 static void KillPlayerUnlessExplosionProtected(int x, int y)
14092 {
14093   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14094     KillPlayer(PLAYERINFO(x, y));
14095 }
14096
14097 void BuryPlayer(struct PlayerInfo *player)
14098 {
14099   int jx = player->jx, jy = player->jy;
14100
14101   if (!player->active)
14102     return;
14103
14104   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14105   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14106
14107   RemovePlayer(player);
14108
14109   player->buried = TRUE;
14110
14111   if (game.all_players_gone)
14112     game.GameOver = TRUE;
14113 }
14114
14115 void RemovePlayer(struct PlayerInfo *player)
14116 {
14117   int jx = player->jx, jy = player->jy;
14118   int i, found = FALSE;
14119
14120   player->present = FALSE;
14121   player->active = FALSE;
14122
14123   // required for some CE actions (even if the player is not active anymore)
14124   player->MovPos = 0;
14125
14126   if (!ExplodeField[jx][jy])
14127     StorePlayer[jx][jy] = 0;
14128
14129   if (player->is_moving)
14130     TEST_DrawLevelField(player->last_jx, player->last_jy);
14131
14132   for (i = 0; i < MAX_PLAYERS; i++)
14133     if (stored_player[i].active)
14134       found = TRUE;
14135
14136   if (!found)
14137   {
14138     game.all_players_gone = TRUE;
14139     game.GameOver = TRUE;
14140   }
14141
14142   game.exit_x = game.robot_wheel_x = jx;
14143   game.exit_y = game.robot_wheel_y = jy;
14144 }
14145
14146 void ExitPlayer(struct PlayerInfo *player)
14147 {
14148   DrawPlayer(player);   // needed here only to cleanup last field
14149   RemovePlayer(player);
14150
14151   if (game.players_still_needed > 0)
14152     game.players_still_needed--;
14153 }
14154
14155 static void SetFieldForSnapping(int x, int y, int element, int direction,
14156                                 int player_index_bit)
14157 {
14158   struct ElementInfo *ei = &element_info[element];
14159   int direction_bit = MV_DIR_TO_BIT(direction);
14160   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14161   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14162                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14163
14164   Tile[x][y] = EL_ELEMENT_SNAPPING;
14165   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14166   MovDir[x][y] = direction;
14167   Store[x][y] = element;
14168   Store2[x][y] = player_index_bit;
14169
14170   ResetGfxAnimation(x, y);
14171
14172   GfxElement[x][y] = element;
14173   GfxAction[x][y] = action;
14174   GfxDir[x][y] = direction;
14175   GfxFrame[x][y] = -1;
14176 }
14177
14178 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14179                                    int player_index_bit)
14180 {
14181   TestIfElementTouchesCustomElement(x, y);      // for empty space
14182
14183   if (level.finish_dig_collect)
14184   {
14185     int dig_side = MV_DIR_OPPOSITE(direction);
14186     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14187                         CE_PLAYER_COLLECTS_X);
14188
14189     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14190                                         player_index_bit, dig_side);
14191     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14192                                         player_index_bit, dig_side);
14193   }
14194 }
14195
14196 /*
14197   =============================================================================
14198   checkDiagonalPushing()
14199   -----------------------------------------------------------------------------
14200   check if diagonal input device direction results in pushing of object
14201   (by checking if the alternative direction is walkable, diggable, ...)
14202   =============================================================================
14203 */
14204
14205 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14206                                     int x, int y, int real_dx, int real_dy)
14207 {
14208   int jx, jy, dx, dy, xx, yy;
14209
14210   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14211     return TRUE;
14212
14213   // diagonal direction: check alternative direction
14214   jx = player->jx;
14215   jy = player->jy;
14216   dx = x - jx;
14217   dy = y - jy;
14218   xx = jx + (dx == 0 ? real_dx : 0);
14219   yy = jy + (dy == 0 ? real_dy : 0);
14220
14221   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14222 }
14223
14224 /*
14225   =============================================================================
14226   DigField()
14227   -----------------------------------------------------------------------------
14228   x, y:                 field next to player (non-diagonal) to try to dig to
14229   real_dx, real_dy:     direction as read from input device (can be diagonal)
14230   =============================================================================
14231 */
14232
14233 static int DigField(struct PlayerInfo *player,
14234                     int oldx, int oldy, int x, int y,
14235                     int real_dx, int real_dy, int mode)
14236 {
14237   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14238   boolean player_was_pushing = player->is_pushing;
14239   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14240   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14241   int jx = oldx, jy = oldy;
14242   int dx = x - jx, dy = y - jy;
14243   int nextx = x + dx, nexty = y + dy;
14244   int move_direction = (dx == -1 ? MV_LEFT  :
14245                         dx == +1 ? MV_RIGHT :
14246                         dy == -1 ? MV_UP    :
14247                         dy == +1 ? MV_DOWN  : MV_NONE);
14248   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14249   int dig_side = MV_DIR_OPPOSITE(move_direction);
14250   int old_element = Tile[jx][jy];
14251   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14252   int collect_count;
14253
14254   if (is_player)                // function can also be called by EL_PENGUIN
14255   {
14256     if (player->MovPos == 0)
14257     {
14258       player->is_digging = FALSE;
14259       player->is_collecting = FALSE;
14260     }
14261
14262     if (player->MovPos == 0)    // last pushing move finished
14263       player->is_pushing = FALSE;
14264
14265     if (mode == DF_NO_PUSH)     // player just stopped pushing
14266     {
14267       player->is_switching = FALSE;
14268       player->push_delay = -1;
14269
14270       return MP_NO_ACTION;
14271     }
14272   }
14273   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14274     old_element = Back[jx][jy];
14275
14276   // in case of element dropped at player position, check background
14277   else if (Back[jx][jy] != EL_EMPTY &&
14278            game.engine_version >= VERSION_IDENT(2,2,0,0))
14279     old_element = Back[jx][jy];
14280
14281   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14282     return MP_NO_ACTION;        // field has no opening in this direction
14283
14284   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14285     return MP_NO_ACTION;        // field has no opening in this direction
14286
14287   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14288   {
14289     SplashAcid(x, y);
14290
14291     Tile[jx][jy] = player->artwork_element;
14292     InitMovingField(jx, jy, MV_DOWN);
14293     Store[jx][jy] = EL_ACID;
14294     ContinueMoving(jx, jy);
14295     BuryPlayer(player);
14296
14297     return MP_DONT_RUN_INTO;
14298   }
14299
14300   if (player_can_move && DONT_RUN_INTO(element))
14301   {
14302     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14303
14304     return MP_DONT_RUN_INTO;
14305   }
14306
14307   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14308     return MP_NO_ACTION;
14309
14310   collect_count = element_info[element].collect_count_initial;
14311
14312   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14313     return MP_NO_ACTION;
14314
14315   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14316     player_can_move = player_can_move_or_snap;
14317
14318   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14319       game.engine_version >= VERSION_IDENT(2,2,0,0))
14320   {
14321     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14322                                player->index_bit, dig_side);
14323     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14324                                         player->index_bit, dig_side);
14325
14326     if (element == EL_DC_LANDMINE)
14327       Bang(x, y);
14328
14329     if (Tile[x][y] != element)          // field changed by snapping
14330       return MP_ACTION;
14331
14332     return MP_NO_ACTION;
14333   }
14334
14335   if (player->gravity && is_player && !player->is_auto_moving &&
14336       canFallDown(player) && move_direction != MV_DOWN &&
14337       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14338     return MP_NO_ACTION;        // player cannot walk here due to gravity
14339
14340   if (player_can_move &&
14341       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14342   {
14343     int sound_element = SND_ELEMENT(element);
14344     int sound_action = ACTION_WALKING;
14345
14346     if (IS_RND_GATE(element))
14347     {
14348       if (!player->key[RND_GATE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_RND_GATE_GRAY(element))
14352     {
14353       if (!player->key[RND_GATE_GRAY_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14357     {
14358       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (element == EL_EXIT_OPEN ||
14362              element == EL_EM_EXIT_OPEN ||
14363              element == EL_EM_EXIT_OPENING ||
14364              element == EL_STEEL_EXIT_OPEN ||
14365              element == EL_EM_STEEL_EXIT_OPEN ||
14366              element == EL_EM_STEEL_EXIT_OPENING ||
14367              element == EL_SP_EXIT_OPEN ||
14368              element == EL_SP_EXIT_OPENING)
14369     {
14370       sound_action = ACTION_PASSING;    // player is passing exit
14371     }
14372     else if (element == EL_EMPTY)
14373     {
14374       sound_action = ACTION_MOVING;             // nothing to walk on
14375     }
14376
14377     // play sound from background or player, whatever is available
14378     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14379       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14380     else
14381       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14382   }
14383   else if (player_can_move &&
14384            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14385   {
14386     if (!ACCESS_FROM(element, opposite_direction))
14387       return MP_NO_ACTION;      // field not accessible from this direction
14388
14389     if (CAN_MOVE(element))      // only fixed elements can be passed!
14390       return MP_NO_ACTION;
14391
14392     if (IS_EM_GATE(element))
14393     {
14394       if (!player->key[EM_GATE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EM_GATE_GRAY(element))
14398     {
14399       if (!player->key[EM_GATE_GRAY_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14403     {
14404       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EMC_GATE(element))
14408     {
14409       if (!player->key[EMC_GATE_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (IS_EMC_GATE_GRAY(element))
14413     {
14414       if (!player->key[EMC_GATE_GRAY_NR(element)])
14415         return MP_NO_ACTION;
14416     }
14417     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14418     {
14419       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14420         return MP_NO_ACTION;
14421     }
14422     else if (element == EL_DC_GATE_WHITE ||
14423              element == EL_DC_GATE_WHITE_GRAY ||
14424              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14425     {
14426       if (player->num_white_keys == 0)
14427         return MP_NO_ACTION;
14428
14429       player->num_white_keys--;
14430     }
14431     else if (IS_SP_PORT(element))
14432     {
14433       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14434           element == EL_SP_GRAVITY_PORT_RIGHT ||
14435           element == EL_SP_GRAVITY_PORT_UP ||
14436           element == EL_SP_GRAVITY_PORT_DOWN)
14437         player->gravity = !player->gravity;
14438       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14439                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14440                element == EL_SP_GRAVITY_ON_PORT_UP ||
14441                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14442         player->gravity = TRUE;
14443       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14444                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14445                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14446                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14447         player->gravity = FALSE;
14448     }
14449
14450     // automatically move to the next field with double speed
14451     player->programmed_action = move_direction;
14452
14453     if (player->move_delay_reset_counter == 0)
14454     {
14455       player->move_delay_reset_counter = 2;     // two double speed steps
14456
14457       DOUBLE_PLAYER_SPEED(player);
14458     }
14459
14460     PlayLevelSoundAction(x, y, ACTION_PASSING);
14461   }
14462   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14463   {
14464     RemoveField(x, y);
14465
14466     if (mode != DF_SNAP)
14467     {
14468       GfxElement[x][y] = GFX_ELEMENT(element);
14469       player->is_digging = TRUE;
14470     }
14471
14472     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14473
14474     // use old behaviour for old levels (digging)
14475     if (!level.finish_dig_collect)
14476     {
14477       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14478                                           player->index_bit, dig_side);
14479
14480       // if digging triggered player relocation, finish digging tile
14481       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14482         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14483     }
14484
14485     if (mode == DF_SNAP)
14486     {
14487       if (level.block_snap_field)
14488         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14489       else
14490         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14491
14492       // use old behaviour for old levels (snapping)
14493       if (!level.finish_dig_collect)
14494         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14495                                             player->index_bit, dig_side);
14496     }
14497   }
14498   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14499   {
14500     RemoveField(x, y);
14501
14502     if (is_player && mode != DF_SNAP)
14503     {
14504       GfxElement[x][y] = element;
14505       player->is_collecting = TRUE;
14506     }
14507
14508     if (element == EL_SPEED_PILL)
14509     {
14510       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14511     }
14512     else if (element == EL_EXTRA_TIME && level.time > 0)
14513     {
14514       TimeLeft += level.extra_time;
14515
14516       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14517
14518       DisplayGameControlValues();
14519     }
14520     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14521     {
14522       player->shield_normal_time_left += level.shield_normal_time;
14523       if (element == EL_SHIELD_DEADLY)
14524         player->shield_deadly_time_left += level.shield_deadly_time;
14525     }
14526     else if (element == EL_DYNAMITE ||
14527              element == EL_EM_DYNAMITE ||
14528              element == EL_SP_DISK_RED)
14529     {
14530       if (player->inventory_size < MAX_INVENTORY_SIZE)
14531         player->inventory_element[player->inventory_size++] = element;
14532
14533       DrawGameDoorValues();
14534     }
14535     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14536     {
14537       player->dynabomb_count++;
14538       player->dynabombs_left++;
14539     }
14540     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14541     {
14542       player->dynabomb_size++;
14543     }
14544     else if (element == EL_DYNABOMB_INCREASE_POWER)
14545     {
14546       player->dynabomb_xl = TRUE;
14547     }
14548     else if (IS_KEY(element))
14549     {
14550       player->key[KEY_NR(element)] = TRUE;
14551
14552       DrawGameDoorValues();
14553     }
14554     else if (element == EL_DC_KEY_WHITE)
14555     {
14556       player->num_white_keys++;
14557
14558       // display white keys?
14559       // DrawGameDoorValues();
14560     }
14561     else if (IS_ENVELOPE(element))
14562     {
14563       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14564
14565       if (!wait_for_snapping)
14566         player->show_envelope = element;
14567     }
14568     else if (element == EL_EMC_LENSES)
14569     {
14570       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14571
14572       RedrawAllInvisibleElementsForLenses();
14573     }
14574     else if (element == EL_EMC_MAGNIFIER)
14575     {
14576       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14577
14578       RedrawAllInvisibleElementsForMagnifier();
14579     }
14580     else if (IS_DROPPABLE(element) ||
14581              IS_THROWABLE(element))     // can be collected and dropped
14582     {
14583       int i;
14584
14585       if (collect_count == 0)
14586         player->inventory_infinite_element = element;
14587       else
14588         for (i = 0; i < collect_count; i++)
14589           if (player->inventory_size < MAX_INVENTORY_SIZE)
14590             player->inventory_element[player->inventory_size++] = element;
14591
14592       DrawGameDoorValues();
14593     }
14594     else if (collect_count > 0)
14595     {
14596       game.gems_still_needed -= collect_count;
14597       if (game.gems_still_needed < 0)
14598         game.gems_still_needed = 0;
14599
14600       game.snapshot.collected_item = TRUE;
14601
14602       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14603
14604       DisplayGameControlValues();
14605     }
14606
14607     RaiseScoreElement(element);
14608     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14609
14610     // use old behaviour for old levels (collecting)
14611     if (!level.finish_dig_collect && is_player)
14612     {
14613       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14614                                           player->index_bit, dig_side);
14615
14616       // if collecting triggered player relocation, finish collecting tile
14617       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14618         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14619     }
14620
14621     if (mode == DF_SNAP)
14622     {
14623       if (level.block_snap_field)
14624         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14625       else
14626         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14627
14628       // use old behaviour for old levels (snapping)
14629       if (!level.finish_dig_collect)
14630         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14631                                             player->index_bit, dig_side);
14632     }
14633   }
14634   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14635   {
14636     if (mode == DF_SNAP && element != EL_BD_ROCK)
14637       return MP_NO_ACTION;
14638
14639     if (CAN_FALL(element) && dy)
14640       return MP_NO_ACTION;
14641
14642     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14643         !(element == EL_SPRING && level.use_spring_bug))
14644       return MP_NO_ACTION;
14645
14646     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14647         ((move_direction & MV_VERTICAL &&
14648           ((element_info[element].move_pattern & MV_LEFT &&
14649             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14650            (element_info[element].move_pattern & MV_RIGHT &&
14651             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14652          (move_direction & MV_HORIZONTAL &&
14653           ((element_info[element].move_pattern & MV_UP &&
14654             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14655            (element_info[element].move_pattern & MV_DOWN &&
14656             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14657       return MP_NO_ACTION;
14658
14659     // do not push elements already moving away faster than player
14660     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14661         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14662       return MP_NO_ACTION;
14663
14664     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14665     {
14666       if (player->push_delay_value == -1 || !player_was_pushing)
14667         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14668     }
14669     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14670     {
14671       if (player->push_delay_value == -1)
14672         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14673     }
14674     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14675     {
14676       if (!player->is_pushing)
14677         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14678     }
14679
14680     player->is_pushing = TRUE;
14681     player->is_active = TRUE;
14682
14683     if (!(IN_LEV_FIELD(nextx, nexty) &&
14684           (IS_FREE(nextx, nexty) ||
14685            (IS_SB_ELEMENT(element) &&
14686             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14687            (IS_CUSTOM_ELEMENT(element) &&
14688             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14689       return MP_NO_ACTION;
14690
14691     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14692       return MP_NO_ACTION;
14693
14694     if (player->push_delay == -1)       // new pushing; restart delay
14695       player->push_delay = 0;
14696
14697     if (player->push_delay < player->push_delay_value &&
14698         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14699         element != EL_SPRING && element != EL_BALLOON)
14700     {
14701       // make sure that there is no move delay before next try to push
14702       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14703         player->move_delay = 0;
14704
14705       return MP_NO_ACTION;
14706     }
14707
14708     if (IS_CUSTOM_ELEMENT(element) &&
14709         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14710     {
14711       if (!DigFieldByCE(nextx, nexty, element))
14712         return MP_NO_ACTION;
14713     }
14714
14715     if (IS_SB_ELEMENT(element))
14716     {
14717       boolean sokoban_task_solved = FALSE;
14718
14719       if (element == EL_SOKOBAN_FIELD_FULL)
14720       {
14721         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14722
14723         IncrementSokobanFieldsNeeded();
14724         IncrementSokobanObjectsNeeded();
14725       }
14726
14727       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14728       {
14729         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14730
14731         DecrementSokobanFieldsNeeded();
14732         DecrementSokobanObjectsNeeded();
14733
14734         // sokoban object was pushed from empty field to sokoban field
14735         if (Back[x][y] == EL_EMPTY)
14736           sokoban_task_solved = TRUE;
14737       }
14738
14739       Tile[x][y] = EL_SOKOBAN_OBJECT;
14740
14741       if (Back[x][y] == Back[nextx][nexty])
14742         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14743       else if (Back[x][y] != 0)
14744         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14745                                     ACTION_EMPTYING);
14746       else
14747         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14748                                     ACTION_FILLING);
14749
14750       if (sokoban_task_solved &&
14751           game.sokoban_fields_still_needed == 0 &&
14752           game.sokoban_objects_still_needed == 0 &&
14753           level.auto_exit_sokoban)
14754       {
14755         game.players_still_needed = 0;
14756
14757         LevelSolved();
14758
14759         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14760       }
14761     }
14762     else
14763       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14764
14765     InitMovingField(x, y, move_direction);
14766     GfxAction[x][y] = ACTION_PUSHING;
14767
14768     if (mode == DF_SNAP)
14769       ContinueMoving(x, y);
14770     else
14771       MovPos[x][y] = (dx != 0 ? dx : dy);
14772
14773     Pushed[x][y] = TRUE;
14774     Pushed[nextx][nexty] = TRUE;
14775
14776     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14777       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14778     else
14779       player->push_delay_value = -1;    // get new value later
14780
14781     // check for element change _after_ element has been pushed
14782     if (game.use_change_when_pushing_bug)
14783     {
14784       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14785                                  player->index_bit, dig_side);
14786       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14787                                           player->index_bit, dig_side);
14788     }
14789   }
14790   else if (IS_SWITCHABLE(element))
14791   {
14792     if (PLAYER_SWITCHING(player, x, y))
14793     {
14794       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14795                                           player->index_bit, dig_side);
14796
14797       return MP_ACTION;
14798     }
14799
14800     player->is_switching = TRUE;
14801     player->switch_x = x;
14802     player->switch_y = y;
14803
14804     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14805
14806     if (element == EL_ROBOT_WHEEL)
14807     {
14808       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14809
14810       game.robot_wheel_x = x;
14811       game.robot_wheel_y = y;
14812       game.robot_wheel_active = TRUE;
14813
14814       TEST_DrawLevelField(x, y);
14815     }
14816     else if (element == EL_SP_TERMINAL)
14817     {
14818       int xx, yy;
14819
14820       SCAN_PLAYFIELD(xx, yy)
14821       {
14822         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14823         {
14824           Bang(xx, yy);
14825         }
14826         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14827         {
14828           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14829
14830           ResetGfxAnimation(xx, yy);
14831           TEST_DrawLevelField(xx, yy);
14832         }
14833       }
14834     }
14835     else if (IS_BELT_SWITCH(element))
14836     {
14837       ToggleBeltSwitch(x, y);
14838     }
14839     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14840              element == EL_SWITCHGATE_SWITCH_DOWN ||
14841              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14842              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14843     {
14844       ToggleSwitchgateSwitch(x, y);
14845     }
14846     else if (element == EL_LIGHT_SWITCH ||
14847              element == EL_LIGHT_SWITCH_ACTIVE)
14848     {
14849       ToggleLightSwitch(x, y);
14850     }
14851     else if (element == EL_TIMEGATE_SWITCH ||
14852              element == EL_DC_TIMEGATE_SWITCH)
14853     {
14854       ActivateTimegateSwitch(x, y);
14855     }
14856     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14857              element == EL_BALLOON_SWITCH_RIGHT ||
14858              element == EL_BALLOON_SWITCH_UP    ||
14859              element == EL_BALLOON_SWITCH_DOWN  ||
14860              element == EL_BALLOON_SWITCH_NONE  ||
14861              element == EL_BALLOON_SWITCH_ANY)
14862     {
14863       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14864                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14865                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14866                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14867                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14868                              move_direction);
14869     }
14870     else if (element == EL_LAMP)
14871     {
14872       Tile[x][y] = EL_LAMP_ACTIVE;
14873       game.lights_still_needed--;
14874
14875       ResetGfxAnimation(x, y);
14876       TEST_DrawLevelField(x, y);
14877     }
14878     else if (element == EL_TIME_ORB_FULL)
14879     {
14880       Tile[x][y] = EL_TIME_ORB_EMPTY;
14881
14882       if (level.time > 0 || level.use_time_orb_bug)
14883       {
14884         TimeLeft += level.time_orb_time;
14885         game.no_time_limit = FALSE;
14886
14887         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14888
14889         DisplayGameControlValues();
14890       }
14891
14892       ResetGfxAnimation(x, y);
14893       TEST_DrawLevelField(x, y);
14894     }
14895     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14896              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14897     {
14898       int xx, yy;
14899
14900       game.ball_active = !game.ball_active;
14901
14902       SCAN_PLAYFIELD(xx, yy)
14903       {
14904         int e = Tile[xx][yy];
14905
14906         if (game.ball_active)
14907         {
14908           if (e == EL_EMC_MAGIC_BALL)
14909             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14910           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14911             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14912         }
14913         else
14914         {
14915           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14916             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14917           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14918             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14919         }
14920       }
14921     }
14922
14923     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14924                                         player->index_bit, dig_side);
14925
14926     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14927                                         player->index_bit, dig_side);
14928
14929     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14930                                         player->index_bit, dig_side);
14931
14932     return MP_ACTION;
14933   }
14934   else
14935   {
14936     if (!PLAYER_SWITCHING(player, x, y))
14937     {
14938       player->is_switching = TRUE;
14939       player->switch_x = x;
14940       player->switch_y = y;
14941
14942       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14943                                  player->index_bit, dig_side);
14944       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14945                                           player->index_bit, dig_side);
14946
14947       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14948                                  player->index_bit, dig_side);
14949       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14950                                           player->index_bit, dig_side);
14951     }
14952
14953     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14954                                player->index_bit, dig_side);
14955     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14956                                         player->index_bit, dig_side);
14957
14958     return MP_NO_ACTION;
14959   }
14960
14961   player->push_delay = -1;
14962
14963   if (is_player)                // function can also be called by EL_PENGUIN
14964   {
14965     if (Tile[x][y] != element)          // really digged/collected something
14966     {
14967       player->is_collecting = !player->is_digging;
14968       player->is_active = TRUE;
14969
14970       player->last_removed_element = element;
14971     }
14972   }
14973
14974   return MP_MOVING;
14975 }
14976
14977 static boolean DigFieldByCE(int x, int y, int digging_element)
14978 {
14979   int element = Tile[x][y];
14980
14981   if (!IS_FREE(x, y))
14982   {
14983     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14984                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14985                   ACTION_BREAKING);
14986
14987     // no element can dig solid indestructible elements
14988     if (IS_INDESTRUCTIBLE(element) &&
14989         !IS_DIGGABLE(element) &&
14990         !IS_COLLECTIBLE(element))
14991       return FALSE;
14992
14993     if (AmoebaNr[x][y] &&
14994         (element == EL_AMOEBA_FULL ||
14995          element == EL_BD_AMOEBA ||
14996          element == EL_AMOEBA_GROWING))
14997     {
14998       AmoebaCnt[AmoebaNr[x][y]]--;
14999       AmoebaCnt2[AmoebaNr[x][y]]--;
15000     }
15001
15002     if (IS_MOVING(x, y))
15003       RemoveMovingField(x, y);
15004     else
15005     {
15006       RemoveField(x, y);
15007       TEST_DrawLevelField(x, y);
15008     }
15009
15010     // if digged element was about to explode, prevent the explosion
15011     ExplodeField[x][y] = EX_TYPE_NONE;
15012
15013     PlayLevelSoundAction(x, y, action);
15014   }
15015
15016   Store[x][y] = EL_EMPTY;
15017
15018   // this makes it possible to leave the removed element again
15019   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15020     Store[x][y] = element;
15021
15022   return TRUE;
15023 }
15024
15025 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15026 {
15027   int jx = player->jx, jy = player->jy;
15028   int x = jx + dx, y = jy + dy;
15029   int snap_direction = (dx == -1 ? MV_LEFT  :
15030                         dx == +1 ? MV_RIGHT :
15031                         dy == -1 ? MV_UP    :
15032                         dy == +1 ? MV_DOWN  : MV_NONE);
15033   boolean can_continue_snapping = (level.continuous_snapping &&
15034                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15035
15036   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15037     return FALSE;
15038
15039   if (!player->active || !IN_LEV_FIELD(x, y))
15040     return FALSE;
15041
15042   if (dx && dy)
15043     return FALSE;
15044
15045   if (!dx && !dy)
15046   {
15047     if (player->MovPos == 0)
15048       player->is_pushing = FALSE;
15049
15050     player->is_snapping = FALSE;
15051
15052     if (player->MovPos == 0)
15053     {
15054       player->is_moving = FALSE;
15055       player->is_digging = FALSE;
15056       player->is_collecting = FALSE;
15057     }
15058
15059     return FALSE;
15060   }
15061
15062   // prevent snapping with already pressed snap key when not allowed
15063   if (player->is_snapping && !can_continue_snapping)
15064     return FALSE;
15065
15066   player->MovDir = snap_direction;
15067
15068   if (player->MovPos == 0)
15069   {
15070     player->is_moving = FALSE;
15071     player->is_digging = FALSE;
15072     player->is_collecting = FALSE;
15073   }
15074
15075   player->is_dropping = FALSE;
15076   player->is_dropping_pressed = FALSE;
15077   player->drop_pressed_delay = 0;
15078
15079   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15080     return FALSE;
15081
15082   player->is_snapping = TRUE;
15083   player->is_active = TRUE;
15084
15085   if (player->MovPos == 0)
15086   {
15087     player->is_moving = FALSE;
15088     player->is_digging = FALSE;
15089     player->is_collecting = FALSE;
15090   }
15091
15092   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15093     TEST_DrawLevelField(player->last_jx, player->last_jy);
15094
15095   TEST_DrawLevelField(x, y);
15096
15097   return TRUE;
15098 }
15099
15100 static boolean DropElement(struct PlayerInfo *player)
15101 {
15102   int old_element, new_element;
15103   int dropx = player->jx, dropy = player->jy;
15104   int drop_direction = player->MovDir;
15105   int drop_side = drop_direction;
15106   int drop_element = get_next_dropped_element(player);
15107
15108   /* do not drop an element on top of another element; when holding drop key
15109      pressed without moving, dropped element must move away before the next
15110      element can be dropped (this is especially important if the next element
15111      is dynamite, which can be placed on background for historical reasons) */
15112   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15113     return MP_ACTION;
15114
15115   if (IS_THROWABLE(drop_element))
15116   {
15117     dropx += GET_DX_FROM_DIR(drop_direction);
15118     dropy += GET_DY_FROM_DIR(drop_direction);
15119
15120     if (!IN_LEV_FIELD(dropx, dropy))
15121       return FALSE;
15122   }
15123
15124   old_element = Tile[dropx][dropy];     // old element at dropping position
15125   new_element = drop_element;           // default: no change when dropping
15126
15127   // check if player is active, not moving and ready to drop
15128   if (!player->active || player->MovPos || player->drop_delay > 0)
15129     return FALSE;
15130
15131   // check if player has anything that can be dropped
15132   if (new_element == EL_UNDEFINED)
15133     return FALSE;
15134
15135   // only set if player has anything that can be dropped
15136   player->is_dropping_pressed = TRUE;
15137
15138   // check if drop key was pressed long enough for EM style dynamite
15139   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15140     return FALSE;
15141
15142   // check if anything can be dropped at the current position
15143   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15144     return FALSE;
15145
15146   // collected custom elements can only be dropped on empty fields
15147   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15148     return FALSE;
15149
15150   if (old_element != EL_EMPTY)
15151     Back[dropx][dropy] = old_element;   // store old element on this field
15152
15153   ResetGfxAnimation(dropx, dropy);
15154   ResetRandomAnimationValue(dropx, dropy);
15155
15156   if (player->inventory_size > 0 ||
15157       player->inventory_infinite_element != EL_UNDEFINED)
15158   {
15159     if (player->inventory_size > 0)
15160     {
15161       player->inventory_size--;
15162
15163       DrawGameDoorValues();
15164
15165       if (new_element == EL_DYNAMITE)
15166         new_element = EL_DYNAMITE_ACTIVE;
15167       else if (new_element == EL_EM_DYNAMITE)
15168         new_element = EL_EM_DYNAMITE_ACTIVE;
15169       else if (new_element == EL_SP_DISK_RED)
15170         new_element = EL_SP_DISK_RED_ACTIVE;
15171     }
15172
15173     Tile[dropx][dropy] = new_element;
15174
15175     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15176       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15177                           el2img(Tile[dropx][dropy]), 0);
15178
15179     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15180
15181     // needed if previous element just changed to "empty" in the last frame
15182     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15183
15184     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15185                                player->index_bit, drop_side);
15186     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15187                                         CE_PLAYER_DROPS_X,
15188                                         player->index_bit, drop_side);
15189
15190     TestIfElementTouchesCustomElement(dropx, dropy);
15191   }
15192   else          // player is dropping a dyna bomb
15193   {
15194     player->dynabombs_left--;
15195
15196     Tile[dropx][dropy] = new_element;
15197
15198     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15199       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15200                           el2img(Tile[dropx][dropy]), 0);
15201
15202     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15203   }
15204
15205   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15206     InitField_WithBug1(dropx, dropy, FALSE);
15207
15208   new_element = Tile[dropx][dropy];     // element might have changed
15209
15210   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15211       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15212   {
15213     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15214       MovDir[dropx][dropy] = drop_direction;
15215
15216     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15217
15218     // do not cause impact style collision by dropping elements that can fall
15219     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15220   }
15221
15222   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15223   player->is_dropping = TRUE;
15224
15225   player->drop_pressed_delay = 0;
15226   player->is_dropping_pressed = FALSE;
15227
15228   player->drop_x = dropx;
15229   player->drop_y = dropy;
15230
15231   return TRUE;
15232 }
15233
15234 // ----------------------------------------------------------------------------
15235 // game sound playing functions
15236 // ----------------------------------------------------------------------------
15237
15238 static int *loop_sound_frame = NULL;
15239 static int *loop_sound_volume = NULL;
15240
15241 void InitPlayLevelSound(void)
15242 {
15243   int num_sounds = getSoundListSize();
15244
15245   checked_free(loop_sound_frame);
15246   checked_free(loop_sound_volume);
15247
15248   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15249   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15250 }
15251
15252 static void PlayLevelSound(int x, int y, int nr)
15253 {
15254   int sx = SCREENX(x), sy = SCREENY(y);
15255   int volume, stereo_position;
15256   int max_distance = 8;
15257   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15258
15259   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15260       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15261     return;
15262
15263   if (!IN_LEV_FIELD(x, y) ||
15264       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15265       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15266     return;
15267
15268   volume = SOUND_MAX_VOLUME;
15269
15270   if (!IN_SCR_FIELD(sx, sy))
15271   {
15272     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15273     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15274
15275     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15276   }
15277
15278   stereo_position = (SOUND_MAX_LEFT +
15279                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15280                      (SCR_FIELDX + 2 * max_distance));
15281
15282   if (IS_LOOP_SOUND(nr))
15283   {
15284     /* This assures that quieter loop sounds do not overwrite louder ones,
15285        while restarting sound volume comparison with each new game frame. */
15286
15287     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15288       return;
15289
15290     loop_sound_volume[nr] = volume;
15291     loop_sound_frame[nr] = FrameCounter;
15292   }
15293
15294   PlaySoundExt(nr, volume, stereo_position, type);
15295 }
15296
15297 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15298 {
15299   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15300                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15301                  y < LEVELY(BY1) ? LEVELY(BY1) :
15302                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15303                  sound_action);
15304 }
15305
15306 static void PlayLevelSoundAction(int x, int y, int action)
15307 {
15308   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15309 }
15310
15311 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15312 {
15313   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15314
15315   if (sound_effect != SND_UNDEFINED)
15316     PlayLevelSound(x, y, sound_effect);
15317 }
15318
15319 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15320                                               int action)
15321 {
15322   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15323
15324   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15325     PlayLevelSound(x, y, sound_effect);
15326 }
15327
15328 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15329 {
15330   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15331
15332   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15333     PlayLevelSound(x, y, sound_effect);
15334 }
15335
15336 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15337 {
15338   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15339
15340   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15341     StopSound(sound_effect);
15342 }
15343
15344 static int getLevelMusicNr(void)
15345 {
15346   if (levelset.music[level_nr] != MUS_UNDEFINED)
15347     return levelset.music[level_nr];            // from config file
15348   else
15349     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15350 }
15351
15352 static void FadeLevelSounds(void)
15353 {
15354   FadeSounds();
15355 }
15356
15357 static void FadeLevelMusic(void)
15358 {
15359   int music_nr = getLevelMusicNr();
15360   char *curr_music = getCurrentlyPlayingMusicFilename();
15361   char *next_music = getMusicInfoEntryFilename(music_nr);
15362
15363   if (!strEqual(curr_music, next_music))
15364     FadeMusic();
15365 }
15366
15367 void FadeLevelSoundsAndMusic(void)
15368 {
15369   FadeLevelSounds();
15370   FadeLevelMusic();
15371 }
15372
15373 static void PlayLevelMusic(void)
15374 {
15375   int music_nr = getLevelMusicNr();
15376   char *curr_music = getCurrentlyPlayingMusicFilename();
15377   char *next_music = getMusicInfoEntryFilename(music_nr);
15378
15379   if (!strEqual(curr_music, next_music))
15380     PlayMusicLoop(music_nr);
15381 }
15382
15383 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15384 {
15385   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15386   int offset = 0;
15387   int x = xx - offset;
15388   int y = yy - offset;
15389
15390   switch (sample)
15391   {
15392     case SOUND_blank:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15394       break;
15395
15396     case SOUND_roll:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15398       break;
15399
15400     case SOUND_stone:
15401       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15402       break;
15403
15404     case SOUND_nut:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15406       break;
15407
15408     case SOUND_crack:
15409       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15410       break;
15411
15412     case SOUND_bug:
15413       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15414       break;
15415
15416     case SOUND_tank:
15417       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15418       break;
15419
15420     case SOUND_android_clone:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15422       break;
15423
15424     case SOUND_android_move:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15426       break;
15427
15428     case SOUND_spring:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15430       break;
15431
15432     case SOUND_slurp:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15434       break;
15435
15436     case SOUND_eater:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15438       break;
15439
15440     case SOUND_eater_eat:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15442       break;
15443
15444     case SOUND_alien:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15446       break;
15447
15448     case SOUND_collect:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15450       break;
15451
15452     case SOUND_diamond:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15454       break;
15455
15456     case SOUND_squash:
15457       // !!! CHECK THIS !!!
15458 #if 1
15459       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15460 #else
15461       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15462 #endif
15463       break;
15464
15465     case SOUND_wonderfall:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15467       break;
15468
15469     case SOUND_drip:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15471       break;
15472
15473     case SOUND_push:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15475       break;
15476
15477     case SOUND_dirt:
15478       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15479       break;
15480
15481     case SOUND_acid:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15483       break;
15484
15485     case SOUND_ball:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15487       break;
15488
15489     case SOUND_slide:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15491       break;
15492
15493     case SOUND_wonder:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15495       break;
15496
15497     case SOUND_door:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15499       break;
15500
15501     case SOUND_exit_open:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15503       break;
15504
15505     case SOUND_exit_leave:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15507       break;
15508
15509     case SOUND_dynamite:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15511       break;
15512
15513     case SOUND_tick:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15515       break;
15516
15517     case SOUND_press:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15519       break;
15520
15521     case SOUND_wheel:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15523       break;
15524
15525     case SOUND_boom:
15526       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15527       break;
15528
15529     case SOUND_die:
15530       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15531       break;
15532
15533     case SOUND_time:
15534       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15535       break;
15536
15537     default:
15538       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15539       break;
15540   }
15541 }
15542
15543 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15544 {
15545   int element = map_element_SP_to_RND(element_sp);
15546   int action = map_action_SP_to_RND(action_sp);
15547   int offset = (setup.sp_show_border_elements ? 0 : 1);
15548   int x = xx - offset;
15549   int y = yy - offset;
15550
15551   PlayLevelSoundElementAction(x, y, element, action);
15552 }
15553
15554 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15555 {
15556   int element = map_element_MM_to_RND(element_mm);
15557   int action = map_action_MM_to_RND(action_mm);
15558   int offset = 0;
15559   int x = xx - offset;
15560   int y = yy - offset;
15561
15562   if (!IS_MM_ELEMENT(element))
15563     element = EL_MM_DEFAULT;
15564
15565   PlayLevelSoundElementAction(x, y, element, action);
15566 }
15567
15568 void PlaySound_MM(int sound_mm)
15569 {
15570   int sound = map_sound_MM_to_RND(sound_mm);
15571
15572   if (sound == SND_UNDEFINED)
15573     return;
15574
15575   PlaySound(sound);
15576 }
15577
15578 void PlaySoundLoop_MM(int sound_mm)
15579 {
15580   int sound = map_sound_MM_to_RND(sound_mm);
15581
15582   if (sound == SND_UNDEFINED)
15583     return;
15584
15585   PlaySoundLoop(sound);
15586 }
15587
15588 void StopSound_MM(int sound_mm)
15589 {
15590   int sound = map_sound_MM_to_RND(sound_mm);
15591
15592   if (sound == SND_UNDEFINED)
15593     return;
15594
15595   StopSound(sound);
15596 }
15597
15598 void RaiseScore(int value)
15599 {
15600   game.score += value;
15601
15602   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15603
15604   DisplayGameControlValues();
15605 }
15606
15607 void RaiseScoreElement(int element)
15608 {
15609   switch (element)
15610   {
15611     case EL_EMERALD:
15612     case EL_BD_DIAMOND:
15613     case EL_EMERALD_YELLOW:
15614     case EL_EMERALD_RED:
15615     case EL_EMERALD_PURPLE:
15616     case EL_SP_INFOTRON:
15617       RaiseScore(level.score[SC_EMERALD]);
15618       break;
15619     case EL_DIAMOND:
15620       RaiseScore(level.score[SC_DIAMOND]);
15621       break;
15622     case EL_CRYSTAL:
15623       RaiseScore(level.score[SC_CRYSTAL]);
15624       break;
15625     case EL_PEARL:
15626       RaiseScore(level.score[SC_PEARL]);
15627       break;
15628     case EL_BUG:
15629     case EL_BD_BUTTERFLY:
15630     case EL_SP_ELECTRON:
15631       RaiseScore(level.score[SC_BUG]);
15632       break;
15633     case EL_SPACESHIP:
15634     case EL_BD_FIREFLY:
15635     case EL_SP_SNIKSNAK:
15636       RaiseScore(level.score[SC_SPACESHIP]);
15637       break;
15638     case EL_YAMYAM:
15639     case EL_DARK_YAMYAM:
15640       RaiseScore(level.score[SC_YAMYAM]);
15641       break;
15642     case EL_ROBOT:
15643       RaiseScore(level.score[SC_ROBOT]);
15644       break;
15645     case EL_PACMAN:
15646       RaiseScore(level.score[SC_PACMAN]);
15647       break;
15648     case EL_NUT:
15649       RaiseScore(level.score[SC_NUT]);
15650       break;
15651     case EL_DYNAMITE:
15652     case EL_EM_DYNAMITE:
15653     case EL_SP_DISK_RED:
15654     case EL_DYNABOMB_INCREASE_NUMBER:
15655     case EL_DYNABOMB_INCREASE_SIZE:
15656     case EL_DYNABOMB_INCREASE_POWER:
15657       RaiseScore(level.score[SC_DYNAMITE]);
15658       break;
15659     case EL_SHIELD_NORMAL:
15660     case EL_SHIELD_DEADLY:
15661       RaiseScore(level.score[SC_SHIELD]);
15662       break;
15663     case EL_EXTRA_TIME:
15664       RaiseScore(level.extra_time_score);
15665       break;
15666     case EL_KEY_1:
15667     case EL_KEY_2:
15668     case EL_KEY_3:
15669     case EL_KEY_4:
15670     case EL_EM_KEY_1:
15671     case EL_EM_KEY_2:
15672     case EL_EM_KEY_3:
15673     case EL_EM_KEY_4:
15674     case EL_EMC_KEY_5:
15675     case EL_EMC_KEY_6:
15676     case EL_EMC_KEY_7:
15677     case EL_EMC_KEY_8:
15678     case EL_DC_KEY_WHITE:
15679       RaiseScore(level.score[SC_KEY]);
15680       break;
15681     default:
15682       RaiseScore(element_info[element].collect_score);
15683       break;
15684   }
15685 }
15686
15687 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15688 {
15689   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15690   {
15691     if (!quick_quit)
15692     {
15693       // prevent short reactivation of overlay buttons while closing door
15694       SetOverlayActive(FALSE);
15695       UnmapGameButtons();
15696
15697       // door may still be open due to skipped or envelope style request
15698       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15699     }
15700
15701     if (network.enabled)
15702       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15703     else
15704     {
15705       if (quick_quit)
15706         FadeSkipNextFadeIn();
15707
15708       SetGameStatus(GAME_MODE_MAIN);
15709
15710       DrawMainMenu();
15711     }
15712   }
15713   else          // continue playing the game
15714   {
15715     if (tape.playing && tape.deactivate_display)
15716       TapeDeactivateDisplayOff(TRUE);
15717
15718     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15719
15720     if (tape.playing && tape.deactivate_display)
15721       TapeDeactivateDisplayOn();
15722   }
15723 }
15724
15725 void RequestQuitGame(boolean escape_key_pressed)
15726 {
15727   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15728   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15729                         level_editor_test_game);
15730   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15731                           quick_quit || score_info_tape_play);
15732
15733   RequestQuitGameExt(skip_request, quick_quit,
15734                      "Do you really want to quit the game?");
15735 }
15736
15737 void RequestRestartGame(char *message)
15738 {
15739   game.restart_game_message = NULL;
15740
15741   boolean has_started_game = hasStartedNetworkGame();
15742   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15743
15744   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15745   {
15746     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15747   }
15748   else
15749   {
15750     // needed in case of envelope request to close game panel
15751     CloseDoor(DOOR_CLOSE_1);
15752
15753     SetGameStatus(GAME_MODE_MAIN);
15754
15755     DrawMainMenu();
15756   }
15757 }
15758
15759 void CheckGameOver(void)
15760 {
15761   static boolean last_game_over = FALSE;
15762   static int game_over_delay = 0;
15763   int game_over_delay_value = 50;
15764   boolean game_over = checkGameFailed();
15765
15766   // do not handle game over if request dialog is already active
15767   if (game.request_active)
15768     return;
15769
15770   // do not ask to play again if game was never actually played
15771   if (!game.GamePlayed)
15772     return;
15773
15774   if (!game_over)
15775   {
15776     last_game_over = FALSE;
15777     game_over_delay = game_over_delay_value;
15778
15779     return;
15780   }
15781
15782   if (game_over_delay > 0)
15783   {
15784     game_over_delay--;
15785
15786     return;
15787   }
15788
15789   if (last_game_over != game_over)
15790     game.restart_game_message = (hasStartedNetworkGame() ?
15791                                  "Game over! Play it again?" :
15792                                  "Game over!");
15793
15794   last_game_over = game_over;
15795 }
15796
15797 boolean checkGameSolved(void)
15798 {
15799   // set for all game engines if level was solved
15800   return game.LevelSolved_GameEnd;
15801 }
15802
15803 boolean checkGameFailed(void)
15804 {
15805   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15806     return (game_em.game_over && !game_em.level_solved);
15807   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15808     return (game_sp.game_over && !game_sp.level_solved);
15809   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15810     return (game_mm.game_over && !game_mm.level_solved);
15811   else                          // GAME_ENGINE_TYPE_RND
15812     return (game.GameOver && !game.LevelSolved);
15813 }
15814
15815 boolean checkGameEnded(void)
15816 {
15817   return (checkGameSolved() || checkGameFailed());
15818 }
15819
15820
15821 // ----------------------------------------------------------------------------
15822 // random generator functions
15823 // ----------------------------------------------------------------------------
15824
15825 unsigned int InitEngineRandom_RND(int seed)
15826 {
15827   game.num_random_calls = 0;
15828
15829   return InitEngineRandom(seed);
15830 }
15831
15832 unsigned int RND(int max)
15833 {
15834   if (max > 0)
15835   {
15836     game.num_random_calls++;
15837
15838     return GetEngineRandom(max);
15839   }
15840
15841   return 0;
15842 }
15843
15844
15845 // ----------------------------------------------------------------------------
15846 // game engine snapshot handling functions
15847 // ----------------------------------------------------------------------------
15848
15849 struct EngineSnapshotInfo
15850 {
15851   // runtime values for custom element collect score
15852   int collect_score[NUM_CUSTOM_ELEMENTS];
15853
15854   // runtime values for group element choice position
15855   int choice_pos[NUM_GROUP_ELEMENTS];
15856
15857   // runtime values for belt position animations
15858   int belt_graphic[4][NUM_BELT_PARTS];
15859   int belt_anim_mode[4][NUM_BELT_PARTS];
15860 };
15861
15862 static struct EngineSnapshotInfo engine_snapshot_rnd;
15863 static char *snapshot_level_identifier = NULL;
15864 static int snapshot_level_nr = -1;
15865
15866 static void SaveEngineSnapshotValues_RND(void)
15867 {
15868   static int belt_base_active_element[4] =
15869   {
15870     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15871     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15872     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15873     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15874   };
15875   int i, j;
15876
15877   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15878   {
15879     int element = EL_CUSTOM_START + i;
15880
15881     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15882   }
15883
15884   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15885   {
15886     int element = EL_GROUP_START + i;
15887
15888     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15889   }
15890
15891   for (i = 0; i < 4; i++)
15892   {
15893     for (j = 0; j < NUM_BELT_PARTS; j++)
15894     {
15895       int element = belt_base_active_element[i] + j;
15896       int graphic = el2img(element);
15897       int anim_mode = graphic_info[graphic].anim_mode;
15898
15899       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15900       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15901     }
15902   }
15903 }
15904
15905 static void LoadEngineSnapshotValues_RND(void)
15906 {
15907   unsigned int num_random_calls = game.num_random_calls;
15908   int i, j;
15909
15910   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15911   {
15912     int element = EL_CUSTOM_START + i;
15913
15914     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15915   }
15916
15917   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15918   {
15919     int element = EL_GROUP_START + i;
15920
15921     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15922   }
15923
15924   for (i = 0; i < 4; i++)
15925   {
15926     for (j = 0; j < NUM_BELT_PARTS; j++)
15927     {
15928       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15929       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15930
15931       graphic_info[graphic].anim_mode = anim_mode;
15932     }
15933   }
15934
15935   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15936   {
15937     InitRND(tape.random_seed);
15938     for (i = 0; i < num_random_calls; i++)
15939       RND(1);
15940   }
15941
15942   if (game.num_random_calls != num_random_calls)
15943   {
15944     Error("number of random calls out of sync");
15945     Error("number of random calls should be %d", num_random_calls);
15946     Error("number of random calls is %d", game.num_random_calls);
15947
15948     Fail("this should not happen -- please debug");
15949   }
15950 }
15951
15952 void FreeEngineSnapshotSingle(void)
15953 {
15954   FreeSnapshotSingle();
15955
15956   setString(&snapshot_level_identifier, NULL);
15957   snapshot_level_nr = -1;
15958 }
15959
15960 void FreeEngineSnapshotList(void)
15961 {
15962   FreeSnapshotList();
15963 }
15964
15965 static ListNode *SaveEngineSnapshotBuffers(void)
15966 {
15967   ListNode *buffers = NULL;
15968
15969   // copy some special values to a structure better suited for the snapshot
15970
15971   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15972     SaveEngineSnapshotValues_RND();
15973   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15974     SaveEngineSnapshotValues_EM();
15975   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15976     SaveEngineSnapshotValues_SP(&buffers);
15977   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15978     SaveEngineSnapshotValues_MM(&buffers);
15979
15980   // save values stored in special snapshot structure
15981
15982   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15983     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15984   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15985     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15986   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15987     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15988   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15989     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15990
15991   // save further RND engine values
15992
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15996
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16002
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16006
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16008
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16011
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16030
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16033
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16037
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16040
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16047
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16050
16051 #if 0
16052   ListNode *node = engine_snapshot_list_rnd;
16053   int num_bytes = 0;
16054
16055   while (node != NULL)
16056   {
16057     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16058
16059     node = node->next;
16060   }
16061
16062   Debug("game:playing:SaveEngineSnapshotBuffers",
16063         "size of engine snapshot: %d bytes", num_bytes);
16064 #endif
16065
16066   return buffers;
16067 }
16068
16069 void SaveEngineSnapshotSingle(void)
16070 {
16071   ListNode *buffers = SaveEngineSnapshotBuffers();
16072
16073   // finally save all snapshot buffers to single snapshot
16074   SaveSnapshotSingle(buffers);
16075
16076   // save level identification information
16077   setString(&snapshot_level_identifier, leveldir_current->identifier);
16078   snapshot_level_nr = level_nr;
16079 }
16080
16081 boolean CheckSaveEngineSnapshotToList(void)
16082 {
16083   boolean save_snapshot =
16084     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16085      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16086       game.snapshot.changed_action) ||
16087      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16088       game.snapshot.collected_item));
16089
16090   game.snapshot.changed_action = FALSE;
16091   game.snapshot.collected_item = FALSE;
16092   game.snapshot.save_snapshot = save_snapshot;
16093
16094   return save_snapshot;
16095 }
16096
16097 void SaveEngineSnapshotToList(void)
16098 {
16099   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16100       tape.quick_resume)
16101     return;
16102
16103   ListNode *buffers = SaveEngineSnapshotBuffers();
16104
16105   // finally save all snapshot buffers to snapshot list
16106   SaveSnapshotToList(buffers);
16107 }
16108
16109 void SaveEngineSnapshotToListInitial(void)
16110 {
16111   FreeEngineSnapshotList();
16112
16113   SaveEngineSnapshotToList();
16114 }
16115
16116 static void LoadEngineSnapshotValues(void)
16117 {
16118   // restore special values from snapshot structure
16119
16120   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16121     LoadEngineSnapshotValues_RND();
16122   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16123     LoadEngineSnapshotValues_EM();
16124   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16125     LoadEngineSnapshotValues_SP();
16126   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16127     LoadEngineSnapshotValues_MM();
16128 }
16129
16130 void LoadEngineSnapshotSingle(void)
16131 {
16132   LoadSnapshotSingle();
16133
16134   LoadEngineSnapshotValues();
16135 }
16136
16137 static void LoadEngineSnapshot_Undo(int steps)
16138 {
16139   LoadSnapshotFromList_Older(steps);
16140
16141   LoadEngineSnapshotValues();
16142 }
16143
16144 static void LoadEngineSnapshot_Redo(int steps)
16145 {
16146   LoadSnapshotFromList_Newer(steps);
16147
16148   LoadEngineSnapshotValues();
16149 }
16150
16151 boolean CheckEngineSnapshotSingle(void)
16152 {
16153   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16154           snapshot_level_nr == level_nr);
16155 }
16156
16157 boolean CheckEngineSnapshotList(void)
16158 {
16159   return CheckSnapshotList();
16160 }
16161
16162
16163 // ---------- new game button stuff -------------------------------------------
16164
16165 static struct
16166 {
16167   int graphic;
16168   struct XY *pos;
16169   int gadget_id;
16170   boolean *setup_value;
16171   boolean allowed_on_tape;
16172   boolean is_touch_button;
16173   char *infotext;
16174 } gamebutton_info[NUM_GAME_BUTTONS] =
16175 {
16176   {
16177     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16178     GAME_CTRL_ID_STOP,                          NULL,
16179     TRUE, FALSE,                                "stop game"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16183     GAME_CTRL_ID_PAUSE,                         NULL,
16184     TRUE, FALSE,                                "pause game"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16188     GAME_CTRL_ID_PLAY,                          NULL,
16189     TRUE, FALSE,                                "play game"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16193     GAME_CTRL_ID_UNDO,                          NULL,
16194     TRUE, FALSE,                                "undo step"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16198     GAME_CTRL_ID_REDO,                          NULL,
16199     TRUE, FALSE,                                "redo step"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16203     GAME_CTRL_ID_SAVE,                          NULL,
16204     TRUE, FALSE,                                "save game"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16208     GAME_CTRL_ID_PAUSE2,                        NULL,
16209     TRUE, FALSE,                                "pause game"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16213     GAME_CTRL_ID_LOAD,                          NULL,
16214     TRUE, FALSE,                                "load game"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16218     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16219     FALSE, FALSE,                               "stop game"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16223     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16224     FALSE, FALSE,                               "pause game"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16228     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16229     FALSE, FALSE,                               "play game"
16230   },
16231   {
16232     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16233     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16234     FALSE, TRUE,                                "stop game"
16235   },
16236   {
16237     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16238     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16239     FALSE, TRUE,                                "pause game"
16240   },
16241   {
16242     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16243     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16244     TRUE, FALSE,                                "background music on/off"
16245   },
16246   {
16247     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16248     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16249     TRUE, FALSE,                                "sound loops on/off"
16250   },
16251   {
16252     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16253     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16254     TRUE, FALSE,                                "normal sounds on/off"
16255   },
16256   {
16257     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16258     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16259     FALSE, FALSE,                               "background music on/off"
16260   },
16261   {
16262     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16263     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16264     FALSE, FALSE,                               "sound loops on/off"
16265   },
16266   {
16267     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16268     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16269     FALSE, FALSE,                               "normal sounds on/off"
16270   }
16271 };
16272
16273 void CreateGameButtons(void)
16274 {
16275   int i;
16276
16277   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16278   {
16279     int graphic = gamebutton_info[i].graphic;
16280     struct GraphicInfo *gfx = &graphic_info[graphic];
16281     struct XY *pos = gamebutton_info[i].pos;
16282     struct GadgetInfo *gi;
16283     int button_type;
16284     boolean checked;
16285     unsigned int event_mask;
16286     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16287     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16288     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16289     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16290     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16291     int gd_x   = gfx->src_x;
16292     int gd_y   = gfx->src_y;
16293     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16294     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16295     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16296     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16297     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16298     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16299     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16300     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16301     int id = i;
16302
16303     // do not use touch buttons if touch controls are disabled
16304     if (is_touch_button &&
16305         strEqual(setup.touch.control_type, TOUCH_CONTROL_OFF))
16306       continue;
16307
16308     if (gfx->bitmap == NULL)
16309     {
16310       game_gadget[id] = NULL;
16311
16312       continue;
16313     }
16314
16315     if (id == GAME_CTRL_ID_STOP ||
16316         id == GAME_CTRL_ID_PANEL_STOP ||
16317         id == GAME_CTRL_ID_TOUCH_STOP ||
16318         id == GAME_CTRL_ID_PLAY ||
16319         id == GAME_CTRL_ID_PANEL_PLAY ||
16320         id == GAME_CTRL_ID_SAVE ||
16321         id == GAME_CTRL_ID_LOAD)
16322     {
16323       button_type = GD_TYPE_NORMAL_BUTTON;
16324       checked = FALSE;
16325       event_mask = GD_EVENT_RELEASED;
16326     }
16327     else if (id == GAME_CTRL_ID_UNDO ||
16328              id == GAME_CTRL_ID_REDO)
16329     {
16330       button_type = GD_TYPE_NORMAL_BUTTON;
16331       checked = FALSE;
16332       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16333     }
16334     else
16335     {
16336       button_type = GD_TYPE_CHECK_BUTTON;
16337       checked = (gamebutton_info[i].setup_value != NULL ?
16338                  *gamebutton_info[i].setup_value : FALSE);
16339       event_mask = GD_EVENT_PRESSED;
16340     }
16341
16342     gi = CreateGadget(GDI_CUSTOM_ID, id,
16343                       GDI_IMAGE_ID, graphic,
16344                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16345                       GDI_X, base_x + x,
16346                       GDI_Y, base_y + y,
16347                       GDI_WIDTH, gfx->width,
16348                       GDI_HEIGHT, gfx->height,
16349                       GDI_TYPE, button_type,
16350                       GDI_STATE, GD_BUTTON_UNPRESSED,
16351                       GDI_CHECKED, checked,
16352                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16353                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16354                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16355                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16356                       GDI_DIRECT_DRAW, FALSE,
16357                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16358                       GDI_EVENT_MASK, event_mask,
16359                       GDI_CALLBACK_ACTION, HandleGameButtons,
16360                       GDI_END);
16361
16362     if (gi == NULL)
16363       Fail("cannot create gadget");
16364
16365     game_gadget[id] = gi;
16366   }
16367 }
16368
16369 void FreeGameButtons(void)
16370 {
16371   int i;
16372
16373   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16374     FreeGadget(game_gadget[i]);
16375 }
16376
16377 static void UnmapGameButtonsAtSamePosition(int id)
16378 {
16379   int i;
16380
16381   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16382     if (i != id &&
16383         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16384         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16385       UnmapGadget(game_gadget[i]);
16386 }
16387
16388 static void UnmapGameButtonsAtSamePosition_All(void)
16389 {
16390   if (setup.show_load_save_buttons)
16391   {
16392     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16393     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16394     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16395   }
16396   else if (setup.show_undo_redo_buttons)
16397   {
16398     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16399     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16400     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16401   }
16402   else
16403   {
16404     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16405     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16406     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16407
16408     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16409     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16410     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16411   }
16412 }
16413
16414 void MapLoadSaveButtons(void)
16415 {
16416   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16417   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16418
16419   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16420   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16421 }
16422
16423 void MapUndoRedoButtons(void)
16424 {
16425   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16426   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16427
16428   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16429   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16430 }
16431
16432 void ModifyPauseButtons(void)
16433 {
16434   static int ids[] =
16435   {
16436     GAME_CTRL_ID_PAUSE,
16437     GAME_CTRL_ID_PAUSE2,
16438     GAME_CTRL_ID_PANEL_PAUSE,
16439     GAME_CTRL_ID_TOUCH_PAUSE,
16440     -1
16441   };
16442   int i;
16443
16444   for (i = 0; ids[i] > -1; i++)
16445     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16446 }
16447
16448 static void MapGameButtonsExt(boolean on_tape)
16449 {
16450   int i;
16451
16452   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16453   {
16454     if ((i == GAME_CTRL_ID_UNDO ||
16455          i == GAME_CTRL_ID_REDO) &&
16456         game_status != GAME_MODE_PLAYING)
16457       continue;
16458
16459     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16460       MapGadget(game_gadget[i]);
16461   }
16462
16463   UnmapGameButtonsAtSamePosition_All();
16464
16465   RedrawGameButtons();
16466 }
16467
16468 static void UnmapGameButtonsExt(boolean on_tape)
16469 {
16470   int i;
16471
16472   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16473     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16474       UnmapGadget(game_gadget[i]);
16475 }
16476
16477 static void RedrawGameButtonsExt(boolean on_tape)
16478 {
16479   int i;
16480
16481   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16482     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16483       RedrawGadget(game_gadget[i]);
16484 }
16485
16486 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16487 {
16488   if (gi == NULL)
16489     return;
16490
16491   gi->checked = state;
16492 }
16493
16494 static void RedrawSoundButtonGadget(int id)
16495 {
16496   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16497              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16498              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16499              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16500              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16501              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16502              id);
16503
16504   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16505   RedrawGadget(game_gadget[id2]);
16506 }
16507
16508 void MapGameButtons(void)
16509 {
16510   MapGameButtonsExt(FALSE);
16511 }
16512
16513 void UnmapGameButtons(void)
16514 {
16515   UnmapGameButtonsExt(FALSE);
16516 }
16517
16518 void RedrawGameButtons(void)
16519 {
16520   RedrawGameButtonsExt(FALSE);
16521 }
16522
16523 void MapGameButtonsOnTape(void)
16524 {
16525   MapGameButtonsExt(TRUE);
16526 }
16527
16528 void UnmapGameButtonsOnTape(void)
16529 {
16530   UnmapGameButtonsExt(TRUE);
16531 }
16532
16533 void RedrawGameButtonsOnTape(void)
16534 {
16535   RedrawGameButtonsExt(TRUE);
16536 }
16537
16538 static void GameUndoRedoExt(void)
16539 {
16540   ClearPlayerAction();
16541
16542   tape.pausing = TRUE;
16543
16544   RedrawPlayfield();
16545   UpdateAndDisplayGameControlValues();
16546
16547   DrawCompleteVideoDisplay();
16548   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16549   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16550   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16551
16552   ModifyPauseButtons();
16553
16554   BackToFront();
16555 }
16556
16557 static void GameUndo(int steps)
16558 {
16559   if (!CheckEngineSnapshotList())
16560     return;
16561
16562   int tape_property_bits = tape.property_bits;
16563
16564   LoadEngineSnapshot_Undo(steps);
16565
16566   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16567
16568   GameUndoRedoExt();
16569 }
16570
16571 static void GameRedo(int steps)
16572 {
16573   if (!CheckEngineSnapshotList())
16574     return;
16575
16576   int tape_property_bits = tape.property_bits;
16577
16578   LoadEngineSnapshot_Redo(steps);
16579
16580   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16581
16582   GameUndoRedoExt();
16583 }
16584
16585 static void HandleGameButtonsExt(int id, int button)
16586 {
16587   static boolean game_undo_executed = FALSE;
16588   int steps = BUTTON_STEPSIZE(button);
16589   boolean handle_game_buttons =
16590     (game_status == GAME_MODE_PLAYING ||
16591      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16592
16593   if (!handle_game_buttons)
16594     return;
16595
16596   switch (id)
16597   {
16598     case GAME_CTRL_ID_STOP:
16599     case GAME_CTRL_ID_PANEL_STOP:
16600     case GAME_CTRL_ID_TOUCH_STOP:
16601       TapeStopGame();
16602
16603       break;
16604
16605     case GAME_CTRL_ID_PAUSE:
16606     case GAME_CTRL_ID_PAUSE2:
16607     case GAME_CTRL_ID_PANEL_PAUSE:
16608     case GAME_CTRL_ID_TOUCH_PAUSE:
16609       if (network.enabled && game_status == GAME_MODE_PLAYING)
16610       {
16611         if (tape.pausing)
16612           SendToServer_ContinuePlaying();
16613         else
16614           SendToServer_PausePlaying();
16615       }
16616       else
16617         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16618
16619       game_undo_executed = FALSE;
16620
16621       break;
16622
16623     case GAME_CTRL_ID_PLAY:
16624     case GAME_CTRL_ID_PANEL_PLAY:
16625       if (game_status == GAME_MODE_MAIN)
16626       {
16627         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16628       }
16629       else if (tape.pausing)
16630       {
16631         if (network.enabled)
16632           SendToServer_ContinuePlaying();
16633         else
16634           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16635       }
16636       break;
16637
16638     case GAME_CTRL_ID_UNDO:
16639       // Important: When using "save snapshot when collecting an item" mode,
16640       // load last (current) snapshot for first "undo" after pressing "pause"
16641       // (else the last-but-one snapshot would be loaded, because the snapshot
16642       // pointer already points to the last snapshot when pressing "pause",
16643       // which is fine for "every step/move" mode, but not for "every collect")
16644       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16645           !game_undo_executed)
16646         steps--;
16647
16648       game_undo_executed = TRUE;
16649
16650       GameUndo(steps);
16651       break;
16652
16653     case GAME_CTRL_ID_REDO:
16654       GameRedo(steps);
16655       break;
16656
16657     case GAME_CTRL_ID_SAVE:
16658       TapeQuickSave();
16659       break;
16660
16661     case GAME_CTRL_ID_LOAD:
16662       TapeQuickLoad();
16663       break;
16664
16665     case SOUND_CTRL_ID_MUSIC:
16666     case SOUND_CTRL_ID_PANEL_MUSIC:
16667       if (setup.sound_music)
16668       { 
16669         setup.sound_music = FALSE;
16670
16671         FadeMusic();
16672       }
16673       else if (audio.music_available)
16674       { 
16675         setup.sound = setup.sound_music = TRUE;
16676
16677         SetAudioMode(setup.sound);
16678
16679         if (game_status == GAME_MODE_PLAYING)
16680           PlayLevelMusic();
16681       }
16682
16683       RedrawSoundButtonGadget(id);
16684
16685       break;
16686
16687     case SOUND_CTRL_ID_LOOPS:
16688     case SOUND_CTRL_ID_PANEL_LOOPS:
16689       if (setup.sound_loops)
16690         setup.sound_loops = FALSE;
16691       else if (audio.loops_available)
16692       {
16693         setup.sound = setup.sound_loops = TRUE;
16694
16695         SetAudioMode(setup.sound);
16696       }
16697
16698       RedrawSoundButtonGadget(id);
16699
16700       break;
16701
16702     case SOUND_CTRL_ID_SIMPLE:
16703     case SOUND_CTRL_ID_PANEL_SIMPLE:
16704       if (setup.sound_simple)
16705         setup.sound_simple = FALSE;
16706       else if (audio.sound_available)
16707       {
16708         setup.sound = setup.sound_simple = TRUE;
16709
16710         SetAudioMode(setup.sound);
16711       }
16712
16713       RedrawSoundButtonGadget(id);
16714
16715       break;
16716
16717     default:
16718       break;
16719   }
16720 }
16721
16722 static void HandleGameButtons(struct GadgetInfo *gi)
16723 {
16724   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16725 }
16726
16727 void HandleSoundButtonKeys(Key key)
16728 {
16729   if (key == setup.shortcut.sound_simple)
16730     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16731   else if (key == setup.shortcut.sound_loops)
16732     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16733   else if (key == setup.shortcut.sound_music)
16734     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16735 }