872b4e9b2b769f61906a6063006cfd0b1a5f51e8
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_push_delay =
3099     (game.engine_version > VERSION_IDENT(4,3,7,1));
3100
3101   game_em.use_snap_key_bug =
3102     (game.engine_version < VERSION_IDENT(4,0,1,0));
3103
3104   game_em.use_random_bug =
3105     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3106
3107   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3108
3109   game_em.use_old_explosions            = use_old_em_engine;
3110   game_em.use_old_android               = use_old_em_engine;
3111   game_em.use_old_push_elements         = use_old_em_engine;
3112   game_em.use_old_push_into_acid        = use_old_em_engine;
3113
3114   game_em.use_wrap_around               = !use_old_em_engine;
3115
3116   // --------------------------------------------------------------------------
3117
3118   // set maximal allowed number of custom element changes per game frame
3119   game.max_num_changes_per_frame = 1;
3120
3121   // default scan direction: scan playfield from top/left to bottom/right
3122   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3123
3124   // dynamically adjust element properties according to game engine version
3125   InitElementPropertiesEngine(game.engine_version);
3126
3127   // ---------- initialize special element properties -------------------------
3128
3129   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3130   if (use_amoeba_dropping_cannot_fall_bug)
3131     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3132
3133   // ---------- initialize player's initial move delay ------------------------
3134
3135   // dynamically adjust player properties according to level information
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137     game.initial_move_delay_value[i] =
3138       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3139
3140   // dynamically adjust player properties according to game engine version
3141   for (i = 0; i < MAX_PLAYERS; i++)
3142     game.initial_move_delay[i] =
3143       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3144        game.initial_move_delay_value[i] : 0);
3145
3146   // ---------- initialize player's initial push delay ------------------------
3147
3148   // dynamically adjust player properties according to game engine version
3149   game.initial_push_delay_value =
3150     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3151
3152   // ---------- initialize changing elements ----------------------------------
3153
3154   // initialize changing elements information
3155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3156   {
3157     struct ElementInfo *ei = &element_info[i];
3158
3159     // this pointer might have been changed in the level editor
3160     ei->change = &ei->change_page[0];
3161
3162     if (!IS_CUSTOM_ELEMENT(i))
3163     {
3164       ei->change->target_element = EL_EMPTY_SPACE;
3165       ei->change->delay_fixed = 0;
3166       ei->change->delay_random = 0;
3167       ei->change->delay_frames = 1;
3168     }
3169
3170     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3171     {
3172       ei->has_change_event[j] = FALSE;
3173
3174       ei->event_page_nr[j] = 0;
3175       ei->event_page[j] = &ei->change_page[0];
3176     }
3177   }
3178
3179   // add changing elements from pre-defined list
3180   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3181   {
3182     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3183     struct ElementInfo *ei = &element_info[ch_delay->element];
3184
3185     ei->change->target_element       = ch_delay->target_element;
3186     ei->change->delay_fixed          = ch_delay->change_delay;
3187
3188     ei->change->pre_change_function  = ch_delay->pre_change_function;
3189     ei->change->change_function      = ch_delay->change_function;
3190     ei->change->post_change_function = ch_delay->post_change_function;
3191
3192     ei->change->can_change = TRUE;
3193     ei->change->can_change_or_has_action = TRUE;
3194
3195     ei->has_change_event[CE_DELAY] = TRUE;
3196
3197     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3198     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3199   }
3200
3201   // ---------- initialize if element can trigger global animations -----------
3202
3203   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3204   {
3205     struct ElementInfo *ei = &element_info[i];
3206
3207     ei->has_anim_event = FALSE;
3208   }
3209
3210   InitGlobalAnimEventsForCustomElements();
3211
3212   // ---------- initialize internal run-time variables ------------------------
3213
3214   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3215   {
3216     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3217
3218     for (j = 0; j < ei->num_change_pages; j++)
3219     {
3220       ei->change_page[j].can_change_or_has_action =
3221         (ei->change_page[j].can_change |
3222          ei->change_page[j].has_action);
3223     }
3224   }
3225
3226   // add change events from custom element configuration
3227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3228   {
3229     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       if (!ei->change_page[j].can_change_or_has_action)
3234         continue;
3235
3236       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3237       {
3238         // only add event page for the first page found with this event
3239         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3240         {
3241           ei->has_change_event[k] = TRUE;
3242
3243           ei->event_page_nr[k] = j;
3244           ei->event_page[k] = &ei->change_page[j];
3245         }
3246       }
3247     }
3248   }
3249
3250   // ---------- initialize reference elements in change conditions ------------
3251
3252   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3253   {
3254     int element = EL_CUSTOM_START + i;
3255     struct ElementInfo *ei = &element_info[element];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       int trigger_element = ei->change_page[j].initial_trigger_element;
3260
3261       if (trigger_element >= EL_PREV_CE_8 &&
3262           trigger_element <= EL_NEXT_CE_8)
3263         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3264
3265       ei->change_page[j].trigger_element = trigger_element;
3266     }
3267   }
3268
3269   // ---------- initialize run-time trigger player and element ----------------
3270
3271   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3272   {
3273     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3274
3275     for (j = 0; j < ei->num_change_pages; j++)
3276     {
3277       struct ElementChangeInfo *change = &ei->change_page[j];
3278
3279       change->actual_trigger_element = EL_EMPTY;
3280       change->actual_trigger_player = EL_EMPTY;
3281       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3282       change->actual_trigger_side = CH_SIDE_NONE;
3283       change->actual_trigger_ce_value = 0;
3284       change->actual_trigger_ce_score = 0;
3285       change->actual_trigger_x = -1;
3286       change->actual_trigger_y = -1;
3287     }
3288   }
3289
3290   // ---------- initialize trigger events -------------------------------------
3291
3292   // initialize trigger events information
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3295       trigger_events[i][j] = FALSE;
3296
3297   // add trigger events from element change event properties
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299   {
3300     struct ElementInfo *ei = &element_info[i];
3301
3302     for (j = 0; j < ei->num_change_pages; j++)
3303     {
3304       struct ElementChangeInfo *change = &ei->change_page[j];
3305
3306       if (!change->can_change_or_has_action)
3307         continue;
3308
3309       if (change->has_event[CE_BY_OTHER_ACTION])
3310       {
3311         int trigger_element = change->trigger_element;
3312
3313         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3314         {
3315           if (change->has_event[k])
3316           {
3317             if (IS_GROUP_ELEMENT(trigger_element))
3318             {
3319               struct ElementGroupInfo *group =
3320                 element_info[trigger_element].group;
3321
3322               for (l = 0; l < group->num_elements_resolved; l++)
3323                 trigger_events[group->element_resolved[l]][k] = TRUE;
3324             }
3325             else if (trigger_element == EL_ANY_ELEMENT)
3326               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3327                 trigger_events[l][k] = TRUE;
3328             else
3329               trigger_events[trigger_element][k] = TRUE;
3330           }
3331         }
3332       }
3333     }
3334   }
3335
3336   // ---------- initialize push delay -----------------------------------------
3337
3338   // initialize push delay values to default
3339   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3340   {
3341     if (!IS_CUSTOM_ELEMENT(i))
3342     {
3343       // set default push delay values (corrected since version 3.0.7-1)
3344       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3345       {
3346         element_info[i].push_delay_fixed = 2;
3347         element_info[i].push_delay_random = 8;
3348       }
3349       else
3350       {
3351         element_info[i].push_delay_fixed = 8;
3352         element_info[i].push_delay_random = 8;
3353       }
3354     }
3355   }
3356
3357   // set push delay value for certain elements from pre-defined list
3358   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = push_delay_list[i].element;
3361
3362     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3363     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3364   }
3365
3366   // set push delay value for Supaplex elements for newer engine versions
3367   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3368   {
3369     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3370     {
3371       if (IS_SP_ELEMENT(i))
3372       {
3373         // set SP push delay to just enough to push under a falling zonk
3374         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3375
3376         element_info[i].push_delay_fixed  = delay;
3377         element_info[i].push_delay_random = 0;
3378       }
3379     }
3380   }
3381
3382   // ---------- initialize move stepsize --------------------------------------
3383
3384   // initialize move stepsize values to default
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3388
3389   // set move stepsize value for certain elements from pre-defined list
3390   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3391   {
3392     int e = move_stepsize_list[i].element;
3393
3394     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3395
3396     // set move stepsize value for certain elements for older engine versions
3397     if (use_old_move_stepsize_for_magic_wall)
3398     {
3399       if (e == EL_MAGIC_WALL_FILLING ||
3400           e == EL_MAGIC_WALL_EMPTYING ||
3401           e == EL_BD_MAGIC_WALL_FILLING ||
3402           e == EL_BD_MAGIC_WALL_EMPTYING)
3403         element_info[e].move_stepsize *= 2;
3404     }
3405   }
3406
3407   // ---------- initialize collect score --------------------------------------
3408
3409   // initialize collect score values for custom elements from initial value
3410   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3411     if (IS_CUSTOM_ELEMENT(i))
3412       element_info[i].collect_score = element_info[i].collect_score_initial;
3413
3414   // ---------- initialize collect count --------------------------------------
3415
3416   // initialize collect count values for non-custom elements
3417   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3418     if (!IS_CUSTOM_ELEMENT(i))
3419       element_info[i].collect_count_initial = 0;
3420
3421   // add collect count values for all elements from pre-defined list
3422   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3423     element_info[collect_count_list[i].element].collect_count_initial =
3424       collect_count_list[i].count;
3425
3426   // ---------- initialize access direction -----------------------------------
3427
3428   // initialize access direction values to default (access from every side)
3429   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3430     if (!IS_CUSTOM_ELEMENT(i))
3431       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3432
3433   // set access direction value for certain elements from pre-defined list
3434   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3435     element_info[access_direction_list[i].element].access_direction =
3436       access_direction_list[i].direction;
3437
3438   // ---------- initialize explosion content ----------------------------------
3439   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3440   {
3441     if (IS_CUSTOM_ELEMENT(i))
3442       continue;
3443
3444     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3445     {
3446       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3447
3448       element_info[i].content.e[x][y] =
3449         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3450          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3451          i == EL_PLAYER_3 ? EL_EMERALD :
3452          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3453          i == EL_MOLE ? EL_EMERALD_RED :
3454          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3455          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3456          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3457          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3458          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3459          i == EL_WALL_EMERALD ? EL_EMERALD :
3460          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3461          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3462          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3463          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3464          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3465          i == EL_WALL_PEARL ? EL_PEARL :
3466          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3467          EL_EMPTY);
3468     }
3469   }
3470
3471   // ---------- initialize recursion detection --------------------------------
3472   recursion_loop_depth = 0;
3473   recursion_loop_detected = FALSE;
3474   recursion_loop_element = EL_UNDEFINED;
3475
3476   // ---------- initialize graphics engine ------------------------------------
3477   game.scroll_delay_value =
3478     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3479      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3480      !setup.forced_scroll_delay           ? 0 :
3481      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3482   if (game.forced_scroll_delay_value == -1)
3483     game.scroll_delay_value =
3484       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3485
3486   // ---------- initialize game engine snapshots ------------------------------
3487   for (i = 0; i < MAX_PLAYERS; i++)
3488     game.snapshot.last_action[i] = 0;
3489   game.snapshot.changed_action = FALSE;
3490   game.snapshot.collected_item = FALSE;
3491   game.snapshot.mode =
3492     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3493      SNAPSHOT_MODE_EVERY_STEP :
3494      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3495      SNAPSHOT_MODE_EVERY_MOVE :
3496      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3497      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3498   game.snapshot.save_snapshot = FALSE;
3499
3500   // ---------- initialize level time for Supaplex engine ---------------------
3501   // Supaplex levels with time limit currently unsupported -- should be added
3502   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3503     level.time = 0;
3504
3505   // ---------- initialize flags for handling game actions --------------------
3506
3507   // set flags for game actions to default values
3508   game.use_key_actions = TRUE;
3509   game.use_mouse_actions = FALSE;
3510
3511   // when using Mirror Magic game engine, handle mouse events only
3512   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3513   {
3514     game.use_key_actions = FALSE;
3515     game.use_mouse_actions = TRUE;
3516   }
3517
3518   // check for custom elements with mouse click events
3519   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3520   {
3521     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3522     {
3523       int element = EL_CUSTOM_START + i;
3524
3525       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3526           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3527           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3528           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3529         game.use_mouse_actions = TRUE;
3530     }
3531   }
3532 }
3533
3534 static int get_num_special_action(int element, int action_first,
3535                                   int action_last)
3536 {
3537   int num_special_action = 0;
3538   int i, j;
3539
3540   for (i = action_first; i <= action_last; i++)
3541   {
3542     boolean found = FALSE;
3543
3544     for (j = 0; j < NUM_DIRECTIONS; j++)
3545       if (el_act_dir2img(element, i, j) !=
3546           el_act_dir2img(element, ACTION_DEFAULT, j))
3547         found = TRUE;
3548
3549     if (found)
3550       num_special_action++;
3551     else
3552       break;
3553   }
3554
3555   return num_special_action;
3556 }
3557
3558
3559 // ============================================================================
3560 // InitGame()
3561 // ----------------------------------------------------------------------------
3562 // initialize and start new game
3563 // ============================================================================
3564
3565 #if DEBUG_INIT_PLAYER
3566 static void DebugPrintPlayerStatus(char *message)
3567 {
3568   int i;
3569
3570   if (!options.debug)
3571     return;
3572
3573   Debug("game:init:player", "%s:", message);
3574
3575   for (i = 0; i < MAX_PLAYERS; i++)
3576   {
3577     struct PlayerInfo *player = &stored_player[i];
3578
3579     Debug("game:init:player",
3580           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3581           i + 1,
3582           player->present,
3583           player->connected,
3584           player->connected_locally,
3585           player->connected_network,
3586           player->active,
3587           (local_player == player ? " (local player)" : ""));
3588   }
3589 }
3590 #endif
3591
3592 void InitGame(void)
3593 {
3594   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3595   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3596   int fade_mask = REDRAW_FIELD;
3597   boolean restarting = (game_status == GAME_MODE_PLAYING);
3598   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3599   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3600   int initial_move_dir = MV_DOWN;
3601   int i, j, x, y;
3602
3603   // required here to update video display before fading (FIX THIS)
3604   DrawMaskedBorder(REDRAW_DOOR_2);
3605
3606   if (!game.restart_level)
3607     CloseDoor(DOOR_CLOSE_1);
3608
3609   if (restarting)
3610   {
3611     // force fading out global animations displayed during game play
3612     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3613   }
3614   else
3615   {
3616     SetGameStatus(GAME_MODE_PLAYING);
3617   }
3618
3619   if (level_editor_test_game)
3620     FadeSkipNextFadeOut();
3621   else
3622     FadeSetEnterScreen();
3623
3624   if (CheckFadeAll())
3625     fade_mask = REDRAW_ALL;
3626
3627   FadeLevelSoundsAndMusic();
3628
3629   ExpireSoundLoops(TRUE);
3630
3631   FadeOut(fade_mask);
3632
3633   if (restarting)
3634   {
3635     // force restarting global animations displayed during game play
3636     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3637
3638     // this is required for "transforming" fade modes like cross-fading
3639     // (else global animations will be stopped, but not restarted here)
3640     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3641
3642     SetGameStatus(GAME_MODE_PLAYING);
3643   }
3644
3645   if (level_editor_test_game)
3646     FadeSkipNextFadeIn();
3647
3648   // needed if different viewport properties defined for playing
3649   ChangeViewportPropertiesIfNeeded();
3650
3651   ClearField();
3652
3653   DrawCompleteVideoDisplay();
3654
3655   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3656
3657   InitGameEngine();
3658   InitGameControlValues();
3659
3660   if (tape.recording)
3661   {
3662     // initialize tape actions from game when recording tape
3663     tape.use_key_actions   = game.use_key_actions;
3664     tape.use_mouse_actions = game.use_mouse_actions;
3665
3666     // initialize visible playfield size when recording tape (for team mode)
3667     tape.scr_fieldx = SCR_FIELDX;
3668     tape.scr_fieldy = SCR_FIELDY;
3669   }
3670
3671   // don't play tapes over network
3672   network_playing = (network.enabled && !tape.playing);
3673
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     struct PlayerInfo *player = &stored_player[i];
3677
3678     player->index_nr = i;
3679     player->index_bit = (1 << i);
3680     player->element_nr = EL_PLAYER_1 + i;
3681
3682     player->present = FALSE;
3683     player->active = FALSE;
3684     player->mapped = FALSE;
3685
3686     player->killed = FALSE;
3687     player->reanimated = FALSE;
3688     player->buried = FALSE;
3689
3690     player->action = 0;
3691     player->effective_action = 0;
3692     player->programmed_action = 0;
3693     player->snap_action = 0;
3694
3695     player->mouse_action.lx = 0;
3696     player->mouse_action.ly = 0;
3697     player->mouse_action.button = 0;
3698     player->mouse_action.button_hint = 0;
3699
3700     player->effective_mouse_action.lx = 0;
3701     player->effective_mouse_action.ly = 0;
3702     player->effective_mouse_action.button = 0;
3703     player->effective_mouse_action.button_hint = 0;
3704
3705     for (j = 0; j < MAX_NUM_KEYS; j++)
3706       player->key[j] = FALSE;
3707
3708     player->num_white_keys = 0;
3709
3710     player->dynabomb_count = 0;
3711     player->dynabomb_size = 1;
3712     player->dynabombs_left = 0;
3713     player->dynabomb_xl = FALSE;
3714
3715     player->MovDir = initial_move_dir;
3716     player->MovPos = 0;
3717     player->GfxPos = 0;
3718     player->GfxDir = initial_move_dir;
3719     player->GfxAction = ACTION_DEFAULT;
3720     player->Frame = 0;
3721     player->StepFrame = 0;
3722
3723     player->initial_element = player->element_nr;
3724     player->artwork_element =
3725       (level.use_artwork_element[i] ? level.artwork_element[i] :
3726        player->element_nr);
3727     player->use_murphy = FALSE;
3728
3729     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3730     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3731
3732     player->gravity = level.initial_player_gravity[i];
3733
3734     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3735
3736     player->actual_frame_counter.count = 0;
3737     player->actual_frame_counter.value = 1;
3738
3739     player->step_counter = 0;
3740
3741     player->last_move_dir = initial_move_dir;
3742
3743     player->is_active = FALSE;
3744
3745     player->is_waiting = FALSE;
3746     player->is_moving = FALSE;
3747     player->is_auto_moving = FALSE;
3748     player->is_digging = FALSE;
3749     player->is_snapping = FALSE;
3750     player->is_collecting = FALSE;
3751     player->is_pushing = FALSE;
3752     player->is_switching = FALSE;
3753     player->is_dropping = FALSE;
3754     player->is_dropping_pressed = FALSE;
3755
3756     player->is_bored = FALSE;
3757     player->is_sleeping = FALSE;
3758
3759     player->was_waiting = TRUE;
3760     player->was_moving = FALSE;
3761     player->was_snapping = FALSE;
3762     player->was_dropping = FALSE;
3763
3764     player->force_dropping = FALSE;
3765
3766     player->frame_counter_bored = -1;
3767     player->frame_counter_sleeping = -1;
3768
3769     player->anim_delay_counter = 0;
3770     player->post_delay_counter = 0;
3771
3772     player->dir_waiting = initial_move_dir;
3773     player->action_waiting = ACTION_DEFAULT;
3774     player->last_action_waiting = ACTION_DEFAULT;
3775     player->special_action_bored = ACTION_DEFAULT;
3776     player->special_action_sleeping = ACTION_DEFAULT;
3777
3778     player->switch_x = -1;
3779     player->switch_y = -1;
3780
3781     player->drop_x = -1;
3782     player->drop_y = -1;
3783
3784     player->show_envelope = 0;
3785
3786     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3787
3788     player->push_delay       = -1;      // initialized when pushing starts
3789     player->push_delay_value = game.initial_push_delay_value;
3790
3791     player->drop_delay = 0;
3792     player->drop_pressed_delay = 0;
3793
3794     player->last_jx = -1;
3795     player->last_jy = -1;
3796     player->jx = -1;
3797     player->jy = -1;
3798
3799     player->shield_normal_time_left = 0;
3800     player->shield_deadly_time_left = 0;
3801
3802     player->last_removed_element = EL_UNDEFINED;
3803
3804     player->inventory_infinite_element = EL_UNDEFINED;
3805     player->inventory_size = 0;
3806
3807     if (level.use_initial_inventory[i])
3808     {
3809       for (j = 0; j < level.initial_inventory_size[i]; j++)
3810       {
3811         int element = level.initial_inventory_content[i][j];
3812         int collect_count = element_info[element].collect_count_initial;
3813         int k;
3814
3815         if (!IS_CUSTOM_ELEMENT(element))
3816           collect_count = 1;
3817
3818         if (collect_count == 0)
3819           player->inventory_infinite_element = element;
3820         else
3821           for (k = 0; k < collect_count; k++)
3822             if (player->inventory_size < MAX_INVENTORY_SIZE)
3823               player->inventory_element[player->inventory_size++] = element;
3824       }
3825     }
3826
3827     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3828     SnapField(player, 0, 0);
3829
3830     map_player_action[i] = i;
3831   }
3832
3833   network_player_action_received = FALSE;
3834
3835   // initial null action
3836   if (network_playing)
3837     SendToServer_MovePlayer(MV_NONE);
3838
3839   FrameCounter = 0;
3840   TimeFrames = 0;
3841   TimePlayed = 0;
3842   TimeLeft = level.time;
3843   TapeTime = 0;
3844
3845   ScreenMovDir = MV_NONE;
3846   ScreenMovPos = 0;
3847   ScreenGfxPos = 0;
3848
3849   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3850
3851   game.robot_wheel_x = -1;
3852   game.robot_wheel_y = -1;
3853
3854   game.exit_x = -1;
3855   game.exit_y = -1;
3856
3857   game.all_players_gone = FALSE;
3858
3859   game.LevelSolved = FALSE;
3860   game.GameOver = FALSE;
3861
3862   game.GamePlayed = !tape.playing;
3863
3864   game.LevelSolved_GameWon = FALSE;
3865   game.LevelSolved_GameEnd = FALSE;
3866   game.LevelSolved_SaveTape = FALSE;
3867   game.LevelSolved_SaveScore = FALSE;
3868
3869   game.LevelSolved_CountingTime = 0;
3870   game.LevelSolved_CountingScore = 0;
3871   game.LevelSolved_CountingHealth = 0;
3872
3873   game.RestartGameRequested = FALSE;
3874
3875   game.panel.active = TRUE;
3876
3877   game.no_level_time_limit = (level.time == 0);
3878   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3879
3880   game.yamyam_content_nr = 0;
3881   game.robot_wheel_active = FALSE;
3882   game.magic_wall_active = FALSE;
3883   game.magic_wall_time_left = 0;
3884   game.light_time_left = 0;
3885   game.timegate_time_left = 0;
3886   game.switchgate_pos = 0;
3887   game.wind_direction = level.wind_direction_initial;
3888
3889   game.time_final = 0;
3890   game.score_time_final = 0;
3891
3892   game.score = 0;
3893   game.score_final = 0;
3894
3895   game.health = MAX_HEALTH;
3896   game.health_final = MAX_HEALTH;
3897
3898   game.gems_still_needed = level.gems_needed;
3899   game.sokoban_fields_still_needed = 0;
3900   game.sokoban_objects_still_needed = 0;
3901   game.lights_still_needed = 0;
3902   game.players_still_needed = 0;
3903   game.friends_still_needed = 0;
3904
3905   game.lenses_time_left = 0;
3906   game.magnify_time_left = 0;
3907
3908   game.ball_active = level.ball_active_initial;
3909   game.ball_content_nr = 0;
3910
3911   game.explosions_delayed = TRUE;
3912
3913   game.envelope_active = FALSE;
3914
3915   // special case: set custom artwork setting to initial value
3916   game.use_masked_elements = game.use_masked_elements_initial;
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927 #if DEBUG_INIT_PLAYER
3928   DebugPrintPlayerStatus("Player status at level initialization");
3929 #endif
3930
3931   SCAN_PLAYFIELD(x, y)
3932   {
3933     Tile[x][y] = Last[x][y] = level.field[x][y];
3934     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3935     ChangeDelay[x][y] = 0;
3936     ChangePage[x][y] = -1;
3937     CustomValue[x][y] = 0;              // initialized in InitField()
3938     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3939     AmoebaNr[x][y] = 0;
3940     WasJustMoving[x][y] = 0;
3941     WasJustFalling[x][y] = 0;
3942     CheckCollision[x][y] = 0;
3943     CheckImpact[x][y] = 0;
3944     Stop[x][y] = FALSE;
3945     Pushed[x][y] = FALSE;
3946
3947     ChangeCount[x][y] = 0;
3948     ChangeEvent[x][y] = -1;
3949
3950     ExplodePhase[x][y] = 0;
3951     ExplodeDelay[x][y] = 0;
3952     ExplodeField[x][y] = EX_TYPE_NONE;
3953
3954     RunnerVisit[x][y] = 0;
3955     PlayerVisit[x][y] = 0;
3956
3957     GfxFrame[x][y] = 0;
3958     GfxRandom[x][y] = INIT_GFX_RANDOM();
3959     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3960     GfxElement[x][y] = EL_UNDEFINED;
3961     GfxElementEmpty[x][y] = EL_EMPTY;
3962     GfxAction[x][y] = ACTION_DEFAULT;
3963     GfxDir[x][y] = MV_NONE;
3964     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3965   }
3966
3967   SCAN_PLAYFIELD(x, y)
3968   {
3969     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3970       emulate_bd = FALSE;
3971     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3972       emulate_sp = FALSE;
3973
3974     InitField(x, y, TRUE);
3975
3976     ResetGfxAnimation(x, y);
3977   }
3978
3979   InitBeltMovement();
3980
3981   // required if level does not contain any "empty space" element
3982   if (element_info[EL_EMPTY].use_gfx_element)
3983     game.use_masked_elements = TRUE;
3984
3985   for (i = 0; i < MAX_PLAYERS; i++)
3986   {
3987     struct PlayerInfo *player = &stored_player[i];
3988
3989     // set number of special actions for bored and sleeping animation
3990     player->num_special_action_bored =
3991       get_num_special_action(player->artwork_element,
3992                              ACTION_BORING_1, ACTION_BORING_LAST);
3993     player->num_special_action_sleeping =
3994       get_num_special_action(player->artwork_element,
3995                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3996   }
3997
3998   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3999                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4000
4001   // initialize type of slippery elements
4002   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4003   {
4004     if (!IS_CUSTOM_ELEMENT(i))
4005     {
4006       // default: elements slip down either to the left or right randomly
4007       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4008
4009       // SP style elements prefer to slip down on the left side
4010       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012
4013       // BD style elements prefer to slip down on the left side
4014       if (game.emulation == EMU_BOULDERDASH)
4015         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4016     }
4017   }
4018
4019   // initialize explosion and ignition delay
4020   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4021   {
4022     if (!IS_CUSTOM_ELEMENT(i))
4023     {
4024       int num_phase = 8;
4025       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4026                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4027                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4028       int last_phase = (num_phase + 1) * delay;
4029       int half_phase = (num_phase / 2) * delay;
4030
4031       element_info[i].explosion_delay = last_phase - 1;
4032       element_info[i].ignition_delay = half_phase;
4033
4034       if (i == EL_BLACK_ORB)
4035         element_info[i].ignition_delay = 1;
4036     }
4037   }
4038
4039   // correct non-moving belts to start moving left
4040   for (i = 0; i < NUM_BELTS; i++)
4041     if (game.belt_dir[i] == MV_NONE)
4042       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4043
4044 #if USE_NEW_PLAYER_ASSIGNMENTS
4045   // use preferred player also in local single-player mode
4046   if (!network.enabled && !game.team_mode)
4047   {
4048     int new_index_nr = setup.network_player_nr;
4049
4050     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4051     {
4052       for (i = 0; i < MAX_PLAYERS; i++)
4053         stored_player[i].connected_locally = FALSE;
4054
4055       stored_player[new_index_nr].connected_locally = TRUE;
4056     }
4057   }
4058
4059   for (i = 0; i < MAX_PLAYERS; i++)
4060   {
4061     stored_player[i].connected = FALSE;
4062
4063     // in network game mode, the local player might not be the first player
4064     if (stored_player[i].connected_locally)
4065       local_player = &stored_player[i];
4066   }
4067
4068   if (!network.enabled)
4069     local_player->connected = TRUE;
4070
4071   if (tape.playing)
4072   {
4073     for (i = 0; i < MAX_PLAYERS; i++)
4074       stored_player[i].connected = tape.player_participates[i];
4075   }
4076   else if (network.enabled)
4077   {
4078     // add team mode players connected over the network (needed for correct
4079     // assignment of player figures from level to locally playing players)
4080
4081     for (i = 0; i < MAX_PLAYERS; i++)
4082       if (stored_player[i].connected_network)
4083         stored_player[i].connected = TRUE;
4084   }
4085   else if (game.team_mode)
4086   {
4087     // try to guess locally connected team mode players (needed for correct
4088     // assignment of player figures from level to locally playing players)
4089
4090     for (i = 0; i < MAX_PLAYERS; i++)
4091       if (setup.input[i].use_joystick ||
4092           setup.input[i].key.left != KSYM_UNDEFINED)
4093         stored_player[i].connected = TRUE;
4094   }
4095
4096 #if DEBUG_INIT_PLAYER
4097   DebugPrintPlayerStatus("Player status after level initialization");
4098 #endif
4099
4100 #if DEBUG_INIT_PLAYER
4101   Debug("game:init:player", "Reassigning players ...");
4102 #endif
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       struct PlayerInfo *field_player = NULL;
4112
4113 #if DEBUG_INIT_PLAYER
4114       Debug("game:init:player",
4115             "- looking for field player for player %d ...", i + 1);
4116 #endif
4117
4118       // assign first free player found that is present in the playfield
4119
4120       // first try: look for unmapped playfield player that is not connected
4121       for (j = 0; j < MAX_PLAYERS; j++)
4122         if (field_player == NULL &&
4123             stored_player[j].present &&
4124             !stored_player[j].mapped &&
4125             !stored_player[j].connected)
4126           field_player = &stored_player[j];
4127
4128       // second try: look for *any* unmapped playfield player
4129       for (j = 0; j < MAX_PLAYERS; j++)
4130         if (field_player == NULL &&
4131             stored_player[j].present &&
4132             !stored_player[j].mapped)
4133           field_player = &stored_player[j];
4134
4135       if (field_player != NULL)
4136       {
4137         int jx = field_player->jx, jy = field_player->jy;
4138
4139 #if DEBUG_INIT_PLAYER
4140         Debug("game:init:player", "- found player %d",
4141               field_player->index_nr + 1);
4142 #endif
4143
4144         player->present = FALSE;
4145         player->active = FALSE;
4146
4147         field_player->present = TRUE;
4148         field_player->active = TRUE;
4149
4150         /*
4151         player->initial_element = field_player->initial_element;
4152         player->artwork_element = field_player->artwork_element;
4153
4154         player->block_last_field       = field_player->block_last_field;
4155         player->block_delay_adjustment = field_player->block_delay_adjustment;
4156         */
4157
4158         StorePlayer[jx][jy] = field_player->element_nr;
4159
4160         field_player->jx = field_player->last_jx = jx;
4161         field_player->jy = field_player->last_jy = jy;
4162
4163         if (local_player == player)
4164           local_player = field_player;
4165
4166         map_player_action[field_player->index_nr] = i;
4167
4168         field_player->mapped = TRUE;
4169
4170 #if DEBUG_INIT_PLAYER
4171         Debug("game:init:player", "- map_player_action[%d] == %d",
4172               field_player->index_nr + 1, i + 1);
4173 #endif
4174       }
4175     }
4176
4177     if (player->connected && player->present)
4178       player->mapped = TRUE;
4179   }
4180
4181 #if DEBUG_INIT_PLAYER
4182   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4183 #endif
4184
4185 #else
4186
4187   // check if any connected player was not found in playfield
4188   for (i = 0; i < MAX_PLAYERS; i++)
4189   {
4190     struct PlayerInfo *player = &stored_player[i];
4191
4192     if (player->connected && !player->present)
4193     {
4194       for (j = 0; j < MAX_PLAYERS; j++)
4195       {
4196         struct PlayerInfo *field_player = &stored_player[j];
4197         int jx = field_player->jx, jy = field_player->jy;
4198
4199         // assign first free player found that is present in the playfield
4200         if (field_player->present && !field_player->connected)
4201         {
4202           player->present = TRUE;
4203           player->active = TRUE;
4204
4205           field_player->present = FALSE;
4206           field_player->active = FALSE;
4207
4208           player->initial_element = field_player->initial_element;
4209           player->artwork_element = field_player->artwork_element;
4210
4211           player->block_last_field       = field_player->block_last_field;
4212           player->block_delay_adjustment = field_player->block_delay_adjustment;
4213
4214           StorePlayer[jx][jy] = player->element_nr;
4215
4216           player->jx = player->last_jx = jx;
4217           player->jy = player->last_jy = jy;
4218
4219           break;
4220         }
4221       }
4222     }
4223   }
4224 #endif
4225
4226 #if 0
4227   Debug("game:init:player", "local_player->present == %d",
4228         local_player->present);
4229 #endif
4230
4231   // set focus to local player for network games, else to all players
4232   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4233   game.centered_player_nr_next = game.centered_player_nr;
4234   game.set_centered_player = FALSE;
4235   game.set_centered_player_wrap = FALSE;
4236
4237   if (network_playing && tape.recording)
4238   {
4239     // store client dependent player focus when recording network games
4240     tape.centered_player_nr_next = game.centered_player_nr_next;
4241     tape.set_centered_player = TRUE;
4242   }
4243
4244   if (tape.playing)
4245   {
4246     // when playing a tape, eliminate all players who do not participate
4247
4248 #if USE_NEW_PLAYER_ASSIGNMENTS
4249
4250     if (!game.team_mode)
4251     {
4252       for (i = 0; i < MAX_PLAYERS; i++)
4253       {
4254         if (stored_player[i].active &&
4255             !tape.player_participates[map_player_action[i]])
4256         {
4257           struct PlayerInfo *player = &stored_player[i];
4258           int jx = player->jx, jy = player->jy;
4259
4260 #if DEBUG_INIT_PLAYER
4261           Debug("game:init:player", "Removing player %d at (%d, %d)",
4262                 i + 1, jx, jy);
4263 #endif
4264
4265           player->active = FALSE;
4266           StorePlayer[jx][jy] = 0;
4267           Tile[jx][jy] = EL_EMPTY;
4268         }
4269       }
4270     }
4271
4272 #else
4273
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275     {
4276       if (stored_player[i].active &&
4277           !tape.player_participates[i])
4278       {
4279         struct PlayerInfo *player = &stored_player[i];
4280         int jx = player->jx, jy = player->jy;
4281
4282         player->active = FALSE;
4283         StorePlayer[jx][jy] = 0;
4284         Tile[jx][jy] = EL_EMPTY;
4285       }
4286     }
4287 #endif
4288   }
4289   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4290   {
4291     // when in single player mode, eliminate all but the local player
4292
4293     for (i = 0; i < MAX_PLAYERS; i++)
4294     {
4295       struct PlayerInfo *player = &stored_player[i];
4296
4297       if (player->active && player != local_player)
4298       {
4299         int jx = player->jx, jy = player->jy;
4300
4301         player->active = FALSE;
4302         player->present = FALSE;
4303
4304         StorePlayer[jx][jy] = 0;
4305         Tile[jx][jy] = EL_EMPTY;
4306       }
4307     }
4308   }
4309
4310   for (i = 0; i < MAX_PLAYERS; i++)
4311     if (stored_player[i].active)
4312       game.players_still_needed++;
4313
4314   if (level.solved_by_one_player)
4315     game.players_still_needed = 1;
4316
4317   // when recording the game, store which players take part in the game
4318   if (tape.recording)
4319   {
4320 #if USE_NEW_PLAYER_ASSIGNMENTS
4321     for (i = 0; i < MAX_PLAYERS; i++)
4322       if (stored_player[i].connected)
4323         tape.player_participates[i] = TRUE;
4324 #else
4325     for (i = 0; i < MAX_PLAYERS; i++)
4326       if (stored_player[i].active)
4327         tape.player_participates[i] = TRUE;
4328 #endif
4329   }
4330
4331 #if DEBUG_INIT_PLAYER
4332   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4333 #endif
4334
4335   if (BorderElement == EL_EMPTY)
4336   {
4337     SBX_Left = 0;
4338     SBX_Right = lev_fieldx - SCR_FIELDX;
4339     SBY_Upper = 0;
4340     SBY_Lower = lev_fieldy - SCR_FIELDY;
4341   }
4342   else
4343   {
4344     SBX_Left = -1;
4345     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4346     SBY_Upper = -1;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4348   }
4349
4350   if (full_lev_fieldx <= SCR_FIELDX)
4351     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4352   if (full_lev_fieldy <= SCR_FIELDY)
4353     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4354
4355   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4356     SBX_Left--;
4357   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4358     SBY_Upper--;
4359
4360   // if local player not found, look for custom element that might create
4361   // the player (make some assumptions about the right custom element)
4362   if (!local_player->present)
4363   {
4364     int start_x = 0, start_y = 0;
4365     int found_rating = 0;
4366     int found_element = EL_UNDEFINED;
4367     int player_nr = local_player->index_nr;
4368
4369     SCAN_PLAYFIELD(x, y)
4370     {
4371       int element = Tile[x][y];
4372       int content;
4373       int xx, yy;
4374       boolean is_player;
4375
4376       if (level.use_start_element[player_nr] &&
4377           level.start_element[player_nr] == element &&
4378           found_rating < 4)
4379       {
4380         start_x = x;
4381         start_y = y;
4382
4383         found_rating = 4;
4384         found_element = element;
4385       }
4386
4387       if (!IS_CUSTOM_ELEMENT(element))
4388         continue;
4389
4390       if (CAN_CHANGE(element))
4391       {
4392         for (i = 0; i < element_info[element].num_change_pages; i++)
4393         {
4394           // check for player created from custom element as single target
4395           content = element_info[element].change_page[i].target_element;
4396           is_player = IS_PLAYER_ELEMENT(content);
4397
4398           if (is_player && (found_rating < 3 ||
4399                             (found_rating == 3 && element < found_element)))
4400           {
4401             start_x = x;
4402             start_y = y;
4403
4404             found_rating = 3;
4405             found_element = element;
4406           }
4407         }
4408       }
4409
4410       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4411       {
4412         // check for player created from custom element as explosion content
4413         content = element_info[element].content.e[xx][yy];
4414         is_player = IS_PLAYER_ELEMENT(content);
4415
4416         if (is_player && (found_rating < 2 ||
4417                           (found_rating == 2 && element < found_element)))
4418         {
4419           start_x = x + xx - 1;
4420           start_y = y + yy - 1;
4421
4422           found_rating = 2;
4423           found_element = element;
4424         }
4425
4426         if (!CAN_CHANGE(element))
4427           continue;
4428
4429         for (i = 0; i < element_info[element].num_change_pages; i++)
4430         {
4431           // check for player created from custom element as extended target
4432           content =
4433             element_info[element].change_page[i].target_content.e[xx][yy];
4434
4435           is_player = IS_PLAYER_ELEMENT(content);
4436
4437           if (is_player && (found_rating < 1 ||
4438                             (found_rating == 1 && element < found_element)))
4439           {
4440             start_x = x + xx - 1;
4441             start_y = y + yy - 1;
4442
4443             found_rating = 1;
4444             found_element = element;
4445           }
4446         }
4447       }
4448     }
4449
4450     scroll_x = SCROLL_POSITION_X(start_x);
4451     scroll_y = SCROLL_POSITION_Y(start_y);
4452   }
4453   else
4454   {
4455     scroll_x = SCROLL_POSITION_X(local_player->jx);
4456     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4457   }
4458
4459   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4460     scroll_x = game.forced_scroll_x;
4461   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4462     scroll_y = game.forced_scroll_y;
4463
4464   // !!! FIX THIS (START) !!!
4465   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4466   {
4467     InitGameEngine_EM();
4468   }
4469   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4470   {
4471     InitGameEngine_SP();
4472   }
4473   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4474   {
4475     InitGameEngine_MM();
4476   }
4477   else
4478   {
4479     DrawLevel(REDRAW_FIELD);
4480     DrawAllPlayers();
4481
4482     // after drawing the level, correct some elements
4483     if (game.timegate_time_left == 0)
4484       CloseAllOpenTimegates();
4485   }
4486
4487   // blit playfield from scroll buffer to normal back buffer for fading in
4488   BlitScreenToBitmap(backbuffer);
4489   // !!! FIX THIS (END) !!!
4490
4491   DrawMaskedBorder(fade_mask);
4492
4493   FadeIn(fade_mask);
4494
4495 #if 1
4496   // full screen redraw is required at this point in the following cases:
4497   // - special editor door undrawn when game was started from level editor
4498   // - drawing area (playfield) was changed and has to be removed completely
4499   redraw_mask = REDRAW_ALL;
4500   BackToFront();
4501 #endif
4502
4503   if (!game.restart_level)
4504   {
4505     // copy default game door content to main double buffer
4506
4507     // !!! CHECK AGAIN !!!
4508     SetPanelBackground();
4509     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4510     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4511   }
4512
4513   SetPanelBackground();
4514   SetDrawBackgroundMask(REDRAW_DOOR_1);
4515
4516   UpdateAndDisplayGameControlValues();
4517
4518   if (!game.restart_level)
4519   {
4520     UnmapGameButtons();
4521     UnmapTapeButtons();
4522
4523     FreeGameButtons();
4524     CreateGameButtons();
4525
4526     MapGameButtons();
4527     MapTapeButtons();
4528
4529     // copy actual game door content to door double buffer for OpenDoor()
4530     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4531
4532     OpenDoor(DOOR_OPEN_ALL);
4533
4534     KeyboardAutoRepeatOffUnlessAutoplay();
4535
4536 #if DEBUG_INIT_PLAYER
4537     DebugPrintPlayerStatus("Player status (final)");
4538 #endif
4539   }
4540
4541   UnmapAllGadgets();
4542
4543   MapGameButtons();
4544   MapTapeButtons();
4545
4546   if (!game.restart_level && !tape.playing)
4547   {
4548     LevelStats_incPlayed(level_nr);
4549
4550     SaveLevelSetup_SeriesInfo();
4551   }
4552
4553   game.restart_level = FALSE;
4554   game.request_active = FALSE;
4555
4556   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4557     InitGameActions_MM();
4558
4559   SaveEngineSnapshotToListInitial();
4560
4561   if (!game.restart_level)
4562   {
4563     PlaySound(SND_GAME_STARTING);
4564
4565     if (setup.sound_music)
4566       PlayLevelMusic();
4567   }
4568
4569   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4570 }
4571
4572 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4573                         int actual_player_x, int actual_player_y)
4574 {
4575   // this is used for non-R'n'D game engines to update certain engine values
4576
4577   // needed to determine if sounds are played within the visible screen area
4578   scroll_x = actual_scroll_x;
4579   scroll_y = actual_scroll_y;
4580
4581   // needed to get player position for "follow finger" playing input method
4582   local_player->jx = actual_player_x;
4583   local_player->jy = actual_player_y;
4584 }
4585
4586 void InitMovDir(int x, int y)
4587 {
4588   int i, element = Tile[x][y];
4589   static int xy[4][2] =
4590   {
4591     {  0, +1 },
4592     { +1,  0 },
4593     {  0, -1 },
4594     { -1,  0 }
4595   };
4596   static int direction[3][4] =
4597   {
4598     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4599     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4600     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4601   };
4602
4603   switch (element)
4604   {
4605     case EL_BUG_RIGHT:
4606     case EL_BUG_UP:
4607     case EL_BUG_LEFT:
4608     case EL_BUG_DOWN:
4609       Tile[x][y] = EL_BUG;
4610       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4611       break;
4612
4613     case EL_SPACESHIP_RIGHT:
4614     case EL_SPACESHIP_UP:
4615     case EL_SPACESHIP_LEFT:
4616     case EL_SPACESHIP_DOWN:
4617       Tile[x][y] = EL_SPACESHIP;
4618       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4619       break;
4620
4621     case EL_BD_BUTTERFLY_RIGHT:
4622     case EL_BD_BUTTERFLY_UP:
4623     case EL_BD_BUTTERFLY_LEFT:
4624     case EL_BD_BUTTERFLY_DOWN:
4625       Tile[x][y] = EL_BD_BUTTERFLY;
4626       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4627       break;
4628
4629     case EL_BD_FIREFLY_RIGHT:
4630     case EL_BD_FIREFLY_UP:
4631     case EL_BD_FIREFLY_LEFT:
4632     case EL_BD_FIREFLY_DOWN:
4633       Tile[x][y] = EL_BD_FIREFLY;
4634       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4635       break;
4636
4637     case EL_PACMAN_RIGHT:
4638     case EL_PACMAN_UP:
4639     case EL_PACMAN_LEFT:
4640     case EL_PACMAN_DOWN:
4641       Tile[x][y] = EL_PACMAN;
4642       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4643       break;
4644
4645     case EL_YAMYAM_LEFT:
4646     case EL_YAMYAM_RIGHT:
4647     case EL_YAMYAM_UP:
4648     case EL_YAMYAM_DOWN:
4649       Tile[x][y] = EL_YAMYAM;
4650       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4651       break;
4652
4653     case EL_SP_SNIKSNAK:
4654       MovDir[x][y] = MV_UP;
4655       break;
4656
4657     case EL_SP_ELECTRON:
4658       MovDir[x][y] = MV_LEFT;
4659       break;
4660
4661     case EL_MOLE_LEFT:
4662     case EL_MOLE_RIGHT:
4663     case EL_MOLE_UP:
4664     case EL_MOLE_DOWN:
4665       Tile[x][y] = EL_MOLE;
4666       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4667       break;
4668
4669     case EL_SPRING_LEFT:
4670     case EL_SPRING_RIGHT:
4671       Tile[x][y] = EL_SPRING;
4672       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4673       break;
4674
4675     default:
4676       if (IS_CUSTOM_ELEMENT(element))
4677       {
4678         struct ElementInfo *ei = &element_info[element];
4679         int move_direction_initial = ei->move_direction_initial;
4680         int move_pattern = ei->move_pattern;
4681
4682         if (move_direction_initial == MV_START_PREVIOUS)
4683         {
4684           if (MovDir[x][y] != MV_NONE)
4685             return;
4686
4687           move_direction_initial = MV_START_AUTOMATIC;
4688         }
4689
4690         if (move_direction_initial == MV_START_RANDOM)
4691           MovDir[x][y] = 1 << RND(4);
4692         else if (move_direction_initial & MV_ANY_DIRECTION)
4693           MovDir[x][y] = move_direction_initial;
4694         else if (move_pattern == MV_ALL_DIRECTIONS ||
4695                  move_pattern == MV_TURNING_LEFT ||
4696                  move_pattern == MV_TURNING_RIGHT ||
4697                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4698                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4699                  move_pattern == MV_TURNING_RANDOM)
4700           MovDir[x][y] = 1 << RND(4);
4701         else if (move_pattern == MV_HORIZONTAL)
4702           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4703         else if (move_pattern == MV_VERTICAL)
4704           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4705         else if (move_pattern & MV_ANY_DIRECTION)
4706           MovDir[x][y] = element_info[element].move_pattern;
4707         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4708                  move_pattern == MV_ALONG_RIGHT_SIDE)
4709         {
4710           // use random direction as default start direction
4711           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4712             MovDir[x][y] = 1 << RND(4);
4713
4714           for (i = 0; i < NUM_DIRECTIONS; i++)
4715           {
4716             int x1 = x + xy[i][0];
4717             int y1 = y + xy[i][1];
4718
4719             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4720             {
4721               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4722                 MovDir[x][y] = direction[0][i];
4723               else
4724                 MovDir[x][y] = direction[1][i];
4725
4726               break;
4727             }
4728           }
4729         }                
4730       }
4731       else
4732       {
4733         MovDir[x][y] = 1 << RND(4);
4734
4735         if (element != EL_BUG &&
4736             element != EL_SPACESHIP &&
4737             element != EL_BD_BUTTERFLY &&
4738             element != EL_BD_FIREFLY)
4739           break;
4740
4741         for (i = 0; i < NUM_DIRECTIONS; i++)
4742         {
4743           int x1 = x + xy[i][0];
4744           int y1 = y + xy[i][1];
4745
4746           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4747           {
4748             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4749             {
4750               MovDir[x][y] = direction[0][i];
4751               break;
4752             }
4753             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4754                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4755             {
4756               MovDir[x][y] = direction[1][i];
4757               break;
4758             }
4759           }
4760         }
4761       }
4762       break;
4763   }
4764
4765   GfxDir[x][y] = MovDir[x][y];
4766 }
4767
4768 void InitAmoebaNr(int x, int y)
4769 {
4770   int i;
4771   int group_nr = AmoebaNeighbourNr(x, y);
4772
4773   if (group_nr == 0)
4774   {
4775     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4776     {
4777       if (AmoebaCnt[i] == 0)
4778       {
4779         group_nr = i;
4780         break;
4781       }
4782     }
4783   }
4784
4785   AmoebaNr[x][y] = group_nr;
4786   AmoebaCnt[group_nr]++;
4787   AmoebaCnt2[group_nr]++;
4788 }
4789
4790 static void LevelSolved_SetFinalGameValues(void)
4791 {
4792   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4793   game.score_time_final = (level.use_step_counter ? TimePlayed :
4794                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4795
4796   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4797                       game_em.lev->score :
4798                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4799                       game_mm.score :
4800                       game.score);
4801
4802   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4803                        MM_HEALTH(game_mm.laser_overload_value) :
4804                        game.health);
4805
4806   game.LevelSolved_CountingTime = game.time_final;
4807   game.LevelSolved_CountingScore = game.score_final;
4808   game.LevelSolved_CountingHealth = game.health_final;
4809 }
4810
4811 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4812 {
4813   game.LevelSolved_CountingTime = time;
4814   game.LevelSolved_CountingScore = score;
4815   game.LevelSolved_CountingHealth = health;
4816
4817   game_panel_controls[GAME_PANEL_TIME].value = time;
4818   game_panel_controls[GAME_PANEL_SCORE].value = score;
4819   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4820
4821   DisplayGameControlValues();
4822 }
4823
4824 static void LevelSolved(void)
4825 {
4826   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4827       game.players_still_needed > 0)
4828     return;
4829
4830   game.LevelSolved = TRUE;
4831   game.GameOver = TRUE;
4832
4833   tape.solved = TRUE;
4834
4835   // needed here to display correct panel values while player walks into exit
4836   LevelSolved_SetFinalGameValues();
4837 }
4838
4839 void GameWon(void)
4840 {
4841   static int time_count_steps;
4842   static int time, time_final;
4843   static float score, score_final; // needed for time score < 10 for 10 seconds
4844   static int health, health_final;
4845   static int game_over_delay_1 = 0;
4846   static int game_over_delay_2 = 0;
4847   static int game_over_delay_3 = 0;
4848   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4849   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4850
4851   if (!game.LevelSolved_GameWon)
4852   {
4853     int i;
4854
4855     // do not start end game actions before the player stops moving (to exit)
4856     if (local_player->active && local_player->MovPos)
4857       return;
4858
4859     // calculate final game values after player finished walking into exit
4860     LevelSolved_SetFinalGameValues();
4861
4862     game.LevelSolved_GameWon = TRUE;
4863     game.LevelSolved_SaveTape = tape.recording;
4864     game.LevelSolved_SaveScore = !tape.playing;
4865
4866     if (!tape.playing)
4867     {
4868       LevelStats_incSolved(level_nr);
4869
4870       SaveLevelSetup_SeriesInfo();
4871     }
4872
4873     if (tape.auto_play)         // tape might already be stopped here
4874       tape.auto_play_level_solved = TRUE;
4875
4876     TapeStop();
4877
4878     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4879     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4880     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4881
4882     time = time_final = game.time_final;
4883     score = score_final = game.score_final;
4884     health = health_final = game.health_final;
4885
4886     // update game panel values before (delayed) counting of score (if any)
4887     LevelSolved_DisplayFinalGameValues(time, score, health);
4888
4889     // if level has time score defined, calculate new final game values
4890     if (time_score > 0)
4891     {
4892       int time_final_max = 999;
4893       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4894       int time_frames = 0;
4895       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4896       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4897
4898       if (TimeLeft > 0)
4899       {
4900         time_final = 0;
4901         time_frames = time_frames_left;
4902       }
4903       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4904       {
4905         time_final = time_final_max;
4906         time_frames = time_frames_final_max - time_frames_played;
4907       }
4908
4909       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4910
4911       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4912
4913       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4914       {
4915         health_final = 0;
4916         score_final += health * time_score;
4917       }
4918
4919       game.score_final = score_final;
4920       game.health_final = health_final;
4921     }
4922
4923     // if not counting score after game, immediately update game panel values
4924     if (level_editor_test_game || !setup.count_score_after_game)
4925     {
4926       time = time_final;
4927       score = score_final;
4928
4929       LevelSolved_DisplayFinalGameValues(time, score, health);
4930     }
4931
4932     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4933     {
4934       // check if last player has left the level
4935       if (game.exit_x >= 0 &&
4936           game.exit_y >= 0)
4937       {
4938         int x = game.exit_x;
4939         int y = game.exit_y;
4940         int element = Tile[x][y];
4941
4942         // close exit door after last player
4943         if ((game.all_players_gone &&
4944              (element == EL_EXIT_OPEN ||
4945               element == EL_SP_EXIT_OPEN ||
4946               element == EL_STEEL_EXIT_OPEN)) ||
4947             element == EL_EM_EXIT_OPEN ||
4948             element == EL_EM_STEEL_EXIT_OPEN)
4949         {
4950
4951           Tile[x][y] =
4952             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4953              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4954              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4955              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4956              EL_EM_STEEL_EXIT_CLOSING);
4957
4958           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4959         }
4960
4961         // player disappears
4962         DrawLevelField(x, y);
4963       }
4964
4965       for (i = 0; i < MAX_PLAYERS; i++)
4966       {
4967         struct PlayerInfo *player = &stored_player[i];
4968
4969         if (player->present)
4970         {
4971           RemovePlayer(player);
4972
4973           // player disappears
4974           DrawLevelField(player->jx, player->jy);
4975         }
4976       }
4977     }
4978
4979     PlaySound(SND_GAME_WINNING);
4980   }
4981
4982   if (setup.count_score_after_game)
4983   {
4984     if (time != time_final)
4985     {
4986       if (game_over_delay_1 > 0)
4987       {
4988         game_over_delay_1--;
4989
4990         return;
4991       }
4992
4993       int time_to_go = ABS(time_final - time);
4994       int time_count_dir = (time < time_final ? +1 : -1);
4995
4996       if (time_to_go < time_count_steps)
4997         time_count_steps = 1;
4998
4999       time  += time_count_steps * time_count_dir;
5000       score += time_count_steps * time_score;
5001
5002       // set final score to correct rounding differences after counting score
5003       if (time == time_final)
5004         score = score_final;
5005
5006       LevelSolved_DisplayFinalGameValues(time, score, health);
5007
5008       if (time == time_final)
5009         StopSound(SND_GAME_LEVELTIME_BONUS);
5010       else if (setup.sound_loops)
5011         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5012       else
5013         PlaySound(SND_GAME_LEVELTIME_BONUS);
5014
5015       return;
5016     }
5017
5018     if (health != health_final)
5019     {
5020       if (game_over_delay_2 > 0)
5021       {
5022         game_over_delay_2--;
5023
5024         return;
5025       }
5026
5027       int health_count_dir = (health < health_final ? +1 : -1);
5028
5029       health += health_count_dir;
5030       score  += time_score;
5031
5032       LevelSolved_DisplayFinalGameValues(time, score, health);
5033
5034       if (health == health_final)
5035         StopSound(SND_GAME_LEVELTIME_BONUS);
5036       else if (setup.sound_loops)
5037         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5038       else
5039         PlaySound(SND_GAME_LEVELTIME_BONUS);
5040
5041       return;
5042     }
5043   }
5044
5045   game.panel.active = FALSE;
5046
5047   if (game_over_delay_3 > 0)
5048   {
5049     game_over_delay_3--;
5050
5051     return;
5052   }
5053
5054   GameEnd();
5055 }
5056
5057 void GameEnd(void)
5058 {
5059   // used instead of "level_nr" (needed for network games)
5060   int last_level_nr = levelset.level_nr;
5061   boolean tape_saved = FALSE;
5062
5063   game.LevelSolved_GameEnd = TRUE;
5064
5065   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5066   {
5067     // make sure that request dialog to save tape does not open door again
5068     if (!global.use_envelope_request)
5069       CloseDoor(DOOR_CLOSE_1);
5070
5071     // ask to save tape
5072     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5073
5074     // set unique basename for score tape (also saved in high score table)
5075     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5076   }
5077
5078   // if no tape is to be saved, close both doors simultaneously
5079   CloseDoor(DOOR_CLOSE_ALL);
5080
5081   if (level_editor_test_game || score_info_tape_play)
5082   {
5083     SetGameStatus(GAME_MODE_MAIN);
5084
5085     DrawMainMenu();
5086
5087     return;
5088   }
5089
5090   if (!game.LevelSolved_SaveScore)
5091   {
5092     SetGameStatus(GAME_MODE_MAIN);
5093
5094     DrawMainMenu();
5095
5096     return;
5097   }
5098
5099   if (level_nr == leveldir_current->handicap_level)
5100   {
5101     leveldir_current->handicap_level++;
5102
5103     SaveLevelSetup_SeriesInfo();
5104   }
5105
5106   // save score and score tape before potentially erasing tape below
5107   NewHighScore(last_level_nr, tape_saved);
5108
5109   if (setup.increment_levels &&
5110       level_nr < leveldir_current->last_level &&
5111       !network_playing)
5112   {
5113     level_nr++;         // advance to next level
5114     TapeErase();        // start with empty tape
5115
5116     if (setup.auto_play_next_level)
5117     {
5118       scores.continue_playing = TRUE;
5119       scores.next_level_nr = level_nr;
5120
5121       LoadLevel(level_nr);
5122
5123       SaveLevelSetup_SeriesInfo();
5124     }
5125   }
5126
5127   if (scores.last_added >= 0 && setup.show_scores_after_game)
5128   {
5129     SetGameStatus(GAME_MODE_SCORES);
5130
5131     DrawHallOfFame(last_level_nr);
5132   }
5133   else if (scores.continue_playing)
5134   {
5135     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5136   }
5137   else
5138   {
5139     SetGameStatus(GAME_MODE_MAIN);
5140
5141     DrawMainMenu();
5142   }
5143 }
5144
5145 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5146                          boolean one_score_entry_per_name)
5147 {
5148   int i;
5149
5150   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5151     return -1;
5152
5153   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5154   {
5155     struct ScoreEntry *entry = &list->entry[i];
5156     boolean score_is_better = (new_entry->score >  entry->score);
5157     boolean score_is_equal  = (new_entry->score == entry->score);
5158     boolean time_is_better  = (new_entry->time  <  entry->time);
5159     boolean time_is_equal   = (new_entry->time  == entry->time);
5160     boolean better_by_score = (score_is_better ||
5161                                (score_is_equal && time_is_better));
5162     boolean better_by_time  = (time_is_better ||
5163                                (time_is_equal && score_is_better));
5164     boolean is_better = (level.rate_time_over_score ? better_by_time :
5165                          better_by_score);
5166     boolean entry_is_empty = (entry->score == 0 &&
5167                               entry->time == 0);
5168
5169     // prevent adding server score entries if also existing in local score file
5170     // (special case: historic score entries have an empty tape basename entry)
5171     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5172         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5173     {
5174       // add fields from server score entry not stored in local score entry
5175       // (currently, this means setting platform, version and country fields;
5176       // in rare cases, this may also correct an invalid score value, as
5177       // historic scores might have been truncated to 16-bit values locally)
5178       *entry = *new_entry;
5179
5180       return -1;
5181     }
5182
5183     if (is_better || entry_is_empty)
5184     {
5185       // player has made it to the hall of fame
5186
5187       if (i < MAX_SCORE_ENTRIES - 1)
5188       {
5189         int m = MAX_SCORE_ENTRIES - 1;
5190         int l;
5191
5192         if (one_score_entry_per_name)
5193         {
5194           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5195             if (strEqual(list->entry[l].name, new_entry->name))
5196               m = l;
5197
5198           if (m == i)   // player's new highscore overwrites his old one
5199             goto put_into_list;
5200         }
5201
5202         for (l = m; l > i; l--)
5203           list->entry[l] = list->entry[l - 1];
5204       }
5205
5206       put_into_list:
5207
5208       *entry = *new_entry;
5209
5210       return i;
5211     }
5212     else if (one_score_entry_per_name &&
5213              strEqual(entry->name, new_entry->name))
5214     {
5215       // player already in high score list with better score or time
5216
5217       return -1;
5218     }
5219   }
5220
5221   // special case: new score is beyond the last high score list position
5222   return MAX_SCORE_ENTRIES;
5223 }
5224
5225 void NewHighScore(int level_nr, boolean tape_saved)
5226 {
5227   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5228   boolean one_per_name = FALSE;
5229
5230   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5231   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5232
5233   new_entry.score = game.score_final;
5234   new_entry.time = game.score_time_final;
5235
5236   LoadScore(level_nr);
5237
5238   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5239
5240   if (scores.last_added >= MAX_SCORE_ENTRIES)
5241   {
5242     scores.last_added = MAX_SCORE_ENTRIES - 1;
5243     scores.force_last_added = TRUE;
5244
5245     scores.entry[scores.last_added] = new_entry;
5246
5247     // store last added local score entry (before merging server scores)
5248     scores.last_added_local = scores.last_added;
5249
5250     return;
5251   }
5252
5253   if (scores.last_added < 0)
5254     return;
5255
5256   SaveScore(level_nr);
5257
5258   // store last added local score entry (before merging server scores)
5259   scores.last_added_local = scores.last_added;
5260
5261   if (!game.LevelSolved_SaveTape)
5262     return;
5263
5264   SaveScoreTape(level_nr);
5265
5266   if (setup.ask_for_using_api_server)
5267   {
5268     setup.use_api_server =
5269       Request("Upload your score and tape to the high score server?", REQ_ASK);
5270
5271     if (!setup.use_api_server)
5272       Request("Not using high score server! Use setup menu to enable again!",
5273               REQ_CONFIRM);
5274
5275     runtime.use_api_server = setup.use_api_server;
5276
5277     // after asking for using API server once, do not ask again
5278     setup.ask_for_using_api_server = FALSE;
5279
5280     SaveSetup_ServerSetup();
5281   }
5282
5283   SaveServerScore(level_nr, tape_saved);
5284 }
5285
5286 void MergeServerScore(void)
5287 {
5288   struct ScoreEntry last_added_entry;
5289   boolean one_per_name = FALSE;
5290   int i;
5291
5292   if (scores.last_added >= 0)
5293     last_added_entry = scores.entry[scores.last_added];
5294
5295   for (i = 0; i < server_scores.num_entries; i++)
5296   {
5297     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5298
5299     if (pos >= 0 && pos <= scores.last_added)
5300       scores.last_added++;
5301   }
5302
5303   if (scores.last_added >= MAX_SCORE_ENTRIES)
5304   {
5305     scores.last_added = MAX_SCORE_ENTRIES - 1;
5306     scores.force_last_added = TRUE;
5307
5308     scores.entry[scores.last_added] = last_added_entry;
5309   }
5310 }
5311
5312 static int getElementMoveStepsizeExt(int x, int y, int direction)
5313 {
5314   int element = Tile[x][y];
5315   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5316   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5317   int horiz_move = (dx != 0);
5318   int sign = (horiz_move ? dx : dy);
5319   int step = sign * element_info[element].move_stepsize;
5320
5321   // special values for move stepsize for spring and things on conveyor belt
5322   if (horiz_move)
5323   {
5324     if (CAN_FALL(element) &&
5325         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5326       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5327     else if (element == EL_SPRING)
5328       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5329   }
5330
5331   return step;
5332 }
5333
5334 static int getElementMoveStepsize(int x, int y)
5335 {
5336   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5337 }
5338
5339 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5340 {
5341   if (player->GfxAction != action || player->GfxDir != dir)
5342   {
5343     player->GfxAction = action;
5344     player->GfxDir = dir;
5345     player->Frame = 0;
5346     player->StepFrame = 0;
5347   }
5348 }
5349
5350 static void ResetGfxFrame(int x, int y)
5351 {
5352   // profiling showed that "autotest" spends 10~20% of its time in this function
5353   if (DrawingDeactivatedField())
5354     return;
5355
5356   int element = Tile[x][y];
5357   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5358
5359   if (graphic_info[graphic].anim_global_sync)
5360     GfxFrame[x][y] = FrameCounter;
5361   else if (graphic_info[graphic].anim_global_anim_sync)
5362     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5363   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5364     GfxFrame[x][y] = CustomValue[x][y];
5365   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5366     GfxFrame[x][y] = element_info[element].collect_score;
5367   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5368     GfxFrame[x][y] = ChangeDelay[x][y];
5369 }
5370
5371 static void ResetGfxAnimation(int x, int y)
5372 {
5373   GfxAction[x][y] = ACTION_DEFAULT;
5374   GfxDir[x][y] = MovDir[x][y];
5375   GfxFrame[x][y] = 0;
5376
5377   ResetGfxFrame(x, y);
5378 }
5379
5380 static void ResetRandomAnimationValue(int x, int y)
5381 {
5382   GfxRandom[x][y] = INIT_GFX_RANDOM();
5383 }
5384
5385 static void InitMovingField(int x, int y, int direction)
5386 {
5387   int element = Tile[x][y];
5388   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5389   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5390   int newx = x + dx;
5391   int newy = y + dy;
5392   boolean is_moving_before, is_moving_after;
5393
5394   // check if element was/is moving or being moved before/after mode change
5395   is_moving_before = (WasJustMoving[x][y] != 0);
5396   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5397
5398   // reset animation only for moving elements which change direction of moving
5399   // or which just started or stopped moving
5400   // (else CEs with property "can move" / "not moving" are reset each frame)
5401   if (is_moving_before != is_moving_after ||
5402       direction != MovDir[x][y])
5403     ResetGfxAnimation(x, y);
5404
5405   MovDir[x][y] = direction;
5406   GfxDir[x][y] = direction;
5407
5408   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5409                      direction == MV_DOWN && CAN_FALL(element) ?
5410                      ACTION_FALLING : ACTION_MOVING);
5411
5412   // this is needed for CEs with property "can move" / "not moving"
5413
5414   if (is_moving_after)
5415   {
5416     if (Tile[newx][newy] == EL_EMPTY)
5417       Tile[newx][newy] = EL_BLOCKED;
5418
5419     MovDir[newx][newy] = MovDir[x][y];
5420
5421     CustomValue[newx][newy] = CustomValue[x][y];
5422
5423     GfxFrame[newx][newy] = GfxFrame[x][y];
5424     GfxRandom[newx][newy] = GfxRandom[x][y];
5425     GfxAction[newx][newy] = GfxAction[x][y];
5426     GfxDir[newx][newy] = GfxDir[x][y];
5427   }
5428 }
5429
5430 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5431 {
5432   int direction = MovDir[x][y];
5433   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5434   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5435
5436   *goes_to_x = newx;
5437   *goes_to_y = newy;
5438 }
5439
5440 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5441 {
5442   int direction = MovDir[x][y];
5443   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5444   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5445
5446   *comes_from_x = oldx;
5447   *comes_from_y = oldy;
5448 }
5449
5450 static int MovingOrBlocked2Element(int x, int y)
5451 {
5452   int element = Tile[x][y];
5453
5454   if (element == EL_BLOCKED)
5455   {
5456     int oldx, oldy;
5457
5458     Blocked2Moving(x, y, &oldx, &oldy);
5459
5460     return Tile[oldx][oldy];
5461   }
5462
5463   return element;
5464 }
5465
5466 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5467 {
5468   // like MovingOrBlocked2Element(), but if element is moving
5469   // and (x, y) is the field the moving element is just leaving,
5470   // return EL_BLOCKED instead of the element value
5471   int element = Tile[x][y];
5472
5473   if (IS_MOVING(x, y))
5474   {
5475     if (element == EL_BLOCKED)
5476     {
5477       int oldx, oldy;
5478
5479       Blocked2Moving(x, y, &oldx, &oldy);
5480       return Tile[oldx][oldy];
5481     }
5482     else
5483       return EL_BLOCKED;
5484   }
5485   else
5486     return element;
5487 }
5488
5489 static void RemoveField(int x, int y)
5490 {
5491   Tile[x][y] = EL_EMPTY;
5492
5493   MovPos[x][y] = 0;
5494   MovDir[x][y] = 0;
5495   MovDelay[x][y] = 0;
5496
5497   CustomValue[x][y] = 0;
5498
5499   AmoebaNr[x][y] = 0;
5500   ChangeDelay[x][y] = 0;
5501   ChangePage[x][y] = -1;
5502   Pushed[x][y] = FALSE;
5503
5504   GfxElement[x][y] = EL_UNDEFINED;
5505   GfxAction[x][y] = ACTION_DEFAULT;
5506   GfxDir[x][y] = MV_NONE;
5507 }
5508
5509 static void RemoveMovingField(int x, int y)
5510 {
5511   int oldx = x, oldy = y, newx = x, newy = y;
5512   int element = Tile[x][y];
5513   int next_element = EL_UNDEFINED;
5514
5515   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5516     return;
5517
5518   if (IS_MOVING(x, y))
5519   {
5520     Moving2Blocked(x, y, &newx, &newy);
5521
5522     if (Tile[newx][newy] != EL_BLOCKED)
5523     {
5524       // element is moving, but target field is not free (blocked), but
5525       // already occupied by something different (example: acid pool);
5526       // in this case, only remove the moving field, but not the target
5527
5528       RemoveField(oldx, oldy);
5529
5530       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5531
5532       TEST_DrawLevelField(oldx, oldy);
5533
5534       return;
5535     }
5536   }
5537   else if (element == EL_BLOCKED)
5538   {
5539     Blocked2Moving(x, y, &oldx, &oldy);
5540     if (!IS_MOVING(oldx, oldy))
5541       return;
5542   }
5543
5544   if (element == EL_BLOCKED &&
5545       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5546        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5547        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5548        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5549        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5550        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5551     next_element = get_next_element(Tile[oldx][oldy]);
5552
5553   RemoveField(oldx, oldy);
5554   RemoveField(newx, newy);
5555
5556   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5557
5558   if (next_element != EL_UNDEFINED)
5559     Tile[oldx][oldy] = next_element;
5560
5561   TEST_DrawLevelField(oldx, oldy);
5562   TEST_DrawLevelField(newx, newy);
5563 }
5564
5565 void DrawDynamite(int x, int y)
5566 {
5567   int sx = SCREENX(x), sy = SCREENY(y);
5568   int graphic = el2img(Tile[x][y]);
5569   int frame;
5570
5571   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5572     return;
5573
5574   if (IS_WALKABLE_INSIDE(Back[x][y]))
5575     return;
5576
5577   if (Back[x][y])
5578     DrawLevelElement(x, y, Back[x][y]);
5579   else if (Store[x][y])
5580     DrawLevelElement(x, y, Store[x][y]);
5581   else if (game.use_masked_elements)
5582     DrawLevelElement(x, y, EL_EMPTY);
5583
5584   frame = getGraphicAnimationFrameXY(graphic, x, y);
5585
5586   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5587     DrawGraphicThruMask(sx, sy, graphic, frame);
5588   else
5589     DrawGraphic(sx, sy, graphic, frame);
5590 }
5591
5592 static void CheckDynamite(int x, int y)
5593 {
5594   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5595   {
5596     MovDelay[x][y]--;
5597
5598     if (MovDelay[x][y] != 0)
5599     {
5600       DrawDynamite(x, y);
5601       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5602
5603       return;
5604     }
5605   }
5606
5607   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5608
5609   Bang(x, y);
5610 }
5611
5612 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5613 {
5614   boolean num_checked_players = 0;
5615   int i;
5616
5617   for (i = 0; i < MAX_PLAYERS; i++)
5618   {
5619     if (stored_player[i].active)
5620     {
5621       int sx = stored_player[i].jx;
5622       int sy = stored_player[i].jy;
5623
5624       if (num_checked_players == 0)
5625       {
5626         *sx1 = *sx2 = sx;
5627         *sy1 = *sy2 = sy;
5628       }
5629       else
5630       {
5631         *sx1 = MIN(*sx1, sx);
5632         *sy1 = MIN(*sy1, sy);
5633         *sx2 = MAX(*sx2, sx);
5634         *sy2 = MAX(*sy2, sy);
5635       }
5636
5637       num_checked_players++;
5638     }
5639   }
5640 }
5641
5642 static boolean checkIfAllPlayersFitToScreen_RND(void)
5643 {
5644   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5645
5646   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5647
5648   return (sx2 - sx1 < SCR_FIELDX &&
5649           sy2 - sy1 < SCR_FIELDY);
5650 }
5651
5652 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5653 {
5654   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5655
5656   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5657
5658   *sx = (sx1 + sx2) / 2;
5659   *sy = (sy1 + sy2) / 2;
5660 }
5661
5662 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5663                                boolean center_screen, boolean quick_relocation)
5664 {
5665   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5666   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5667   boolean no_delay = (tape.warp_forward);
5668   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5669   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5670   int new_scroll_x, new_scroll_y;
5671
5672   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5673   {
5674     // case 1: quick relocation inside visible screen (without scrolling)
5675
5676     RedrawPlayfield();
5677
5678     return;
5679   }
5680
5681   if (!level.shifted_relocation || center_screen)
5682   {
5683     // relocation _with_ centering of screen
5684
5685     new_scroll_x = SCROLL_POSITION_X(x);
5686     new_scroll_y = SCROLL_POSITION_Y(y);
5687   }
5688   else
5689   {
5690     // relocation _without_ centering of screen
5691
5692     // apply distance between old and new player position to scroll position
5693     int shifted_scroll_x = scroll_x + (x - old_x);
5694     int shifted_scroll_y = scroll_y + (y - old_y);
5695
5696     // make sure that shifted scroll position does not scroll beyond screen
5697     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5698     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5699
5700     // special case for teleporting from one end of the playfield to the other
5701     // (this kludge prevents the destination area to be shifted by half a tile
5702     // against the source destination for even screen width or screen height;
5703     // probably most useful when used with high "game.forced_scroll_delay_value"
5704     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5705     if (quick_relocation)
5706     {
5707       if (EVEN(SCR_FIELDX))
5708       {
5709         // relocate (teleport) between left and right border (half or full)
5710         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5711           new_scroll_x = SBX_Right;
5712         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5713           new_scroll_x = SBX_Right - 1;
5714         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5715           new_scroll_x = SBX_Left;
5716         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5717           new_scroll_x = SBX_Left + 1;
5718       }
5719
5720       if (EVEN(SCR_FIELDY))
5721       {
5722         // relocate (teleport) between top and bottom border (half or full)
5723         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5724           new_scroll_y = SBY_Lower;
5725         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5726           new_scroll_y = SBY_Lower - 1;
5727         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5728           new_scroll_y = SBY_Upper;
5729         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5730           new_scroll_y = SBY_Upper + 1;
5731       }
5732     }
5733   }
5734
5735   if (quick_relocation)
5736   {
5737     // case 2: quick relocation (redraw without visible scrolling)
5738
5739     scroll_x = new_scroll_x;
5740     scroll_y = new_scroll_y;
5741
5742     RedrawPlayfield();
5743
5744     return;
5745   }
5746
5747   // case 3: visible relocation (with scrolling to new position)
5748
5749   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5750
5751   SetVideoFrameDelay(wait_delay_value);
5752
5753   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5754   {
5755     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5756     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5757
5758     if (dx == 0 && dy == 0)             // no scrolling needed at all
5759       break;
5760
5761     scroll_x -= dx;
5762     scroll_y -= dy;
5763
5764     // set values for horizontal/vertical screen scrolling (half tile size)
5765     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5766     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5767     int pos_x = dx * TILEX / 2;
5768     int pos_y = dy * TILEY / 2;
5769     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5770     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5771
5772     ScrollLevel(dx, dy);
5773     DrawAllPlayers();
5774
5775     // scroll in two steps of half tile size to make things smoother
5776     BlitScreenToBitmapExt_RND(window, fx, fy);
5777
5778     // scroll second step to align at full tile size
5779     BlitScreenToBitmap(window);
5780   }
5781
5782   DrawAllPlayers();
5783   BackToFront();
5784
5785   SetVideoFrameDelay(frame_delay_value_old);
5786 }
5787
5788 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5789 {
5790   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5791   int player_nr = GET_PLAYER_NR(el_player);
5792   struct PlayerInfo *player = &stored_player[player_nr];
5793   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5794   boolean no_delay = (tape.warp_forward);
5795   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5796   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5797   int old_jx = player->jx;
5798   int old_jy = player->jy;
5799   int old_element = Tile[old_jx][old_jy];
5800   int element = Tile[jx][jy];
5801   boolean player_relocated = (old_jx != jx || old_jy != jy);
5802
5803   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5804   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5805   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5806   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5807   int leave_side_horiz = move_dir_horiz;
5808   int leave_side_vert  = move_dir_vert;
5809   int enter_side = enter_side_horiz | enter_side_vert;
5810   int leave_side = leave_side_horiz | leave_side_vert;
5811
5812   if (player->buried)           // do not reanimate dead player
5813     return;
5814
5815   if (!player_relocated)        // no need to relocate the player
5816     return;
5817
5818   if (IS_PLAYER(jx, jy))        // player already placed at new position
5819   {
5820     RemoveField(jx, jy);        // temporarily remove newly placed player
5821     DrawLevelField(jx, jy);
5822   }
5823
5824   if (player->present)
5825   {
5826     while (player->MovPos)
5827     {
5828       ScrollPlayer(player, SCROLL_GO_ON);
5829       ScrollScreen(NULL, SCROLL_GO_ON);
5830
5831       AdvanceFrameAndPlayerCounters(player->index_nr);
5832
5833       DrawPlayer(player);
5834
5835       BackToFront_WithFrameDelay(wait_delay_value);
5836     }
5837
5838     DrawPlayer(player);         // needed here only to cleanup last field
5839     DrawLevelField(player->jx, player->jy);     // remove player graphic
5840
5841     player->is_moving = FALSE;
5842   }
5843
5844   if (IS_CUSTOM_ELEMENT(old_element))
5845     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5846                                CE_LEFT_BY_PLAYER,
5847                                player->index_bit, leave_side);
5848
5849   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5850                                       CE_PLAYER_LEAVES_X,
5851                                       player->index_bit, leave_side);
5852
5853   Tile[jx][jy] = el_player;
5854   InitPlayerField(jx, jy, el_player, TRUE);
5855
5856   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5857      possible that the relocation target field did not contain a player element,
5858      but a walkable element, to which the new player was relocated -- in this
5859      case, restore that (already initialized!) element on the player field */
5860   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5861   {
5862     Tile[jx][jy] = element;     // restore previously existing element
5863   }
5864
5865   // only visually relocate centered player
5866   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5867                      FALSE, level.instant_relocation);
5868
5869   TestIfPlayerTouchesBadThing(jx, jy);
5870   TestIfPlayerTouchesCustomElement(jx, jy);
5871
5872   if (IS_CUSTOM_ELEMENT(element))
5873     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5874                                player->index_bit, enter_side);
5875
5876   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5877                                       player->index_bit, enter_side);
5878
5879   if (player->is_switching)
5880   {
5881     /* ensure that relocation while still switching an element does not cause
5882        a new element to be treated as also switched directly after relocation
5883        (this is important for teleporter switches that teleport the player to
5884        a place where another teleporter switch is in the same direction, which
5885        would then incorrectly be treated as immediately switched before the
5886        direction key that caused the switch was released) */
5887
5888     player->switch_x += jx - old_jx;
5889     player->switch_y += jy - old_jy;
5890   }
5891 }
5892
5893 static void Explode(int ex, int ey, int phase, int mode)
5894 {
5895   int x, y;
5896   int last_phase;
5897   int border_element;
5898
5899   if (game.explosions_delayed)
5900   {
5901     ExplodeField[ex][ey] = mode;
5902     return;
5903   }
5904
5905   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5906   {
5907     int center_element = Tile[ex][ey];
5908     int ce_value = CustomValue[ex][ey];
5909     int ce_score = element_info[center_element].collect_score;
5910     int artwork_element, explosion_element;     // set these values later
5911
5912     // remove things displayed in background while burning dynamite
5913     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5914       Back[ex][ey] = 0;
5915
5916     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5917     {
5918       // put moving element to center field (and let it explode there)
5919       center_element = MovingOrBlocked2Element(ex, ey);
5920       RemoveMovingField(ex, ey);
5921       Tile[ex][ey] = center_element;
5922     }
5923
5924     // now "center_element" is finally determined -- set related values now
5925     artwork_element = center_element;           // for custom player artwork
5926     explosion_element = center_element;         // for custom player artwork
5927
5928     if (IS_PLAYER(ex, ey))
5929     {
5930       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5931
5932       artwork_element = stored_player[player_nr].artwork_element;
5933
5934       if (level.use_explosion_element[player_nr])
5935       {
5936         explosion_element = level.explosion_element[player_nr];
5937         artwork_element = explosion_element;
5938       }
5939     }
5940
5941     if (mode == EX_TYPE_NORMAL ||
5942         mode == EX_TYPE_CENTER ||
5943         mode == EX_TYPE_CROSS)
5944       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5945
5946     last_phase = element_info[explosion_element].explosion_delay + 1;
5947
5948     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5949     {
5950       int xx = x - ex + 1;
5951       int yy = y - ey + 1;
5952       int element;
5953
5954       if (!IN_LEV_FIELD(x, y) ||
5955           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5956           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5957         continue;
5958
5959       element = Tile[x][y];
5960
5961       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5962       {
5963         element = MovingOrBlocked2Element(x, y);
5964
5965         if (!IS_EXPLOSION_PROOF(element))
5966           RemoveMovingField(x, y);
5967       }
5968
5969       // indestructible elements can only explode in center (but not flames)
5970       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5971                                            mode == EX_TYPE_BORDER)) ||
5972           element == EL_FLAMES)
5973         continue;
5974
5975       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5976          behaviour, for example when touching a yamyam that explodes to rocks
5977          with active deadly shield, a rock is created under the player !!! */
5978       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5979 #if 0
5980       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5981           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5982            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5983 #else
5984       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5985 #endif
5986       {
5987         if (IS_ACTIVE_BOMB(element))
5988         {
5989           // re-activate things under the bomb like gate or penguin
5990           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5991           Back[x][y] = 0;
5992         }
5993
5994         continue;
5995       }
5996
5997       // save walkable background elements while explosion on same tile
5998       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5999           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6000         Back[x][y] = element;
6001
6002       // ignite explodable elements reached by other explosion
6003       if (element == EL_EXPLOSION)
6004         element = Store2[x][y];
6005
6006       if (AmoebaNr[x][y] &&
6007           (element == EL_AMOEBA_FULL ||
6008            element == EL_BD_AMOEBA ||
6009            element == EL_AMOEBA_GROWING))
6010       {
6011         AmoebaCnt[AmoebaNr[x][y]]--;
6012         AmoebaCnt2[AmoebaNr[x][y]]--;
6013       }
6014
6015       RemoveField(x, y);
6016
6017       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6018       {
6019         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6020
6021         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6022
6023         if (PLAYERINFO(ex, ey)->use_murphy)
6024           Store[x][y] = EL_EMPTY;
6025       }
6026
6027       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6028       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6029       else if (IS_PLAYER_ELEMENT(center_element))
6030         Store[x][y] = EL_EMPTY;
6031       else if (center_element == EL_YAMYAM)
6032         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6033       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6034         Store[x][y] = element_info[center_element].content.e[xx][yy];
6035 #if 1
6036       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6037       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6038       // otherwise) -- FIX THIS !!!
6039       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6040         Store[x][y] = element_info[element].content.e[1][1];
6041 #else
6042       else if (!CAN_EXPLODE(element))
6043         Store[x][y] = element_info[element].content.e[1][1];
6044 #endif
6045       else
6046         Store[x][y] = EL_EMPTY;
6047
6048       if (IS_CUSTOM_ELEMENT(center_element))
6049         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6050                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6051                        Store[x][y] >= EL_PREV_CE_8 &&
6052                        Store[x][y] <= EL_NEXT_CE_8 ?
6053                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6054                        Store[x][y]);
6055
6056       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6057           center_element == EL_AMOEBA_TO_DIAMOND)
6058         Store2[x][y] = element;
6059
6060       Tile[x][y] = EL_EXPLOSION;
6061       GfxElement[x][y] = artwork_element;
6062
6063       ExplodePhase[x][y] = 1;
6064       ExplodeDelay[x][y] = last_phase;
6065
6066       Stop[x][y] = TRUE;
6067     }
6068
6069     if (center_element == EL_YAMYAM)
6070       game.yamyam_content_nr =
6071         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6072
6073     return;
6074   }
6075
6076   if (Stop[ex][ey])
6077     return;
6078
6079   x = ex;
6080   y = ey;
6081
6082   if (phase == 1)
6083     GfxFrame[x][y] = 0;         // restart explosion animation
6084
6085   last_phase = ExplodeDelay[x][y];
6086
6087   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6088
6089   // this can happen if the player leaves an explosion just in time
6090   if (GfxElement[x][y] == EL_UNDEFINED)
6091     GfxElement[x][y] = EL_EMPTY;
6092
6093   border_element = Store2[x][y];
6094   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6095     border_element = StorePlayer[x][y];
6096
6097   if (phase == element_info[border_element].ignition_delay ||
6098       phase == last_phase)
6099   {
6100     boolean border_explosion = FALSE;
6101
6102     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6103         !PLAYER_EXPLOSION_PROTECTED(x, y))
6104     {
6105       KillPlayerUnlessExplosionProtected(x, y);
6106       border_explosion = TRUE;
6107     }
6108     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6109     {
6110       Tile[x][y] = Store2[x][y];
6111       Store2[x][y] = 0;
6112       Bang(x, y);
6113       border_explosion = TRUE;
6114     }
6115     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6116     {
6117       AmoebaToDiamond(x, y);
6118       Store2[x][y] = 0;
6119       border_explosion = TRUE;
6120     }
6121
6122     // if an element just explodes due to another explosion (chain-reaction),
6123     // do not immediately end the new explosion when it was the last frame of
6124     // the explosion (as it would be done in the following "if"-statement!)
6125     if (border_explosion && phase == last_phase)
6126       return;
6127   }
6128
6129   // this can happen if the player was just killed by an explosion
6130   if (GfxElement[x][y] == EL_UNDEFINED)
6131     GfxElement[x][y] = EL_EMPTY;
6132
6133   if (phase == last_phase)
6134   {
6135     int element;
6136
6137     element = Tile[x][y] = Store[x][y];
6138     Store[x][y] = Store2[x][y] = 0;
6139     GfxElement[x][y] = EL_UNDEFINED;
6140
6141     // player can escape from explosions and might therefore be still alive
6142     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6143         element <= EL_PLAYER_IS_EXPLODING_4)
6144     {
6145       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6146       int explosion_element = EL_PLAYER_1 + player_nr;
6147       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6148       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6149
6150       if (level.use_explosion_element[player_nr])
6151         explosion_element = level.explosion_element[player_nr];
6152
6153       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6154                     element_info[explosion_element].content.e[xx][yy]);
6155     }
6156
6157     // restore probably existing indestructible background element
6158     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6159       element = Tile[x][y] = Back[x][y];
6160     Back[x][y] = 0;
6161
6162     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6163     GfxDir[x][y] = MV_NONE;
6164     ChangeDelay[x][y] = 0;
6165     ChangePage[x][y] = -1;
6166
6167     CustomValue[x][y] = 0;
6168
6169     InitField_WithBug2(x, y, FALSE);
6170
6171     TEST_DrawLevelField(x, y);
6172
6173     TestIfElementTouchesCustomElement(x, y);
6174
6175     if (GFX_CRUMBLED(element))
6176       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6177
6178     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6179       StorePlayer[x][y] = 0;
6180
6181     if (IS_PLAYER_ELEMENT(element))
6182       RelocatePlayer(x, y, element);
6183   }
6184   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6185   {
6186     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6187     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6188
6189     if (phase == 1)
6190       TEST_DrawLevelFieldCrumbled(x, y);
6191
6192     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6193     {
6194       DrawLevelElement(x, y, Back[x][y]);
6195       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6196     }
6197     else if (IS_WALKABLE_UNDER(Back[x][y]))
6198     {
6199       DrawLevelGraphic(x, y, graphic, frame);
6200       DrawLevelElementThruMask(x, y, Back[x][y]);
6201     }
6202     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6203       DrawLevelGraphic(x, y, graphic, frame);
6204   }
6205 }
6206
6207 static void DynaExplode(int ex, int ey)
6208 {
6209   int i, j;
6210   int dynabomb_element = Tile[ex][ey];
6211   int dynabomb_size = 1;
6212   boolean dynabomb_xl = FALSE;
6213   struct PlayerInfo *player;
6214   struct XY *xy = xy_topdown;
6215
6216   if (IS_ACTIVE_BOMB(dynabomb_element))
6217   {
6218     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6219     dynabomb_size = player->dynabomb_size;
6220     dynabomb_xl = player->dynabomb_xl;
6221     player->dynabombs_left++;
6222   }
6223
6224   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6225
6226   for (i = 0; i < NUM_DIRECTIONS; i++)
6227   {
6228     for (j = 1; j <= dynabomb_size; j++)
6229     {
6230       int x = ex + j * xy[i].x;
6231       int y = ey + j * xy[i].y;
6232       int element;
6233
6234       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6235         break;
6236
6237       element = Tile[x][y];
6238
6239       // do not restart explosions of fields with active bombs
6240       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6241         continue;
6242
6243       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6244
6245       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6246           !IS_DIGGABLE(element) && !dynabomb_xl)
6247         break;
6248     }
6249   }
6250 }
6251
6252 void Bang(int x, int y)
6253 {
6254   int element = MovingOrBlocked2Element(x, y);
6255   int explosion_type = EX_TYPE_NORMAL;
6256
6257   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6258   {
6259     struct PlayerInfo *player = PLAYERINFO(x, y);
6260
6261     element = Tile[x][y] = player->initial_element;
6262
6263     if (level.use_explosion_element[player->index_nr])
6264     {
6265       int explosion_element = level.explosion_element[player->index_nr];
6266
6267       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6268         explosion_type = EX_TYPE_CROSS;
6269       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6270         explosion_type = EX_TYPE_CENTER;
6271     }
6272   }
6273
6274   switch (element)
6275   {
6276     case EL_BUG:
6277     case EL_SPACESHIP:
6278     case EL_BD_BUTTERFLY:
6279     case EL_BD_FIREFLY:
6280     case EL_YAMYAM:
6281     case EL_DARK_YAMYAM:
6282     case EL_ROBOT:
6283     case EL_PACMAN:
6284     case EL_MOLE:
6285       RaiseScoreElement(element);
6286       break;
6287
6288     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6289     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6290     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6291     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6292     case EL_DYNABOMB_INCREASE_NUMBER:
6293     case EL_DYNABOMB_INCREASE_SIZE:
6294     case EL_DYNABOMB_INCREASE_POWER:
6295       explosion_type = EX_TYPE_DYNA;
6296       break;
6297
6298     case EL_DC_LANDMINE:
6299       explosion_type = EX_TYPE_CENTER;
6300       break;
6301
6302     case EL_PENGUIN:
6303     case EL_LAMP:
6304     case EL_LAMP_ACTIVE:
6305     case EL_AMOEBA_TO_DIAMOND:
6306       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6307         explosion_type = EX_TYPE_CENTER;
6308       break;
6309
6310     default:
6311       if (element_info[element].explosion_type == EXPLODES_CROSS)
6312         explosion_type = EX_TYPE_CROSS;
6313       else if (element_info[element].explosion_type == EXPLODES_1X1)
6314         explosion_type = EX_TYPE_CENTER;
6315       break;
6316   }
6317
6318   if (explosion_type == EX_TYPE_DYNA)
6319     DynaExplode(x, y);
6320   else
6321     Explode(x, y, EX_PHASE_START, explosion_type);
6322
6323   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6324 }
6325
6326 static void SplashAcid(int x, int y)
6327 {
6328   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6329       (!IN_LEV_FIELD(x - 1, y - 2) ||
6330        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6331     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6332
6333   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6334       (!IN_LEV_FIELD(x + 1, y - 2) ||
6335        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6336     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6337
6338   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6339 }
6340
6341 static void InitBeltMovement(void)
6342 {
6343   static int belt_base_element[4] =
6344   {
6345     EL_CONVEYOR_BELT_1_LEFT,
6346     EL_CONVEYOR_BELT_2_LEFT,
6347     EL_CONVEYOR_BELT_3_LEFT,
6348     EL_CONVEYOR_BELT_4_LEFT
6349   };
6350   static int belt_base_active_element[4] =
6351   {
6352     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6354     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6355     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6356   };
6357
6358   int x, y, i, j;
6359
6360   // set frame order for belt animation graphic according to belt direction
6361   for (i = 0; i < NUM_BELTS; i++)
6362   {
6363     int belt_nr = i;
6364
6365     for (j = 0; j < NUM_BELT_PARTS; j++)
6366     {
6367       int element = belt_base_active_element[belt_nr] + j;
6368       int graphic_1 = el2img(element);
6369       int graphic_2 = el2panelimg(element);
6370
6371       if (game.belt_dir[i] == MV_LEFT)
6372       {
6373         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6374         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6375       }
6376       else
6377       {
6378         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6379         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6380       }
6381     }
6382   }
6383
6384   SCAN_PLAYFIELD(x, y)
6385   {
6386     int element = Tile[x][y];
6387
6388     for (i = 0; i < NUM_BELTS; i++)
6389     {
6390       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6391       {
6392         int e_belt_nr = getBeltNrFromBeltElement(element);
6393         int belt_nr = i;
6394
6395         if (e_belt_nr == belt_nr)
6396         {
6397           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6398
6399           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6400         }
6401       }
6402     }
6403   }
6404 }
6405
6406 static void ToggleBeltSwitch(int x, int y)
6407 {
6408   static int belt_base_element[4] =
6409   {
6410     EL_CONVEYOR_BELT_1_LEFT,
6411     EL_CONVEYOR_BELT_2_LEFT,
6412     EL_CONVEYOR_BELT_3_LEFT,
6413     EL_CONVEYOR_BELT_4_LEFT
6414   };
6415   static int belt_base_active_element[4] =
6416   {
6417     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6418     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6419     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6420     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6421   };
6422   static int belt_base_switch_element[4] =
6423   {
6424     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6425     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6426     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6427     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6428   };
6429   static int belt_move_dir[4] =
6430   {
6431     MV_LEFT,
6432     MV_NONE,
6433     MV_RIGHT,
6434     MV_NONE,
6435   };
6436
6437   int element = Tile[x][y];
6438   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6439   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6440   int belt_dir = belt_move_dir[belt_dir_nr];
6441   int xx, yy, i;
6442
6443   if (!IS_BELT_SWITCH(element))
6444     return;
6445
6446   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6447   game.belt_dir[belt_nr] = belt_dir;
6448
6449   if (belt_dir_nr == 3)
6450     belt_dir_nr = 1;
6451
6452   // set frame order for belt animation graphic according to belt direction
6453   for (i = 0; i < NUM_BELT_PARTS; i++)
6454   {
6455     int element = belt_base_active_element[belt_nr] + i;
6456     int graphic_1 = el2img(element);
6457     int graphic_2 = el2panelimg(element);
6458
6459     if (belt_dir == MV_LEFT)
6460     {
6461       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6462       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6463     }
6464     else
6465     {
6466       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6467       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6468     }
6469   }
6470
6471   SCAN_PLAYFIELD(xx, yy)
6472   {
6473     int element = Tile[xx][yy];
6474
6475     if (IS_BELT_SWITCH(element))
6476     {
6477       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6478
6479       if (e_belt_nr == belt_nr)
6480       {
6481         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6482         TEST_DrawLevelField(xx, yy);
6483       }
6484     }
6485     else if (IS_BELT(element) && belt_dir != MV_NONE)
6486     {
6487       int e_belt_nr = getBeltNrFromBeltElement(element);
6488
6489       if (e_belt_nr == belt_nr)
6490       {
6491         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6492
6493         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6494         TEST_DrawLevelField(xx, yy);
6495       }
6496     }
6497     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6498     {
6499       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6500
6501       if (e_belt_nr == belt_nr)
6502       {
6503         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6504
6505         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6506         TEST_DrawLevelField(xx, yy);
6507       }
6508     }
6509   }
6510 }
6511
6512 static void ToggleSwitchgateSwitch(void)
6513 {
6514   int xx, yy;
6515
6516   game.switchgate_pos = !game.switchgate_pos;
6517
6518   SCAN_PLAYFIELD(xx, yy)
6519   {
6520     int element = Tile[xx][yy];
6521
6522     if (element == EL_SWITCHGATE_SWITCH_UP)
6523     {
6524       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6525       TEST_DrawLevelField(xx, yy);
6526     }
6527     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6528     {
6529       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6530       TEST_DrawLevelField(xx, yy);
6531     }
6532     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6533     {
6534       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6535       TEST_DrawLevelField(xx, yy);
6536     }
6537     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6538     {
6539       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6540       TEST_DrawLevelField(xx, yy);
6541     }
6542     else if (element == EL_SWITCHGATE_OPEN ||
6543              element == EL_SWITCHGATE_OPENING)
6544     {
6545       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6546
6547       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6548     }
6549     else if (element == EL_SWITCHGATE_CLOSED ||
6550              element == EL_SWITCHGATE_CLOSING)
6551     {
6552       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6553
6554       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6555     }
6556   }
6557 }
6558
6559 static int getInvisibleActiveFromInvisibleElement(int element)
6560 {
6561   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6562           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6563           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6564           element);
6565 }
6566
6567 static int getInvisibleFromInvisibleActiveElement(int element)
6568 {
6569   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6570           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6571           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6572           element);
6573 }
6574
6575 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6576 {
6577   int x, y;
6578
6579   SCAN_PLAYFIELD(x, y)
6580   {
6581     int element = Tile[x][y];
6582
6583     if (element == EL_LIGHT_SWITCH &&
6584         game.light_time_left > 0)
6585     {
6586       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6587       TEST_DrawLevelField(x, y);
6588     }
6589     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6590              game.light_time_left == 0)
6591     {
6592       Tile[x][y] = EL_LIGHT_SWITCH;
6593       TEST_DrawLevelField(x, y);
6594     }
6595     else if (element == EL_EMC_DRIPPER &&
6596              game.light_time_left > 0)
6597     {
6598       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6599       TEST_DrawLevelField(x, y);
6600     }
6601     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6602              game.light_time_left == 0)
6603     {
6604       Tile[x][y] = EL_EMC_DRIPPER;
6605       TEST_DrawLevelField(x, y);
6606     }
6607     else if (element == EL_INVISIBLE_STEELWALL ||
6608              element == EL_INVISIBLE_WALL ||
6609              element == EL_INVISIBLE_SAND)
6610     {
6611       if (game.light_time_left > 0)
6612         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6613
6614       TEST_DrawLevelField(x, y);
6615
6616       // uncrumble neighbour fields, if needed
6617       if (element == EL_INVISIBLE_SAND)
6618         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6619     }
6620     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6621              element == EL_INVISIBLE_WALL_ACTIVE ||
6622              element == EL_INVISIBLE_SAND_ACTIVE)
6623     {
6624       if (game.light_time_left == 0)
6625         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6626
6627       TEST_DrawLevelField(x, y);
6628
6629       // re-crumble neighbour fields, if needed
6630       if (element == EL_INVISIBLE_SAND)
6631         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6632     }
6633   }
6634 }
6635
6636 static void RedrawAllInvisibleElementsForLenses(void)
6637 {
6638   int x, y;
6639
6640   SCAN_PLAYFIELD(x, y)
6641   {
6642     int element = Tile[x][y];
6643
6644     if (element == EL_EMC_DRIPPER &&
6645         game.lenses_time_left > 0)
6646     {
6647       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6648       TEST_DrawLevelField(x, y);
6649     }
6650     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6651              game.lenses_time_left == 0)
6652     {
6653       Tile[x][y] = EL_EMC_DRIPPER;
6654       TEST_DrawLevelField(x, y);
6655     }
6656     else if (element == EL_INVISIBLE_STEELWALL ||
6657              element == EL_INVISIBLE_WALL ||
6658              element == EL_INVISIBLE_SAND)
6659     {
6660       if (game.lenses_time_left > 0)
6661         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6662
6663       TEST_DrawLevelField(x, y);
6664
6665       // uncrumble neighbour fields, if needed
6666       if (element == EL_INVISIBLE_SAND)
6667         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6668     }
6669     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6670              element == EL_INVISIBLE_WALL_ACTIVE ||
6671              element == EL_INVISIBLE_SAND_ACTIVE)
6672     {
6673       if (game.lenses_time_left == 0)
6674         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6675
6676       TEST_DrawLevelField(x, y);
6677
6678       // re-crumble neighbour fields, if needed
6679       if (element == EL_INVISIBLE_SAND)
6680         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6681     }
6682   }
6683 }
6684
6685 static void RedrawAllInvisibleElementsForMagnifier(void)
6686 {
6687   int x, y;
6688
6689   SCAN_PLAYFIELD(x, y)
6690   {
6691     int element = Tile[x][y];
6692
6693     if (element == EL_EMC_FAKE_GRASS &&
6694         game.magnify_time_left > 0)
6695     {
6696       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6700              game.magnify_time_left == 0)
6701     {
6702       Tile[x][y] = EL_EMC_FAKE_GRASS;
6703       TEST_DrawLevelField(x, y);
6704     }
6705     else if (IS_GATE_GRAY(element) &&
6706              game.magnify_time_left > 0)
6707     {
6708       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6709                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6710                     IS_EM_GATE_GRAY(element) ?
6711                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6712                     IS_EMC_GATE_GRAY(element) ?
6713                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6714                     IS_DC_GATE_GRAY(element) ?
6715                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6716                     element);
6717       TEST_DrawLevelField(x, y);
6718     }
6719     else if (IS_GATE_GRAY_ACTIVE(element) &&
6720              game.magnify_time_left == 0)
6721     {
6722       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6723                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6724                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6725                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6726                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6727                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6728                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6729                     EL_DC_GATE_WHITE_GRAY :
6730                     element);
6731       TEST_DrawLevelField(x, y);
6732     }
6733   }
6734 }
6735
6736 static void ToggleLightSwitch(int x, int y)
6737 {
6738   int element = Tile[x][y];
6739
6740   game.light_time_left =
6741     (element == EL_LIGHT_SWITCH ?
6742      level.time_light * FRAMES_PER_SECOND : 0);
6743
6744   RedrawAllLightSwitchesAndInvisibleElements();
6745 }
6746
6747 static void ActivateTimegateSwitch(int x, int y)
6748 {
6749   int xx, yy;
6750
6751   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6752
6753   SCAN_PLAYFIELD(xx, yy)
6754   {
6755     int element = Tile[xx][yy];
6756
6757     if (element == EL_TIMEGATE_CLOSED ||
6758         element == EL_TIMEGATE_CLOSING)
6759     {
6760       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6761       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6762     }
6763
6764     /*
6765     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6766     {
6767       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6768       TEST_DrawLevelField(xx, yy);
6769     }
6770     */
6771
6772   }
6773
6774   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6775                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6776 }
6777
6778 static void Impact(int x, int y)
6779 {
6780   boolean last_line = (y == lev_fieldy - 1);
6781   boolean object_hit = FALSE;
6782   boolean impact = (last_line || object_hit);
6783   int element = Tile[x][y];
6784   int smashed = EL_STEELWALL;
6785
6786   if (!last_line)       // check if element below was hit
6787   {
6788     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6789       return;
6790
6791     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6792                                          MovDir[x][y + 1] != MV_DOWN ||
6793                                          MovPos[x][y + 1] <= TILEY / 2));
6794
6795     // do not smash moving elements that left the smashed field in time
6796     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6797         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6798       object_hit = FALSE;
6799
6800 #if USE_QUICKSAND_IMPACT_BUGFIX
6801     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6802     {
6803       RemoveMovingField(x, y + 1);
6804       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6805       Tile[x][y + 2] = EL_ROCK;
6806       TEST_DrawLevelField(x, y + 2);
6807
6808       object_hit = TRUE;
6809     }
6810
6811     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6812     {
6813       RemoveMovingField(x, y + 1);
6814       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6815       Tile[x][y + 2] = EL_ROCK;
6816       TEST_DrawLevelField(x, y + 2);
6817
6818       object_hit = TRUE;
6819     }
6820 #endif
6821
6822     if (object_hit)
6823       smashed = MovingOrBlocked2Element(x, y + 1);
6824
6825     impact = (last_line || object_hit);
6826   }
6827
6828   if (!last_line && smashed == EL_ACID) // element falls into acid
6829   {
6830     SplashAcid(x, y + 1);
6831     return;
6832   }
6833
6834   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6835   // only reset graphic animation if graphic really changes after impact
6836   if (impact &&
6837       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6838   {
6839     ResetGfxAnimation(x, y);
6840     TEST_DrawLevelField(x, y);
6841   }
6842
6843   if (impact && CAN_EXPLODE_IMPACT(element))
6844   {
6845     Bang(x, y);
6846     return;
6847   }
6848   else if (impact && element == EL_PEARL &&
6849            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6850   {
6851     ResetGfxAnimation(x, y);
6852
6853     Tile[x][y] = EL_PEARL_BREAKING;
6854     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6855     return;
6856   }
6857   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6858   {
6859     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6860
6861     return;
6862   }
6863
6864   if (impact && element == EL_AMOEBA_DROP)
6865   {
6866     if (object_hit && IS_PLAYER(x, y + 1))
6867       KillPlayerUnlessEnemyProtected(x, y + 1);
6868     else if (object_hit && smashed == EL_PENGUIN)
6869       Bang(x, y + 1);
6870     else
6871     {
6872       Tile[x][y] = EL_AMOEBA_GROWING;
6873       Store[x][y] = EL_AMOEBA_WET;
6874
6875       ResetRandomAnimationValue(x, y);
6876     }
6877     return;
6878   }
6879
6880   if (object_hit)               // check which object was hit
6881   {
6882     if ((CAN_PASS_MAGIC_WALL(element) && 
6883          (smashed == EL_MAGIC_WALL ||
6884           smashed == EL_BD_MAGIC_WALL)) ||
6885         (CAN_PASS_DC_MAGIC_WALL(element) &&
6886          smashed == EL_DC_MAGIC_WALL))
6887     {
6888       int xx, yy;
6889       int activated_magic_wall =
6890         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6891          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6892          EL_DC_MAGIC_WALL_ACTIVE);
6893
6894       // activate magic wall / mill
6895       SCAN_PLAYFIELD(xx, yy)
6896       {
6897         if (Tile[xx][yy] == smashed)
6898           Tile[xx][yy] = activated_magic_wall;
6899       }
6900
6901       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6902       game.magic_wall_active = TRUE;
6903
6904       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6905                             SND_MAGIC_WALL_ACTIVATING :
6906                             smashed == EL_BD_MAGIC_WALL ?
6907                             SND_BD_MAGIC_WALL_ACTIVATING :
6908                             SND_DC_MAGIC_WALL_ACTIVATING));
6909     }
6910
6911     if (IS_PLAYER(x, y + 1))
6912     {
6913       if (CAN_SMASH_PLAYER(element))
6914       {
6915         KillPlayerUnlessEnemyProtected(x, y + 1);
6916         return;
6917       }
6918     }
6919     else if (smashed == EL_PENGUIN)
6920     {
6921       if (CAN_SMASH_PLAYER(element))
6922       {
6923         Bang(x, y + 1);
6924         return;
6925       }
6926     }
6927     else if (element == EL_BD_DIAMOND)
6928     {
6929       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6930       {
6931         Bang(x, y + 1);
6932         return;
6933       }
6934     }
6935     else if (((element == EL_SP_INFOTRON ||
6936                element == EL_SP_ZONK) &&
6937               (smashed == EL_SP_SNIKSNAK ||
6938                smashed == EL_SP_ELECTRON ||
6939                smashed == EL_SP_DISK_ORANGE)) ||
6940              (element == EL_SP_INFOTRON &&
6941               smashed == EL_SP_DISK_YELLOW))
6942     {
6943       Bang(x, y + 1);
6944       return;
6945     }
6946     else if (CAN_SMASH_EVERYTHING(element))
6947     {
6948       if (IS_CLASSIC_ENEMY(smashed) ||
6949           CAN_EXPLODE_SMASHED(smashed))
6950       {
6951         Bang(x, y + 1);
6952         return;
6953       }
6954       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6955       {
6956         if (smashed == EL_LAMP ||
6957             smashed == EL_LAMP_ACTIVE)
6958         {
6959           Bang(x, y + 1);
6960           return;
6961         }
6962         else if (smashed == EL_NUT)
6963         {
6964           Tile[x][y + 1] = EL_NUT_BREAKING;
6965           PlayLevelSound(x, y, SND_NUT_BREAKING);
6966           RaiseScoreElement(EL_NUT);
6967           return;
6968         }
6969         else if (smashed == EL_PEARL)
6970         {
6971           ResetGfxAnimation(x, y);
6972
6973           Tile[x][y + 1] = EL_PEARL_BREAKING;
6974           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6975           return;
6976         }
6977         else if (smashed == EL_DIAMOND)
6978         {
6979           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6980           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6981           return;
6982         }
6983         else if (IS_BELT_SWITCH(smashed))
6984         {
6985           ToggleBeltSwitch(x, y + 1);
6986         }
6987         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6988                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6989                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6990                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6991         {
6992           ToggleSwitchgateSwitch();
6993         }
6994         else if (smashed == EL_LIGHT_SWITCH ||
6995                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6996         {
6997           ToggleLightSwitch(x, y + 1);
6998         }
6999         else
7000         {
7001           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7002
7003           CheckElementChangeBySide(x, y + 1, smashed, element,
7004                                    CE_SWITCHED, CH_SIDE_TOP);
7005           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7006                                             CH_SIDE_TOP);
7007         }
7008       }
7009       else
7010       {
7011         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7012       }
7013     }
7014   }
7015
7016   // play sound of magic wall / mill
7017   if (!last_line &&
7018       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7019        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7020        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7021   {
7022     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7023       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7024     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7025       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7026     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7027       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7028
7029     return;
7030   }
7031
7032   // play sound of object that hits the ground
7033   if (last_line || object_hit)
7034     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7035 }
7036
7037 static void TurnRoundExt(int x, int y)
7038 {
7039   static struct
7040   {
7041     int dx, dy;
7042   } move_xy[] =
7043   {
7044     {  0,  0 },
7045     { -1,  0 },
7046     { +1,  0 },
7047     {  0,  0 },
7048     {  0, -1 },
7049     {  0,  0 }, { 0, 0 }, { 0, 0 },
7050     {  0, +1 }
7051   };
7052   static struct
7053   {
7054     int left, right, back;
7055   } turn[] =
7056   {
7057     { 0,        0,              0        },
7058     { MV_DOWN,  MV_UP,          MV_RIGHT },
7059     { MV_UP,    MV_DOWN,        MV_LEFT  },
7060     { 0,        0,              0        },
7061     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7062     { 0,        0,              0        },
7063     { 0,        0,              0        },
7064     { 0,        0,              0        },
7065     { MV_RIGHT, MV_LEFT,        MV_UP    }
7066   };
7067
7068   int element = Tile[x][y];
7069   int move_pattern = element_info[element].move_pattern;
7070
7071   int old_move_dir = MovDir[x][y];
7072   int left_dir  = turn[old_move_dir].left;
7073   int right_dir = turn[old_move_dir].right;
7074   int back_dir  = turn[old_move_dir].back;
7075
7076   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7077   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7078   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7079   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7080
7081   int left_x  = x + left_dx,  left_y  = y + left_dy;
7082   int right_x = x + right_dx, right_y = y + right_dy;
7083   int move_x  = x + move_dx,  move_y  = y + move_dy;
7084
7085   int xx, yy;
7086
7087   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7088   {
7089     TestIfBadThingTouchesOtherBadThing(x, y);
7090
7091     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7092       MovDir[x][y] = right_dir;
7093     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7094       MovDir[x][y] = left_dir;
7095
7096     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7097       MovDelay[x][y] = 9;
7098     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7099       MovDelay[x][y] = 1;
7100   }
7101   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7102   {
7103     TestIfBadThingTouchesOtherBadThing(x, y);
7104
7105     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7106       MovDir[x][y] = left_dir;
7107     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7108       MovDir[x][y] = right_dir;
7109
7110     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7111       MovDelay[x][y] = 9;
7112     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7113       MovDelay[x][y] = 1;
7114   }
7115   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7116   {
7117     TestIfBadThingTouchesOtherBadThing(x, y);
7118
7119     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7120       MovDir[x][y] = left_dir;
7121     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7122       MovDir[x][y] = right_dir;
7123
7124     if (MovDir[x][y] != old_move_dir)
7125       MovDelay[x][y] = 9;
7126   }
7127   else if (element == EL_YAMYAM)
7128   {
7129     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7130     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7131
7132     if (can_turn_left && can_turn_right)
7133       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7134     else if (can_turn_left)
7135       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7136     else if (can_turn_right)
7137       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7138     else
7139       MovDir[x][y] = back_dir;
7140
7141     MovDelay[x][y] = 16 + 16 * RND(3);
7142   }
7143   else if (element == EL_DARK_YAMYAM)
7144   {
7145     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7146                                                          left_x, left_y);
7147     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7148                                                          right_x, right_y);
7149
7150     if (can_turn_left && can_turn_right)
7151       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7152     else if (can_turn_left)
7153       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7154     else if (can_turn_right)
7155       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7156     else
7157       MovDir[x][y] = back_dir;
7158
7159     MovDelay[x][y] = 16 + 16 * RND(3);
7160   }
7161   else if (element == EL_PACMAN)
7162   {
7163     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7164     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7165
7166     if (can_turn_left && can_turn_right)
7167       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7168     else if (can_turn_left)
7169       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7170     else if (can_turn_right)
7171       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7172     else
7173       MovDir[x][y] = back_dir;
7174
7175     MovDelay[x][y] = 6 + RND(40);
7176   }
7177   else if (element == EL_PIG)
7178   {
7179     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7180     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7181     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7182     boolean should_turn_left, should_turn_right, should_move_on;
7183     int rnd_value = 24;
7184     int rnd = RND(rnd_value);
7185
7186     should_turn_left = (can_turn_left &&
7187                         (!can_move_on ||
7188                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7189                                                    y + back_dy + left_dy)));
7190     should_turn_right = (can_turn_right &&
7191                          (!can_move_on ||
7192                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7193                                                     y + back_dy + right_dy)));
7194     should_move_on = (can_move_on &&
7195                       (!can_turn_left ||
7196                        !can_turn_right ||
7197                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7198                                                  y + move_dy + left_dy) ||
7199                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7200                                                  y + move_dy + right_dy)));
7201
7202     if (should_turn_left || should_turn_right || should_move_on)
7203     {
7204       if (should_turn_left && should_turn_right && should_move_on)
7205         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7206                         rnd < 2 * rnd_value / 3 ? right_dir :
7207                         old_move_dir);
7208       else if (should_turn_left && should_turn_right)
7209         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7210       else if (should_turn_left && should_move_on)
7211         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7212       else if (should_turn_right && should_move_on)
7213         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7214       else if (should_turn_left)
7215         MovDir[x][y] = left_dir;
7216       else if (should_turn_right)
7217         MovDir[x][y] = right_dir;
7218       else if (should_move_on)
7219         MovDir[x][y] = old_move_dir;
7220     }
7221     else if (can_move_on && rnd > rnd_value / 8)
7222       MovDir[x][y] = old_move_dir;
7223     else if (can_turn_left && can_turn_right)
7224       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7225     else if (can_turn_left && rnd > rnd_value / 8)
7226       MovDir[x][y] = left_dir;
7227     else if (can_turn_right && rnd > rnd_value/8)
7228       MovDir[x][y] = right_dir;
7229     else
7230       MovDir[x][y] = back_dir;
7231
7232     xx = x + move_xy[MovDir[x][y]].dx;
7233     yy = y + move_xy[MovDir[x][y]].dy;
7234
7235     if (!IN_LEV_FIELD(xx, yy) ||
7236         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7237       MovDir[x][y] = old_move_dir;
7238
7239     MovDelay[x][y] = 0;
7240   }
7241   else if (element == EL_DRAGON)
7242   {
7243     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7244     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7245     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7246     int rnd_value = 24;
7247     int rnd = RND(rnd_value);
7248
7249     if (can_move_on && rnd > rnd_value / 8)
7250       MovDir[x][y] = old_move_dir;
7251     else if (can_turn_left && can_turn_right)
7252       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7253     else if (can_turn_left && rnd > rnd_value / 8)
7254       MovDir[x][y] = left_dir;
7255     else if (can_turn_right && rnd > rnd_value / 8)
7256       MovDir[x][y] = right_dir;
7257     else
7258       MovDir[x][y] = back_dir;
7259
7260     xx = x + move_xy[MovDir[x][y]].dx;
7261     yy = y + move_xy[MovDir[x][y]].dy;
7262
7263     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7264       MovDir[x][y] = old_move_dir;
7265
7266     MovDelay[x][y] = 0;
7267   }
7268   else if (element == EL_MOLE)
7269   {
7270     boolean can_move_on =
7271       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7272                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7273                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7274     if (!can_move_on)
7275     {
7276       boolean can_turn_left =
7277         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7278                               IS_AMOEBOID(Tile[left_x][left_y])));
7279
7280       boolean can_turn_right =
7281         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7282                               IS_AMOEBOID(Tile[right_x][right_y])));
7283
7284       if (can_turn_left && can_turn_right)
7285         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7286       else if (can_turn_left)
7287         MovDir[x][y] = left_dir;
7288       else
7289         MovDir[x][y] = right_dir;
7290     }
7291
7292     if (MovDir[x][y] != old_move_dir)
7293       MovDelay[x][y] = 9;
7294   }
7295   else if (element == EL_BALLOON)
7296   {
7297     MovDir[x][y] = game.wind_direction;
7298     MovDelay[x][y] = 0;
7299   }
7300   else if (element == EL_SPRING)
7301   {
7302     if (MovDir[x][y] & MV_HORIZONTAL)
7303     {
7304       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7305           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7306       {
7307         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7308         ResetGfxAnimation(move_x, move_y);
7309         TEST_DrawLevelField(move_x, move_y);
7310
7311         MovDir[x][y] = back_dir;
7312       }
7313       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7314                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7315         MovDir[x][y] = MV_NONE;
7316     }
7317
7318     MovDelay[x][y] = 0;
7319   }
7320   else if (element == EL_ROBOT ||
7321            element == EL_SATELLITE ||
7322            element == EL_PENGUIN ||
7323            element == EL_EMC_ANDROID)
7324   {
7325     int attr_x = -1, attr_y = -1;
7326
7327     if (game.all_players_gone)
7328     {
7329       attr_x = game.exit_x;
7330       attr_y = game.exit_y;
7331     }
7332     else
7333     {
7334       int i;
7335
7336       for (i = 0; i < MAX_PLAYERS; i++)
7337       {
7338         struct PlayerInfo *player = &stored_player[i];
7339         int jx = player->jx, jy = player->jy;
7340
7341         if (!player->active)
7342           continue;
7343
7344         if (attr_x == -1 ||
7345             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7346         {
7347           attr_x = jx;
7348           attr_y = jy;
7349         }
7350       }
7351     }
7352
7353     if (element == EL_ROBOT &&
7354         game.robot_wheel_x >= 0 &&
7355         game.robot_wheel_y >= 0 &&
7356         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7357          game.engine_version < VERSION_IDENT(3,1,0,0)))
7358     {
7359       attr_x = game.robot_wheel_x;
7360       attr_y = game.robot_wheel_y;
7361     }
7362
7363     if (element == EL_PENGUIN)
7364     {
7365       int i;
7366       struct XY *xy = xy_topdown;
7367
7368       for (i = 0; i < NUM_DIRECTIONS; i++)
7369       {
7370         int ex = x + xy[i].x;
7371         int ey = y + xy[i].y;
7372
7373         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7374                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7375                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7376                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7377         {
7378           attr_x = ex;
7379           attr_y = ey;
7380           break;
7381         }
7382       }
7383     }
7384
7385     MovDir[x][y] = MV_NONE;
7386     if (attr_x < x)
7387       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7388     else if (attr_x > x)
7389       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7390     if (attr_y < y)
7391       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7392     else if (attr_y > y)
7393       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7394
7395     if (element == EL_ROBOT)
7396     {
7397       int newx, newy;
7398
7399       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7400         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7401       Moving2Blocked(x, y, &newx, &newy);
7402
7403       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7404         MovDelay[x][y] = 8 + 8 * !RND(3);
7405       else
7406         MovDelay[x][y] = 16;
7407     }
7408     else if (element == EL_PENGUIN)
7409     {
7410       int newx, newy;
7411
7412       MovDelay[x][y] = 1;
7413
7414       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7415       {
7416         boolean first_horiz = RND(2);
7417         int new_move_dir = MovDir[x][y];
7418
7419         MovDir[x][y] =
7420           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7421         Moving2Blocked(x, y, &newx, &newy);
7422
7423         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7424           return;
7425
7426         MovDir[x][y] =
7427           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7428         Moving2Blocked(x, y, &newx, &newy);
7429
7430         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7431           return;
7432
7433         MovDir[x][y] = old_move_dir;
7434         return;
7435       }
7436     }
7437     else if (element == EL_SATELLITE)
7438     {
7439       int newx, newy;
7440
7441       MovDelay[x][y] = 1;
7442
7443       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7444       {
7445         boolean first_horiz = RND(2);
7446         int new_move_dir = MovDir[x][y];
7447
7448         MovDir[x][y] =
7449           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7450         Moving2Blocked(x, y, &newx, &newy);
7451
7452         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7453           return;
7454
7455         MovDir[x][y] =
7456           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7457         Moving2Blocked(x, y, &newx, &newy);
7458
7459         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7460           return;
7461
7462         MovDir[x][y] = old_move_dir;
7463         return;
7464       }
7465     }
7466     else if (element == EL_EMC_ANDROID)
7467     {
7468       static int check_pos[16] =
7469       {
7470         -1,             //  0 => (invalid)
7471         7,              //  1 => MV_LEFT
7472         3,              //  2 => MV_RIGHT
7473         -1,             //  3 => (invalid)
7474         1,              //  4 =>            MV_UP
7475         0,              //  5 => MV_LEFT  | MV_UP
7476         2,              //  6 => MV_RIGHT | MV_UP
7477         -1,             //  7 => (invalid)
7478         5,              //  8 =>            MV_DOWN
7479         6,              //  9 => MV_LEFT  | MV_DOWN
7480         4,              // 10 => MV_RIGHT | MV_DOWN
7481         -1,             // 11 => (invalid)
7482         -1,             // 12 => (invalid)
7483         -1,             // 13 => (invalid)
7484         -1,             // 14 => (invalid)
7485         -1,             // 15 => (invalid)
7486       };
7487       static struct
7488       {
7489         int dx, dy;
7490         int dir;
7491       } check_xy[8] =
7492       {
7493         { -1, -1,       MV_LEFT  | MV_UP   },
7494         {  0, -1,                  MV_UP   },
7495         { +1, -1,       MV_RIGHT | MV_UP   },
7496         { +1,  0,       MV_RIGHT           },
7497         { +1, +1,       MV_RIGHT | MV_DOWN },
7498         {  0, +1,                  MV_DOWN },
7499         { -1, +1,       MV_LEFT  | MV_DOWN },
7500         { -1,  0,       MV_LEFT            },
7501       };
7502       int start_pos, check_order;
7503       boolean can_clone = FALSE;
7504       int i;
7505
7506       // check if there is any free field around current position
7507       for (i = 0; i < 8; i++)
7508       {
7509         int newx = x + check_xy[i].dx;
7510         int newy = y + check_xy[i].dy;
7511
7512         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7513         {
7514           can_clone = TRUE;
7515
7516           break;
7517         }
7518       }
7519
7520       if (can_clone)            // randomly find an element to clone
7521       {
7522         can_clone = FALSE;
7523
7524         start_pos = check_pos[RND(8)];
7525         check_order = (RND(2) ? -1 : +1);
7526
7527         for (i = 0; i < 8; i++)
7528         {
7529           int pos_raw = start_pos + i * check_order;
7530           int pos = (pos_raw + 8) % 8;
7531           int newx = x + check_xy[pos].dx;
7532           int newy = y + check_xy[pos].dy;
7533
7534           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7535           {
7536             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7537             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7538
7539             Store[x][y] = Tile[newx][newy];
7540
7541             can_clone = TRUE;
7542
7543             break;
7544           }
7545         }
7546       }
7547
7548       if (can_clone)            // randomly find a direction to move
7549       {
7550         can_clone = FALSE;
7551
7552         start_pos = check_pos[RND(8)];
7553         check_order = (RND(2) ? -1 : +1);
7554
7555         for (i = 0; i < 8; i++)
7556         {
7557           int pos_raw = start_pos + i * check_order;
7558           int pos = (pos_raw + 8) % 8;
7559           int newx = x + check_xy[pos].dx;
7560           int newy = y + check_xy[pos].dy;
7561           int new_move_dir = check_xy[pos].dir;
7562
7563           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7564           {
7565             MovDir[x][y] = new_move_dir;
7566             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7567
7568             can_clone = TRUE;
7569
7570             break;
7571           }
7572         }
7573       }
7574
7575       if (can_clone)            // cloning and moving successful
7576         return;
7577
7578       // cannot clone -- try to move towards player
7579
7580       start_pos = check_pos[MovDir[x][y] & 0x0f];
7581       check_order = (RND(2) ? -1 : +1);
7582
7583       for (i = 0; i < 3; i++)
7584       {
7585         // first check start_pos, then previous/next or (next/previous) pos
7586         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7587         int pos = (pos_raw + 8) % 8;
7588         int newx = x + check_xy[pos].dx;
7589         int newy = y + check_xy[pos].dy;
7590         int new_move_dir = check_xy[pos].dir;
7591
7592         if (IS_PLAYER(newx, newy))
7593           break;
7594
7595         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7596         {
7597           MovDir[x][y] = new_move_dir;
7598           MovDelay[x][y] = level.android_move_time * 8 + 1;
7599
7600           break;
7601         }
7602       }
7603     }
7604   }
7605   else if (move_pattern == MV_TURNING_LEFT ||
7606            move_pattern == MV_TURNING_RIGHT ||
7607            move_pattern == MV_TURNING_LEFT_RIGHT ||
7608            move_pattern == MV_TURNING_RIGHT_LEFT ||
7609            move_pattern == MV_TURNING_RANDOM ||
7610            move_pattern == MV_ALL_DIRECTIONS)
7611   {
7612     boolean can_turn_left =
7613       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7614     boolean can_turn_right =
7615       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7616
7617     if (element_info[element].move_stepsize == 0)       // "not moving"
7618       return;
7619
7620     if (move_pattern == MV_TURNING_LEFT)
7621       MovDir[x][y] = left_dir;
7622     else if (move_pattern == MV_TURNING_RIGHT)
7623       MovDir[x][y] = right_dir;
7624     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7625       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7626     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7627       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7628     else if (move_pattern == MV_TURNING_RANDOM)
7629       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7630                       can_turn_right && !can_turn_left ? right_dir :
7631                       RND(2) ? left_dir : right_dir);
7632     else if (can_turn_left && can_turn_right)
7633       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7634     else if (can_turn_left)
7635       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7636     else if (can_turn_right)
7637       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7638     else
7639       MovDir[x][y] = back_dir;
7640
7641     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7642   }
7643   else if (move_pattern == MV_HORIZONTAL ||
7644            move_pattern == MV_VERTICAL)
7645   {
7646     if (move_pattern & old_move_dir)
7647       MovDir[x][y] = back_dir;
7648     else if (move_pattern == MV_HORIZONTAL)
7649       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7650     else if (move_pattern == MV_VERTICAL)
7651       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7652
7653     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7654   }
7655   else if (move_pattern & MV_ANY_DIRECTION)
7656   {
7657     MovDir[x][y] = move_pattern;
7658     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7659   }
7660   else if (move_pattern & MV_WIND_DIRECTION)
7661   {
7662     MovDir[x][y] = game.wind_direction;
7663     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7664   }
7665   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7666   {
7667     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7668       MovDir[x][y] = left_dir;
7669     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7670       MovDir[x][y] = right_dir;
7671
7672     if (MovDir[x][y] != old_move_dir)
7673       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674   }
7675   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7676   {
7677     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7678       MovDir[x][y] = right_dir;
7679     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7680       MovDir[x][y] = left_dir;
7681
7682     if (MovDir[x][y] != old_move_dir)
7683       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7684   }
7685   else if (move_pattern == MV_TOWARDS_PLAYER ||
7686            move_pattern == MV_AWAY_FROM_PLAYER)
7687   {
7688     int attr_x = -1, attr_y = -1;
7689     int newx, newy;
7690     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7691
7692     if (game.all_players_gone)
7693     {
7694       attr_x = game.exit_x;
7695       attr_y = game.exit_y;
7696     }
7697     else
7698     {
7699       int i;
7700
7701       for (i = 0; i < MAX_PLAYERS; i++)
7702       {
7703         struct PlayerInfo *player = &stored_player[i];
7704         int jx = player->jx, jy = player->jy;
7705
7706         if (!player->active)
7707           continue;
7708
7709         if (attr_x == -1 ||
7710             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7711         {
7712           attr_x = jx;
7713           attr_y = jy;
7714         }
7715       }
7716     }
7717
7718     MovDir[x][y] = MV_NONE;
7719     if (attr_x < x)
7720       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7721     else if (attr_x > x)
7722       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7723     if (attr_y < y)
7724       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7725     else if (attr_y > y)
7726       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7727
7728     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7729
7730     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7731     {
7732       boolean first_horiz = RND(2);
7733       int new_move_dir = MovDir[x][y];
7734
7735       if (element_info[element].move_stepsize == 0)     // "not moving"
7736       {
7737         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7738         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7739
7740         return;
7741       }
7742
7743       MovDir[x][y] =
7744         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7745       Moving2Blocked(x, y, &newx, &newy);
7746
7747       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7748         return;
7749
7750       MovDir[x][y] =
7751         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7752       Moving2Blocked(x, y, &newx, &newy);
7753
7754       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7755         return;
7756
7757       MovDir[x][y] = old_move_dir;
7758     }
7759   }
7760   else if (move_pattern == MV_WHEN_PUSHED ||
7761            move_pattern == MV_WHEN_DROPPED)
7762   {
7763     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7764       MovDir[x][y] = MV_NONE;
7765
7766     MovDelay[x][y] = 0;
7767   }
7768   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7769   {
7770     struct XY *test_xy = xy_topdown;
7771     static int test_dir[4] =
7772     {
7773       MV_UP,
7774       MV_LEFT,
7775       MV_RIGHT,
7776       MV_DOWN
7777     };
7778     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7779     int move_preference = -1000000;     // start with very low preference
7780     int new_move_dir = MV_NONE;
7781     int start_test = RND(4);
7782     int i;
7783
7784     for (i = 0; i < NUM_DIRECTIONS; i++)
7785     {
7786       int j = (start_test + i) % 4;
7787       int move_dir = test_dir[j];
7788       int move_dir_preference;
7789
7790       xx = x + test_xy[j].x;
7791       yy = y + test_xy[j].y;
7792
7793       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7794           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7795       {
7796         new_move_dir = move_dir;
7797
7798         break;
7799       }
7800
7801       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7802         continue;
7803
7804       move_dir_preference = -1 * RunnerVisit[xx][yy];
7805       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7806         move_dir_preference = PlayerVisit[xx][yy];
7807
7808       if (move_dir_preference > move_preference)
7809       {
7810         // prefer field that has not been visited for the longest time
7811         move_preference = move_dir_preference;
7812         new_move_dir = move_dir;
7813       }
7814       else if (move_dir_preference == move_preference &&
7815                move_dir == old_move_dir)
7816       {
7817         // prefer last direction when all directions are preferred equally
7818         move_preference = move_dir_preference;
7819         new_move_dir = move_dir;
7820       }
7821     }
7822
7823     MovDir[x][y] = new_move_dir;
7824     if (old_move_dir != new_move_dir)
7825       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7826   }
7827 }
7828
7829 static void TurnRound(int x, int y)
7830 {
7831   int direction = MovDir[x][y];
7832
7833   TurnRoundExt(x, y);
7834
7835   GfxDir[x][y] = MovDir[x][y];
7836
7837   if (direction != MovDir[x][y])
7838     GfxFrame[x][y] = 0;
7839
7840   if (MovDelay[x][y])
7841     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7842
7843   ResetGfxFrame(x, y);
7844 }
7845
7846 static boolean JustBeingPushed(int x, int y)
7847 {
7848   int i;
7849
7850   for (i = 0; i < MAX_PLAYERS; i++)
7851   {
7852     struct PlayerInfo *player = &stored_player[i];
7853
7854     if (player->active && player->is_pushing && player->MovPos)
7855     {
7856       int next_jx = player->jx + (player->jx - player->last_jx);
7857       int next_jy = player->jy + (player->jy - player->last_jy);
7858
7859       if (x == next_jx && y == next_jy)
7860         return TRUE;
7861     }
7862   }
7863
7864   return FALSE;
7865 }
7866
7867 static void StartMoving(int x, int y)
7868 {
7869   boolean started_moving = FALSE;       // some elements can fall _and_ move
7870   int element = Tile[x][y];
7871
7872   if (Stop[x][y])
7873     return;
7874
7875   if (MovDelay[x][y] == 0)
7876     GfxAction[x][y] = ACTION_DEFAULT;
7877
7878   if (CAN_FALL(element) && y < lev_fieldy - 1)
7879   {
7880     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7881         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7882       if (JustBeingPushed(x, y))
7883         return;
7884
7885     if (element == EL_QUICKSAND_FULL)
7886     {
7887       if (IS_FREE(x, y + 1))
7888       {
7889         InitMovingField(x, y, MV_DOWN);
7890         started_moving = TRUE;
7891
7892         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7893 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7894         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7895           Store[x][y] = EL_ROCK;
7896 #else
7897         Store[x][y] = EL_ROCK;
7898 #endif
7899
7900         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7901       }
7902       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7903       {
7904         if (!MovDelay[x][y])
7905         {
7906           MovDelay[x][y] = TILEY + 1;
7907
7908           ResetGfxAnimation(x, y);
7909           ResetGfxAnimation(x, y + 1);
7910         }
7911
7912         if (MovDelay[x][y])
7913         {
7914           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7915           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7916
7917           MovDelay[x][y]--;
7918           if (MovDelay[x][y])
7919             return;
7920         }
7921
7922         Tile[x][y] = EL_QUICKSAND_EMPTY;
7923         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7924         Store[x][y + 1] = Store[x][y];
7925         Store[x][y] = 0;
7926
7927         PlayLevelSoundAction(x, y, ACTION_FILLING);
7928       }
7929       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7930       {
7931         if (!MovDelay[x][y])
7932         {
7933           MovDelay[x][y] = TILEY + 1;
7934
7935           ResetGfxAnimation(x, y);
7936           ResetGfxAnimation(x, y + 1);
7937         }
7938
7939         if (MovDelay[x][y])
7940         {
7941           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7942           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7943
7944           MovDelay[x][y]--;
7945           if (MovDelay[x][y])
7946             return;
7947         }
7948
7949         Tile[x][y] = EL_QUICKSAND_EMPTY;
7950         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7951         Store[x][y + 1] = Store[x][y];
7952         Store[x][y] = 0;
7953
7954         PlayLevelSoundAction(x, y, ACTION_FILLING);
7955       }
7956     }
7957     else if (element == EL_QUICKSAND_FAST_FULL)
7958     {
7959       if (IS_FREE(x, y + 1))
7960       {
7961         InitMovingField(x, y, MV_DOWN);
7962         started_moving = TRUE;
7963
7964         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7965 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7966         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7967           Store[x][y] = EL_ROCK;
7968 #else
7969         Store[x][y] = EL_ROCK;
7970 #endif
7971
7972         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7973       }
7974       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7975       {
7976         if (!MovDelay[x][y])
7977         {
7978           MovDelay[x][y] = TILEY + 1;
7979
7980           ResetGfxAnimation(x, y);
7981           ResetGfxAnimation(x, y + 1);
7982         }
7983
7984         if (MovDelay[x][y])
7985         {
7986           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7987           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7988
7989           MovDelay[x][y]--;
7990           if (MovDelay[x][y])
7991             return;
7992         }
7993
7994         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7995         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7996         Store[x][y + 1] = Store[x][y];
7997         Store[x][y] = 0;
7998
7999         PlayLevelSoundAction(x, y, ACTION_FILLING);
8000       }
8001       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8002       {
8003         if (!MovDelay[x][y])
8004         {
8005           MovDelay[x][y] = TILEY + 1;
8006
8007           ResetGfxAnimation(x, y);
8008           ResetGfxAnimation(x, y + 1);
8009         }
8010
8011         if (MovDelay[x][y])
8012         {
8013           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8014           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8015
8016           MovDelay[x][y]--;
8017           if (MovDelay[x][y])
8018             return;
8019         }
8020
8021         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8022         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8023         Store[x][y + 1] = Store[x][y];
8024         Store[x][y] = 0;
8025
8026         PlayLevelSoundAction(x, y, ACTION_FILLING);
8027       }
8028     }
8029     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8030              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8031     {
8032       InitMovingField(x, y, MV_DOWN);
8033       started_moving = TRUE;
8034
8035       Tile[x][y] = EL_QUICKSAND_FILLING;
8036       Store[x][y] = element;
8037
8038       PlayLevelSoundAction(x, y, ACTION_FILLING);
8039     }
8040     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8041              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8042     {
8043       InitMovingField(x, y, MV_DOWN);
8044       started_moving = TRUE;
8045
8046       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8047       Store[x][y] = element;
8048
8049       PlayLevelSoundAction(x, y, ACTION_FILLING);
8050     }
8051     else if (element == EL_MAGIC_WALL_FULL)
8052     {
8053       if (IS_FREE(x, y + 1))
8054       {
8055         InitMovingField(x, y, MV_DOWN);
8056         started_moving = TRUE;
8057
8058         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8059         Store[x][y] = EL_CHANGED(Store[x][y]);
8060       }
8061       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8062       {
8063         if (!MovDelay[x][y])
8064           MovDelay[x][y] = TILEY / 4 + 1;
8065
8066         if (MovDelay[x][y])
8067         {
8068           MovDelay[x][y]--;
8069           if (MovDelay[x][y])
8070             return;
8071         }
8072
8073         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8074         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8075         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8076         Store[x][y] = 0;
8077       }
8078     }
8079     else if (element == EL_BD_MAGIC_WALL_FULL)
8080     {
8081       if (IS_FREE(x, y + 1))
8082       {
8083         InitMovingField(x, y, MV_DOWN);
8084         started_moving = TRUE;
8085
8086         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8087         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8088       }
8089       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8090       {
8091         if (!MovDelay[x][y])
8092           MovDelay[x][y] = TILEY / 4 + 1;
8093
8094         if (MovDelay[x][y])
8095         {
8096           MovDelay[x][y]--;
8097           if (MovDelay[x][y])
8098             return;
8099         }
8100
8101         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8102         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8103         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8104         Store[x][y] = 0;
8105       }
8106     }
8107     else if (element == EL_DC_MAGIC_WALL_FULL)
8108     {
8109       if (IS_FREE(x, y + 1))
8110       {
8111         InitMovingField(x, y, MV_DOWN);
8112         started_moving = TRUE;
8113
8114         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8115         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8116       }
8117       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8118       {
8119         if (!MovDelay[x][y])
8120           MovDelay[x][y] = TILEY / 4 + 1;
8121
8122         if (MovDelay[x][y])
8123         {
8124           MovDelay[x][y]--;
8125           if (MovDelay[x][y])
8126             return;
8127         }
8128
8129         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8130         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8131         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8132         Store[x][y] = 0;
8133       }
8134     }
8135     else if ((CAN_PASS_MAGIC_WALL(element) &&
8136               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8137                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8138              (CAN_PASS_DC_MAGIC_WALL(element) &&
8139               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8140
8141     {
8142       InitMovingField(x, y, MV_DOWN);
8143       started_moving = TRUE;
8144
8145       Tile[x][y] =
8146         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8147          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8148          EL_DC_MAGIC_WALL_FILLING);
8149       Store[x][y] = element;
8150     }
8151     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8152     {
8153       SplashAcid(x, y + 1);
8154
8155       InitMovingField(x, y, MV_DOWN);
8156       started_moving = TRUE;
8157
8158       Store[x][y] = EL_ACID;
8159     }
8160     else if (
8161              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8162               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8163              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8164               CAN_FALL(element) && WasJustFalling[x][y] &&
8165               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8166
8167              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8168               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8169               (Tile[x][y + 1] == EL_BLOCKED)))
8170     {
8171       /* this is needed for a special case not covered by calling "Impact()"
8172          from "ContinueMoving()": if an element moves to a tile directly below
8173          another element which was just falling on that tile (which was empty
8174          in the previous frame), the falling element above would just stop
8175          instead of smashing the element below (in previous version, the above
8176          element was just checked for "moving" instead of "falling", resulting
8177          in incorrect smashes caused by horizontal movement of the above
8178          element; also, the case of the player being the element to smash was
8179          simply not covered here... :-/ ) */
8180
8181       CheckCollision[x][y] = 0;
8182       CheckImpact[x][y] = 0;
8183
8184       Impact(x, y);
8185     }
8186     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8187     {
8188       if (MovDir[x][y] == MV_NONE)
8189       {
8190         InitMovingField(x, y, MV_DOWN);
8191         started_moving = TRUE;
8192       }
8193     }
8194     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8195     {
8196       if (WasJustFalling[x][y]) // prevent animation from being restarted
8197         MovDir[x][y] = MV_DOWN;
8198
8199       InitMovingField(x, y, MV_DOWN);
8200       started_moving = TRUE;
8201     }
8202     else if (element == EL_AMOEBA_DROP)
8203     {
8204       Tile[x][y] = EL_AMOEBA_GROWING;
8205       Store[x][y] = EL_AMOEBA_WET;
8206     }
8207     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8208               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8209              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8210              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8211     {
8212       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8213                                 (IS_FREE(x - 1, y + 1) ||
8214                                  Tile[x - 1][y + 1] == EL_ACID));
8215       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8216                                 (IS_FREE(x + 1, y + 1) ||
8217                                  Tile[x + 1][y + 1] == EL_ACID));
8218       boolean can_fall_any  = (can_fall_left || can_fall_right);
8219       boolean can_fall_both = (can_fall_left && can_fall_right);
8220       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8221
8222       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8223       {
8224         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8225           can_fall_right = FALSE;
8226         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8227           can_fall_left = FALSE;
8228         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8229           can_fall_right = FALSE;
8230         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8231           can_fall_left = FALSE;
8232
8233         can_fall_any  = (can_fall_left || can_fall_right);
8234         can_fall_both = FALSE;
8235       }
8236
8237       if (can_fall_both)
8238       {
8239         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8240           can_fall_right = FALSE;       // slip down on left side
8241         else
8242           can_fall_left = !(can_fall_right = RND(2));
8243
8244         can_fall_both = FALSE;
8245       }
8246
8247       if (can_fall_any)
8248       {
8249         // if not determined otherwise, prefer left side for slipping down
8250         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8251         started_moving = TRUE;
8252       }
8253     }
8254     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8255     {
8256       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8257       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8258       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8259       int belt_dir = game.belt_dir[belt_nr];
8260
8261       if ((belt_dir == MV_LEFT  && left_is_free) ||
8262           (belt_dir == MV_RIGHT && right_is_free))
8263       {
8264         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8265
8266         InitMovingField(x, y, belt_dir);
8267         started_moving = TRUE;
8268
8269         Pushed[x][y] = TRUE;
8270         Pushed[nextx][y] = TRUE;
8271
8272         GfxAction[x][y] = ACTION_DEFAULT;
8273       }
8274       else
8275       {
8276         MovDir[x][y] = 0;       // if element was moving, stop it
8277       }
8278     }
8279   }
8280
8281   // not "else if" because of elements that can fall and move (EL_SPRING)
8282   if (CAN_MOVE(element) && !started_moving)
8283   {
8284     int move_pattern = element_info[element].move_pattern;
8285     int newx, newy;
8286
8287     Moving2Blocked(x, y, &newx, &newy);
8288
8289     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8290       return;
8291
8292     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8293         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8294     {
8295       WasJustMoving[x][y] = 0;
8296       CheckCollision[x][y] = 0;
8297
8298       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8299
8300       if (Tile[x][y] != element)        // element has changed
8301         return;
8302     }
8303
8304     if (!MovDelay[x][y])        // start new movement phase
8305     {
8306       // all objects that can change their move direction after each step
8307       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8308
8309       if (element != EL_YAMYAM &&
8310           element != EL_DARK_YAMYAM &&
8311           element != EL_PACMAN &&
8312           !(move_pattern & MV_ANY_DIRECTION) &&
8313           move_pattern != MV_TURNING_LEFT &&
8314           move_pattern != MV_TURNING_RIGHT &&
8315           move_pattern != MV_TURNING_LEFT_RIGHT &&
8316           move_pattern != MV_TURNING_RIGHT_LEFT &&
8317           move_pattern != MV_TURNING_RANDOM)
8318       {
8319         TurnRound(x, y);
8320
8321         if (MovDelay[x][y] && (element == EL_BUG ||
8322                                element == EL_SPACESHIP ||
8323                                element == EL_SP_SNIKSNAK ||
8324                                element == EL_SP_ELECTRON ||
8325                                element == EL_MOLE))
8326           TEST_DrawLevelField(x, y);
8327       }
8328     }
8329
8330     if (MovDelay[x][y])         // wait some time before next movement
8331     {
8332       MovDelay[x][y]--;
8333
8334       if (element == EL_ROBOT ||
8335           element == EL_YAMYAM ||
8336           element == EL_DARK_YAMYAM)
8337       {
8338         DrawLevelElementAnimationIfNeeded(x, y, element);
8339         PlayLevelSoundAction(x, y, ACTION_WAITING);
8340       }
8341       else if (element == EL_SP_ELECTRON)
8342         DrawLevelElementAnimationIfNeeded(x, y, element);
8343       else if (element == EL_DRAGON)
8344       {
8345         int i;
8346         int dir = MovDir[x][y];
8347         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8348         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8349         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8350                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8351                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8352                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8353         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8354
8355         GfxAction[x][y] = ACTION_ATTACKING;
8356
8357         if (IS_PLAYER(x, y))
8358           DrawPlayerField(x, y);
8359         else
8360           TEST_DrawLevelField(x, y);
8361
8362         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8363
8364         for (i = 1; i <= 3; i++)
8365         {
8366           int xx = x + i * dx;
8367           int yy = y + i * dy;
8368           int sx = SCREENX(xx);
8369           int sy = SCREENY(yy);
8370           int flame_graphic = graphic + (i - 1);
8371
8372           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8373             break;
8374
8375           if (MovDelay[x][y])
8376           {
8377             int flamed = MovingOrBlocked2Element(xx, yy);
8378
8379             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8380               Bang(xx, yy);
8381             else
8382               RemoveMovingField(xx, yy);
8383
8384             ChangeDelay[xx][yy] = 0;
8385
8386             Tile[xx][yy] = EL_FLAMES;
8387
8388             if (IN_SCR_FIELD(sx, sy))
8389             {
8390               TEST_DrawLevelFieldCrumbled(xx, yy);
8391               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8392             }
8393           }
8394           else
8395           {
8396             if (Tile[xx][yy] == EL_FLAMES)
8397               Tile[xx][yy] = EL_EMPTY;
8398             TEST_DrawLevelField(xx, yy);
8399           }
8400         }
8401       }
8402
8403       if (MovDelay[x][y])       // element still has to wait some time
8404       {
8405         PlayLevelSoundAction(x, y, ACTION_WAITING);
8406
8407         return;
8408       }
8409     }
8410
8411     // now make next step
8412
8413     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8414
8415     if (DONT_COLLIDE_WITH(element) &&
8416         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8417         !PLAYER_ENEMY_PROTECTED(newx, newy))
8418     {
8419       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8420
8421       return;
8422     }
8423
8424     else if (CAN_MOVE_INTO_ACID(element) &&
8425              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8426              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8427              (MovDir[x][y] == MV_DOWN ||
8428               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8429     {
8430       SplashAcid(newx, newy);
8431       Store[x][y] = EL_ACID;
8432     }
8433     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8434     {
8435       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8436           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8437           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8438           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8439       {
8440         RemoveField(x, y);
8441         TEST_DrawLevelField(x, y);
8442
8443         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8444         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8445           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8446
8447         game.friends_still_needed--;
8448         if (!game.friends_still_needed &&
8449             !game.GameOver &&
8450             game.all_players_gone)
8451           LevelSolved();
8452
8453         return;
8454       }
8455       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8456       {
8457         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8458           TEST_DrawLevelField(newx, newy);
8459         else
8460           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8461       }
8462       else if (!IS_FREE(newx, newy))
8463       {
8464         GfxAction[x][y] = ACTION_WAITING;
8465
8466         if (IS_PLAYER(x, y))
8467           DrawPlayerField(x, y);
8468         else
8469           TEST_DrawLevelField(x, y);
8470
8471         return;
8472       }
8473     }
8474     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8475     {
8476       if (IS_FOOD_PIG(Tile[newx][newy]))
8477       {
8478         if (IS_MOVING(newx, newy))
8479           RemoveMovingField(newx, newy);
8480         else
8481         {
8482           Tile[newx][newy] = EL_EMPTY;
8483           TEST_DrawLevelField(newx, newy);
8484         }
8485
8486         PlayLevelSound(x, y, SND_PIG_DIGGING);
8487       }
8488       else if (!IS_FREE(newx, newy))
8489       {
8490         if (IS_PLAYER(x, y))
8491           DrawPlayerField(x, y);
8492         else
8493           TEST_DrawLevelField(x, y);
8494
8495         return;
8496       }
8497     }
8498     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8499     {
8500       if (Store[x][y] != EL_EMPTY)
8501       {
8502         boolean can_clone = FALSE;
8503         int xx, yy;
8504
8505         // check if element to clone is still there
8506         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8507         {
8508           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8509           {
8510             can_clone = TRUE;
8511
8512             break;
8513           }
8514         }
8515
8516         // cannot clone or target field not free anymore -- do not clone
8517         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8518           Store[x][y] = EL_EMPTY;
8519       }
8520
8521       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8522       {
8523         if (IS_MV_DIAGONAL(MovDir[x][y]))
8524         {
8525           int diagonal_move_dir = MovDir[x][y];
8526           int stored = Store[x][y];
8527           int change_delay = 8;
8528           int graphic;
8529
8530           // android is moving diagonally
8531
8532           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8533
8534           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8535           GfxElement[x][y] = EL_EMC_ANDROID;
8536           GfxAction[x][y] = ACTION_SHRINKING;
8537           GfxDir[x][y] = diagonal_move_dir;
8538           ChangeDelay[x][y] = change_delay;
8539
8540           if (Store[x][y] == EL_EMPTY)
8541             Store[x][y] = GfxElementEmpty[x][y];
8542
8543           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8544                                    GfxDir[x][y]);
8545
8546           DrawLevelGraphicAnimation(x, y, graphic);
8547           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8548
8549           if (Tile[newx][newy] == EL_ACID)
8550           {
8551             SplashAcid(newx, newy);
8552
8553             return;
8554           }
8555
8556           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8557
8558           Store[newx][newy] = EL_EMC_ANDROID;
8559           GfxElement[newx][newy] = EL_EMC_ANDROID;
8560           GfxAction[newx][newy] = ACTION_GROWING;
8561           GfxDir[newx][newy] = diagonal_move_dir;
8562           ChangeDelay[newx][newy] = change_delay;
8563
8564           graphic = el_act_dir2img(GfxElement[newx][newy],
8565                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8566
8567           DrawLevelGraphicAnimation(newx, newy, graphic);
8568           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8569
8570           return;
8571         }
8572         else
8573         {
8574           Tile[newx][newy] = EL_EMPTY;
8575           TEST_DrawLevelField(newx, newy);
8576
8577           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8578         }
8579       }
8580       else if (!IS_FREE(newx, newy))
8581       {
8582         return;
8583       }
8584     }
8585     else if (IS_CUSTOM_ELEMENT(element) &&
8586              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8587     {
8588       if (!DigFieldByCE(newx, newy, element))
8589         return;
8590
8591       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8592       {
8593         RunnerVisit[x][y] = FrameCounter;
8594         PlayerVisit[x][y] /= 8;         // expire player visit path
8595       }
8596     }
8597     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8598     {
8599       if (!IS_FREE(newx, newy))
8600       {
8601         if (IS_PLAYER(x, y))
8602           DrawPlayerField(x, y);
8603         else
8604           TEST_DrawLevelField(x, y);
8605
8606         return;
8607       }
8608       else
8609       {
8610         boolean wanna_flame = !RND(10);
8611         int dx = newx - x, dy = newy - y;
8612         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8613         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8614         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8615                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8616         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8617                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8618
8619         if ((wanna_flame ||
8620              IS_CLASSIC_ENEMY(element1) ||
8621              IS_CLASSIC_ENEMY(element2)) &&
8622             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8623             element1 != EL_FLAMES && element2 != EL_FLAMES)
8624         {
8625           ResetGfxAnimation(x, y);
8626           GfxAction[x][y] = ACTION_ATTACKING;
8627
8628           if (IS_PLAYER(x, y))
8629             DrawPlayerField(x, y);
8630           else
8631             TEST_DrawLevelField(x, y);
8632
8633           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8634
8635           MovDelay[x][y] = 50;
8636
8637           Tile[newx][newy] = EL_FLAMES;
8638           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8639             Tile[newx1][newy1] = EL_FLAMES;
8640           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8641             Tile[newx2][newy2] = EL_FLAMES;
8642
8643           return;
8644         }
8645       }
8646     }
8647     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8648              Tile[newx][newy] == EL_DIAMOND)
8649     {
8650       if (IS_MOVING(newx, newy))
8651         RemoveMovingField(newx, newy);
8652       else
8653       {
8654         Tile[newx][newy] = EL_EMPTY;
8655         TEST_DrawLevelField(newx, newy);
8656       }
8657
8658       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8659     }
8660     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8661              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8662     {
8663       if (AmoebaNr[newx][newy])
8664       {
8665         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8666         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8667             Tile[newx][newy] == EL_BD_AMOEBA)
8668           AmoebaCnt[AmoebaNr[newx][newy]]--;
8669       }
8670
8671       if (IS_MOVING(newx, newy))
8672       {
8673         RemoveMovingField(newx, newy);
8674       }
8675       else
8676       {
8677         Tile[newx][newy] = EL_EMPTY;
8678         TEST_DrawLevelField(newx, newy);
8679       }
8680
8681       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8682     }
8683     else if ((element == EL_PACMAN || element == EL_MOLE)
8684              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8685     {
8686       if (AmoebaNr[newx][newy])
8687       {
8688         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8689         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8690             Tile[newx][newy] == EL_BD_AMOEBA)
8691           AmoebaCnt[AmoebaNr[newx][newy]]--;
8692       }
8693
8694       if (element == EL_MOLE)
8695       {
8696         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8697         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8698
8699         ResetGfxAnimation(x, y);
8700         GfxAction[x][y] = ACTION_DIGGING;
8701         TEST_DrawLevelField(x, y);
8702
8703         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8704
8705         return;                         // wait for shrinking amoeba
8706       }
8707       else      // element == EL_PACMAN
8708       {
8709         Tile[newx][newy] = EL_EMPTY;
8710         TEST_DrawLevelField(newx, newy);
8711         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8712       }
8713     }
8714     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8715              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8716               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8717     {
8718       // wait for shrinking amoeba to completely disappear
8719       return;
8720     }
8721     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8722     {
8723       // object was running against a wall
8724
8725       TurnRound(x, y);
8726
8727       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8728         DrawLevelElementAnimation(x, y, element);
8729
8730       if (DONT_TOUCH(element))
8731         TestIfBadThingTouchesPlayer(x, y);
8732
8733       return;
8734     }
8735
8736     InitMovingField(x, y, MovDir[x][y]);
8737
8738     PlayLevelSoundAction(x, y, ACTION_MOVING);
8739   }
8740
8741   if (MovDir[x][y])
8742     ContinueMoving(x, y);
8743 }
8744
8745 void ContinueMoving(int x, int y)
8746 {
8747   int element = Tile[x][y];
8748   struct ElementInfo *ei = &element_info[element];
8749   int direction = MovDir[x][y];
8750   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8751   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8752   int newx = x + dx, newy = y + dy;
8753   int stored = Store[x][y];
8754   int stored_new = Store[newx][newy];
8755   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8756   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8757   boolean last_line = (newy == lev_fieldy - 1);
8758   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8759
8760   if (pushed_by_player)         // special case: moving object pushed by player
8761   {
8762     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8763   }
8764   else if (use_step_delay)      // special case: moving object has step delay
8765   {
8766     if (!MovDelay[x][y])
8767       MovPos[x][y] += getElementMoveStepsize(x, y);
8768
8769     if (MovDelay[x][y])
8770       MovDelay[x][y]--;
8771     else
8772       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8773
8774     if (MovDelay[x][y])
8775     {
8776       TEST_DrawLevelField(x, y);
8777
8778       return;   // element is still waiting
8779     }
8780   }
8781   else                          // normal case: generically moving object
8782   {
8783     MovPos[x][y] += getElementMoveStepsize(x, y);
8784   }
8785
8786   if (ABS(MovPos[x][y]) < TILEX)
8787   {
8788     TEST_DrawLevelField(x, y);
8789
8790     return;     // element is still moving
8791   }
8792
8793   // element reached destination field
8794
8795   Tile[x][y] = EL_EMPTY;
8796   Tile[newx][newy] = element;
8797   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8798
8799   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8800   {
8801     element = Tile[newx][newy] = EL_ACID;
8802   }
8803   else if (element == EL_MOLE)
8804   {
8805     Tile[x][y] = EL_SAND;
8806
8807     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8808   }
8809   else if (element == EL_QUICKSAND_FILLING)
8810   {
8811     element = Tile[newx][newy] = get_next_element(element);
8812     Store[newx][newy] = Store[x][y];
8813   }
8814   else if (element == EL_QUICKSAND_EMPTYING)
8815   {
8816     Tile[x][y] = get_next_element(element);
8817     element = Tile[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_QUICKSAND_FAST_FILLING)
8820   {
8821     element = Tile[newx][newy] = get_next_element(element);
8822     Store[newx][newy] = Store[x][y];
8823   }
8824   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8825   {
8826     Tile[x][y] = get_next_element(element);
8827     element = Tile[newx][newy] = Store[x][y];
8828   }
8829   else if (element == EL_MAGIC_WALL_FILLING)
8830   {
8831     element = Tile[newx][newy] = get_next_element(element);
8832     if (!game.magic_wall_active)
8833       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8834     Store[newx][newy] = Store[x][y];
8835   }
8836   else if (element == EL_MAGIC_WALL_EMPTYING)
8837   {
8838     Tile[x][y] = get_next_element(element);
8839     if (!game.magic_wall_active)
8840       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8841     element = Tile[newx][newy] = Store[x][y];
8842
8843     InitField(newx, newy, FALSE);
8844   }
8845   else if (element == EL_BD_MAGIC_WALL_FILLING)
8846   {
8847     element = Tile[newx][newy] = get_next_element(element);
8848     if (!game.magic_wall_active)
8849       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8850     Store[newx][newy] = Store[x][y];
8851   }
8852   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8853   {
8854     Tile[x][y] = get_next_element(element);
8855     if (!game.magic_wall_active)
8856       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8857     element = Tile[newx][newy] = Store[x][y];
8858
8859     InitField(newx, newy, FALSE);
8860   }
8861   else if (element == EL_DC_MAGIC_WALL_FILLING)
8862   {
8863     element = Tile[newx][newy] = get_next_element(element);
8864     if (!game.magic_wall_active)
8865       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8866     Store[newx][newy] = Store[x][y];
8867   }
8868   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8869   {
8870     Tile[x][y] = get_next_element(element);
8871     if (!game.magic_wall_active)
8872       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8873     element = Tile[newx][newy] = Store[x][y];
8874
8875     InitField(newx, newy, FALSE);
8876   }
8877   else if (element == EL_AMOEBA_DROPPING)
8878   {
8879     Tile[x][y] = get_next_element(element);
8880     element = Tile[newx][newy] = Store[x][y];
8881   }
8882   else if (element == EL_SOKOBAN_OBJECT)
8883   {
8884     if (Back[x][y])
8885       Tile[x][y] = Back[x][y];
8886
8887     if (Back[newx][newy])
8888       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8889
8890     Back[x][y] = Back[newx][newy] = 0;
8891   }
8892
8893   Store[x][y] = EL_EMPTY;
8894   MovPos[x][y] = 0;
8895   MovDir[x][y] = 0;
8896   MovDelay[x][y] = 0;
8897
8898   MovDelay[newx][newy] = 0;
8899
8900   if (CAN_CHANGE_OR_HAS_ACTION(element))
8901   {
8902     // copy element change control values to new field
8903     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8904     ChangePage[newx][newy]  = ChangePage[x][y];
8905     ChangeCount[newx][newy] = ChangeCount[x][y];
8906     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8907   }
8908
8909   CustomValue[newx][newy] = CustomValue[x][y];
8910
8911   ChangeDelay[x][y] = 0;
8912   ChangePage[x][y] = -1;
8913   ChangeCount[x][y] = 0;
8914   ChangeEvent[x][y] = -1;
8915
8916   CustomValue[x][y] = 0;
8917
8918   // copy animation control values to new field
8919   GfxFrame[newx][newy]  = GfxFrame[x][y];
8920   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8921   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8922   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8923
8924   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8925
8926   // some elements can leave other elements behind after moving
8927   if (ei->move_leave_element != EL_EMPTY &&
8928       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8929       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8930   {
8931     int move_leave_element = ei->move_leave_element;
8932
8933     // this makes it possible to leave the removed element again
8934     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8935       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8936
8937     Tile[x][y] = move_leave_element;
8938
8939     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8940       MovDir[x][y] = direction;
8941
8942     InitField(x, y, FALSE);
8943
8944     if (GFX_CRUMBLED(Tile[x][y]))
8945       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8946
8947     if (IS_PLAYER_ELEMENT(move_leave_element))
8948       RelocatePlayer(x, y, move_leave_element);
8949   }
8950
8951   // do this after checking for left-behind element
8952   ResetGfxAnimation(x, y);      // reset animation values for old field
8953
8954   if (!CAN_MOVE(element) ||
8955       (CAN_FALL(element) && direction == MV_DOWN &&
8956        (element == EL_SPRING ||
8957         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8958         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8959     GfxDir[x][y] = MovDir[newx][newy] = 0;
8960
8961   TEST_DrawLevelField(x, y);
8962   TEST_DrawLevelField(newx, newy);
8963
8964   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8965
8966   // prevent pushed element from moving on in pushed direction
8967   if (pushed_by_player && CAN_MOVE(element) &&
8968       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8969       !(element_info[element].move_pattern & direction))
8970     TurnRound(newx, newy);
8971
8972   // prevent elements on conveyor belt from moving on in last direction
8973   if (pushed_by_conveyor && CAN_FALL(element) &&
8974       direction & MV_HORIZONTAL)
8975     MovDir[newx][newy] = 0;
8976
8977   if (!pushed_by_player)
8978   {
8979     int nextx = newx + dx, nexty = newy + dy;
8980     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8981
8982     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8983
8984     if (CAN_FALL(element) && direction == MV_DOWN)
8985       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8986
8987     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8988       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8989
8990     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8991       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8992   }
8993
8994   if (DONT_TOUCH(element))      // object may be nasty to player or others
8995   {
8996     TestIfBadThingTouchesPlayer(newx, newy);
8997     TestIfBadThingTouchesFriend(newx, newy);
8998
8999     if (!IS_CUSTOM_ELEMENT(element))
9000       TestIfBadThingTouchesOtherBadThing(newx, newy);
9001   }
9002   else if (element == EL_PENGUIN)
9003     TestIfFriendTouchesBadThing(newx, newy);
9004
9005   if (DONT_GET_HIT_BY(element))
9006   {
9007     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9008   }
9009
9010   // give the player one last chance (one more frame) to move away
9011   if (CAN_FALL(element) && direction == MV_DOWN &&
9012       (last_line || (!IS_FREE(x, newy + 1) &&
9013                      (!IS_PLAYER(x, newy + 1) ||
9014                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9015     Impact(x, newy);
9016
9017   if (pushed_by_player && !game.use_change_when_pushing_bug)
9018   {
9019     int push_side = MV_DIR_OPPOSITE(direction);
9020     struct PlayerInfo *player = PLAYERINFO(x, y);
9021
9022     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9023                                player->index_bit, push_side);
9024     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9025                                         player->index_bit, push_side);
9026   }
9027
9028   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9029     MovDelay[newx][newy] = 1;
9030
9031   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9032
9033   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9034   TestIfElementHitsCustomElement(newx, newy, direction);
9035   TestIfPlayerTouchesCustomElement(newx, newy);
9036   TestIfElementTouchesCustomElement(newx, newy);
9037
9038   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9039       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9040     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9041                              MV_DIR_OPPOSITE(direction));
9042 }
9043
9044 int AmoebaNeighbourNr(int ax, int ay)
9045 {
9046   int i;
9047   int element = Tile[ax][ay];
9048   int group_nr = 0;
9049   struct XY *xy = xy_topdown;
9050
9051   for (i = 0; i < NUM_DIRECTIONS; i++)
9052   {
9053     int x = ax + xy[i].x;
9054     int y = ay + xy[i].y;
9055
9056     if (!IN_LEV_FIELD(x, y))
9057       continue;
9058
9059     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9060       group_nr = AmoebaNr[x][y];
9061   }
9062
9063   return group_nr;
9064 }
9065
9066 static void AmoebaMerge(int ax, int ay)
9067 {
9068   int i, x, y, xx, yy;
9069   int new_group_nr = AmoebaNr[ax][ay];
9070   struct XY *xy = xy_topdown;
9071
9072   if (new_group_nr == 0)
9073     return;
9074
9075   for (i = 0; i < NUM_DIRECTIONS; i++)
9076   {
9077     x = ax + xy[i].x;
9078     y = ay + xy[i].y;
9079
9080     if (!IN_LEV_FIELD(x, y))
9081       continue;
9082
9083     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9084          Tile[x][y] == EL_BD_AMOEBA ||
9085          Tile[x][y] == EL_AMOEBA_DEAD) &&
9086         AmoebaNr[x][y] != new_group_nr)
9087     {
9088       int old_group_nr = AmoebaNr[x][y];
9089
9090       if (old_group_nr == 0)
9091         return;
9092
9093       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9094       AmoebaCnt[old_group_nr] = 0;
9095       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9096       AmoebaCnt2[old_group_nr] = 0;
9097
9098       SCAN_PLAYFIELD(xx, yy)
9099       {
9100         if (AmoebaNr[xx][yy] == old_group_nr)
9101           AmoebaNr[xx][yy] = new_group_nr;
9102       }
9103     }
9104   }
9105 }
9106
9107 void AmoebaToDiamond(int ax, int ay)
9108 {
9109   int i, x, y;
9110
9111   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9112   {
9113     int group_nr = AmoebaNr[ax][ay];
9114
9115 #ifdef DEBUG
9116     if (group_nr == 0)
9117     {
9118       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9119       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9120
9121       return;
9122     }
9123 #endif
9124
9125     SCAN_PLAYFIELD(x, y)
9126     {
9127       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9128       {
9129         AmoebaNr[x][y] = 0;
9130         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9131       }
9132     }
9133
9134     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9135                             SND_AMOEBA_TURNING_TO_GEM :
9136                             SND_AMOEBA_TURNING_TO_ROCK));
9137     Bang(ax, ay);
9138   }
9139   else
9140   {
9141     struct XY *xy = xy_topdown;
9142
9143     for (i = 0; i < NUM_DIRECTIONS; i++)
9144     {
9145       x = ax + xy[i].x;
9146       y = ay + xy[i].y;
9147
9148       if (!IN_LEV_FIELD(x, y))
9149         continue;
9150
9151       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9152       {
9153         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9154                               SND_AMOEBA_TURNING_TO_GEM :
9155                               SND_AMOEBA_TURNING_TO_ROCK));
9156         Bang(x, y);
9157       }
9158     }
9159   }
9160 }
9161
9162 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9163 {
9164   int x, y;
9165   int group_nr = AmoebaNr[ax][ay];
9166   boolean done = FALSE;
9167
9168 #ifdef DEBUG
9169   if (group_nr == 0)
9170   {
9171     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9172     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9173
9174     return;
9175   }
9176 #endif
9177
9178   SCAN_PLAYFIELD(x, y)
9179   {
9180     if (AmoebaNr[x][y] == group_nr &&
9181         (Tile[x][y] == EL_AMOEBA_DEAD ||
9182          Tile[x][y] == EL_BD_AMOEBA ||
9183          Tile[x][y] == EL_AMOEBA_GROWING))
9184     {
9185       AmoebaNr[x][y] = 0;
9186       Tile[x][y] = new_element;
9187       InitField(x, y, FALSE);
9188       TEST_DrawLevelField(x, y);
9189       done = TRUE;
9190     }
9191   }
9192
9193   if (done)
9194     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9195                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9196                             SND_BD_AMOEBA_TURNING_TO_GEM));
9197 }
9198
9199 static void AmoebaGrowing(int x, int y)
9200 {
9201   static DelayCounter sound_delay = { 0 };
9202
9203   if (!MovDelay[x][y])          // start new growing cycle
9204   {
9205     MovDelay[x][y] = 7;
9206
9207     if (DelayReached(&sound_delay))
9208     {
9209       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9210       sound_delay.value = 30;
9211     }
9212   }
9213
9214   if (MovDelay[x][y])           // wait some time before growing bigger
9215   {
9216     MovDelay[x][y]--;
9217     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9218     {
9219       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9220                                            6 - MovDelay[x][y]);
9221
9222       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9223     }
9224
9225     if (!MovDelay[x][y])
9226     {
9227       Tile[x][y] = Store[x][y];
9228       Store[x][y] = 0;
9229       TEST_DrawLevelField(x, y);
9230     }
9231   }
9232 }
9233
9234 static void AmoebaShrinking(int x, int y)
9235 {
9236   static DelayCounter sound_delay = { 0 };
9237
9238   if (!MovDelay[x][y])          // start new shrinking cycle
9239   {
9240     MovDelay[x][y] = 7;
9241
9242     if (DelayReached(&sound_delay))
9243       sound_delay.value = 30;
9244   }
9245
9246   if (MovDelay[x][y])           // wait some time before shrinking
9247   {
9248     MovDelay[x][y]--;
9249     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9250     {
9251       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9252                                            6 - MovDelay[x][y]);
9253
9254       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9255     }
9256
9257     if (!MovDelay[x][y])
9258     {
9259       Tile[x][y] = EL_EMPTY;
9260       TEST_DrawLevelField(x, y);
9261
9262       // don't let mole enter this field in this cycle;
9263       // (give priority to objects falling to this field from above)
9264       Stop[x][y] = TRUE;
9265     }
9266   }
9267 }
9268
9269 static void AmoebaReproduce(int ax, int ay)
9270 {
9271   int i;
9272   int element = Tile[ax][ay];
9273   int graphic = el2img(element);
9274   int newax = ax, neway = ay;
9275   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9276   struct XY *xy = xy_topdown;
9277
9278   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9279   {
9280     Tile[ax][ay] = EL_AMOEBA_DEAD;
9281     TEST_DrawLevelField(ax, ay);
9282     return;
9283   }
9284
9285   if (IS_ANIMATED(graphic))
9286     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9287
9288   if (!MovDelay[ax][ay])        // start making new amoeba field
9289     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9290
9291   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9292   {
9293     MovDelay[ax][ay]--;
9294     if (MovDelay[ax][ay])
9295       return;
9296   }
9297
9298   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9299   {
9300     int start = RND(4);
9301     int x = ax + xy[start].x;
9302     int y = ay + xy[start].y;
9303
9304     if (!IN_LEV_FIELD(x, y))
9305       return;
9306
9307     if (IS_FREE(x, y) ||
9308         CAN_GROW_INTO(Tile[x][y]) ||
9309         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9310         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9311     {
9312       newax = x;
9313       neway = y;
9314     }
9315
9316     if (newax == ax && neway == ay)
9317       return;
9318   }
9319   else                          // normal or "filled" (BD style) amoeba
9320   {
9321     int start = RND(4);
9322     boolean waiting_for_player = FALSE;
9323
9324     for (i = 0; i < NUM_DIRECTIONS; i++)
9325     {
9326       int j = (start + i) % 4;
9327       int x = ax + xy[j].x;
9328       int y = ay + xy[j].y;
9329
9330       if (!IN_LEV_FIELD(x, y))
9331         continue;
9332
9333       if (IS_FREE(x, y) ||
9334           CAN_GROW_INTO(Tile[x][y]) ||
9335           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9336           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9337       {
9338         newax = x;
9339         neway = y;
9340         break;
9341       }
9342       else if (IS_PLAYER(x, y))
9343         waiting_for_player = TRUE;
9344     }
9345
9346     if (newax == ax && neway == ay)             // amoeba cannot grow
9347     {
9348       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9349       {
9350         Tile[ax][ay] = EL_AMOEBA_DEAD;
9351         TEST_DrawLevelField(ax, ay);
9352         AmoebaCnt[AmoebaNr[ax][ay]]--;
9353
9354         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9355         {
9356           if (element == EL_AMOEBA_FULL)
9357             AmoebaToDiamond(ax, ay);
9358           else if (element == EL_BD_AMOEBA)
9359             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9360         }
9361       }
9362       return;
9363     }
9364     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9365     {
9366       // amoeba gets larger by growing in some direction
9367
9368       int new_group_nr = AmoebaNr[ax][ay];
9369
9370 #ifdef DEBUG
9371   if (new_group_nr == 0)
9372   {
9373     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9374           newax, neway);
9375     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9376
9377     return;
9378   }
9379 #endif
9380
9381       AmoebaNr[newax][neway] = new_group_nr;
9382       AmoebaCnt[new_group_nr]++;
9383       AmoebaCnt2[new_group_nr]++;
9384
9385       // if amoeba touches other amoeba(s) after growing, unify them
9386       AmoebaMerge(newax, neway);
9387
9388       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9389       {
9390         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9391         return;
9392       }
9393     }
9394   }
9395
9396   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9397       (neway == lev_fieldy - 1 && newax != ax))
9398   {
9399     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9400     Store[newax][neway] = element;
9401   }
9402   else if (neway == ay || element == EL_EMC_DRIPPER)
9403   {
9404     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9405
9406     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9407   }
9408   else
9409   {
9410     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9411     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9412     Store[ax][ay] = EL_AMOEBA_DROP;
9413     ContinueMoving(ax, ay);
9414     return;
9415   }
9416
9417   TEST_DrawLevelField(newax, neway);
9418 }
9419
9420 static void Life(int ax, int ay)
9421 {
9422   int x1, y1, x2, y2;
9423   int life_time = 40;
9424   int element = Tile[ax][ay];
9425   int graphic = el2img(element);
9426   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9427                          level.biomaze);
9428   boolean changed = FALSE;
9429
9430   if (IS_ANIMATED(graphic))
9431     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9432
9433   if (Stop[ax][ay])
9434     return;
9435
9436   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9437     MovDelay[ax][ay] = life_time;
9438
9439   if (MovDelay[ax][ay])         // wait some time before next cycle
9440   {
9441     MovDelay[ax][ay]--;
9442     if (MovDelay[ax][ay])
9443       return;
9444   }
9445
9446   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9447   {
9448     int xx = ax+x1, yy = ay+y1;
9449     int old_element = Tile[xx][yy];
9450     int num_neighbours = 0;
9451
9452     if (!IN_LEV_FIELD(xx, yy))
9453       continue;
9454
9455     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9456     {
9457       int x = xx+x2, y = yy+y2;
9458
9459       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9460         continue;
9461
9462       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9463       boolean is_neighbour = FALSE;
9464
9465       if (level.use_life_bugs)
9466         is_neighbour =
9467           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9468            (IS_FREE(x, y)                             &&  Stop[x][y]));
9469       else
9470         is_neighbour =
9471           (Last[x][y] == element || is_player_cell);
9472
9473       if (is_neighbour)
9474         num_neighbours++;
9475     }
9476
9477     boolean is_free = FALSE;
9478
9479     if (level.use_life_bugs)
9480       is_free = (IS_FREE(xx, yy));
9481     else
9482       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9483
9484     if (xx == ax && yy == ay)           // field in the middle
9485     {
9486       if (num_neighbours < life_parameter[0] ||
9487           num_neighbours > life_parameter[1])
9488       {
9489         Tile[xx][yy] = EL_EMPTY;
9490         if (Tile[xx][yy] != old_element)
9491           TEST_DrawLevelField(xx, yy);
9492         Stop[xx][yy] = TRUE;
9493         changed = TRUE;
9494       }
9495     }
9496     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9497     {                                   // free border field
9498       if (num_neighbours >= life_parameter[2] &&
9499           num_neighbours <= life_parameter[3])
9500       {
9501         Tile[xx][yy] = element;
9502         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9503         if (Tile[xx][yy] != old_element)
9504           TEST_DrawLevelField(xx, yy);
9505         Stop[xx][yy] = TRUE;
9506         changed = TRUE;
9507       }
9508     }
9509   }
9510
9511   if (changed)
9512     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9513                    SND_GAME_OF_LIFE_GROWING);
9514 }
9515
9516 static void InitRobotWheel(int x, int y)
9517 {
9518   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9519 }
9520
9521 static void RunRobotWheel(int x, int y)
9522 {
9523   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9524 }
9525
9526 static void StopRobotWheel(int x, int y)
9527 {
9528   if (game.robot_wheel_x == x &&
9529       game.robot_wheel_y == y)
9530   {
9531     game.robot_wheel_x = -1;
9532     game.robot_wheel_y = -1;
9533     game.robot_wheel_active = FALSE;
9534   }
9535 }
9536
9537 static void InitTimegateWheel(int x, int y)
9538 {
9539   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9540 }
9541
9542 static void RunTimegateWheel(int x, int y)
9543 {
9544   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9545 }
9546
9547 static void InitMagicBallDelay(int x, int y)
9548 {
9549   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9550 }
9551
9552 static void ActivateMagicBall(int bx, int by)
9553 {
9554   int x, y;
9555
9556   if (level.ball_random)
9557   {
9558     int pos_border = RND(8);    // select one of the eight border elements
9559     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9560     int xx = pos_content % 3;
9561     int yy = pos_content / 3;
9562
9563     x = bx - 1 + xx;
9564     y = by - 1 + yy;
9565
9566     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9567       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9568   }
9569   else
9570   {
9571     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9572     {
9573       int xx = x - bx + 1;
9574       int yy = y - by + 1;
9575
9576       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9577         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9578     }
9579   }
9580
9581   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9582 }
9583
9584 static void CheckExit(int x, int y)
9585 {
9586   if (game.gems_still_needed > 0 ||
9587       game.sokoban_fields_still_needed > 0 ||
9588       game.sokoban_objects_still_needed > 0 ||
9589       game.lights_still_needed > 0)
9590   {
9591     int element = Tile[x][y];
9592     int graphic = el2img(element);
9593
9594     if (IS_ANIMATED(graphic))
9595       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9596
9597     return;
9598   }
9599
9600   // do not re-open exit door closed after last player
9601   if (game.all_players_gone)
9602     return;
9603
9604   Tile[x][y] = EL_EXIT_OPENING;
9605
9606   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9607 }
9608
9609 static void CheckExitEM(int x, int y)
9610 {
9611   if (game.gems_still_needed > 0 ||
9612       game.sokoban_fields_still_needed > 0 ||
9613       game.sokoban_objects_still_needed > 0 ||
9614       game.lights_still_needed > 0)
9615   {
9616     int element = Tile[x][y];
9617     int graphic = el2img(element);
9618
9619     if (IS_ANIMATED(graphic))
9620       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9621
9622     return;
9623   }
9624
9625   // do not re-open exit door closed after last player
9626   if (game.all_players_gone)
9627     return;
9628
9629   Tile[x][y] = EL_EM_EXIT_OPENING;
9630
9631   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9632 }
9633
9634 static void CheckExitSteel(int x, int y)
9635 {
9636   if (game.gems_still_needed > 0 ||
9637       game.sokoban_fields_still_needed > 0 ||
9638       game.sokoban_objects_still_needed > 0 ||
9639       game.lights_still_needed > 0)
9640   {
9641     int element = Tile[x][y];
9642     int graphic = el2img(element);
9643
9644     if (IS_ANIMATED(graphic))
9645       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9646
9647     return;
9648   }
9649
9650   // do not re-open exit door closed after last player
9651   if (game.all_players_gone)
9652     return;
9653
9654   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9655
9656   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9657 }
9658
9659 static void CheckExitSteelEM(int x, int y)
9660 {
9661   if (game.gems_still_needed > 0 ||
9662       game.sokoban_fields_still_needed > 0 ||
9663       game.sokoban_objects_still_needed > 0 ||
9664       game.lights_still_needed > 0)
9665   {
9666     int element = Tile[x][y];
9667     int graphic = el2img(element);
9668
9669     if (IS_ANIMATED(graphic))
9670       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9671
9672     return;
9673   }
9674
9675   // do not re-open exit door closed after last player
9676   if (game.all_players_gone)
9677     return;
9678
9679   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9680
9681   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9682 }
9683
9684 static void CheckExitSP(int x, int y)
9685 {
9686   if (game.gems_still_needed > 0)
9687   {
9688     int element = Tile[x][y];
9689     int graphic = el2img(element);
9690
9691     if (IS_ANIMATED(graphic))
9692       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9693
9694     return;
9695   }
9696
9697   // do not re-open exit door closed after last player
9698   if (game.all_players_gone)
9699     return;
9700
9701   Tile[x][y] = EL_SP_EXIT_OPENING;
9702
9703   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9704 }
9705
9706 static void CloseAllOpenTimegates(void)
9707 {
9708   int x, y;
9709
9710   SCAN_PLAYFIELD(x, y)
9711   {
9712     int element = Tile[x][y];
9713
9714     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9715     {
9716       Tile[x][y] = EL_TIMEGATE_CLOSING;
9717
9718       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9719     }
9720   }
9721 }
9722
9723 static void DrawTwinkleOnField(int x, int y)
9724 {
9725   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9726     return;
9727
9728   if (Tile[x][y] == EL_BD_DIAMOND)
9729     return;
9730
9731   if (MovDelay[x][y] == 0)      // next animation frame
9732     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9733
9734   if (MovDelay[x][y] != 0)      // wait some time before next frame
9735   {
9736     MovDelay[x][y]--;
9737
9738     DrawLevelElementAnimation(x, y, Tile[x][y]);
9739
9740     if (MovDelay[x][y] != 0)
9741     {
9742       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9743                                            10 - MovDelay[x][y]);
9744
9745       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9746     }
9747   }
9748 }
9749
9750 static void WallGrowing(int x, int y)
9751 {
9752   int delay = 6;
9753
9754   if (!MovDelay[x][y])          // next animation frame
9755     MovDelay[x][y] = 3 * delay;
9756
9757   if (MovDelay[x][y])           // wait some time before next frame
9758   {
9759     MovDelay[x][y]--;
9760
9761     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9762     {
9763       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9764       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9765
9766       DrawLevelGraphic(x, y, graphic, frame);
9767     }
9768
9769     if (!MovDelay[x][y])
9770     {
9771       if (MovDir[x][y] == MV_LEFT)
9772       {
9773         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9774           TEST_DrawLevelField(x - 1, y);
9775       }
9776       else if (MovDir[x][y] == MV_RIGHT)
9777       {
9778         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9779           TEST_DrawLevelField(x + 1, y);
9780       }
9781       else if (MovDir[x][y] == MV_UP)
9782       {
9783         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9784           TEST_DrawLevelField(x, y - 1);
9785       }
9786       else
9787       {
9788         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9789           TEST_DrawLevelField(x, y + 1);
9790       }
9791
9792       Tile[x][y] = Store[x][y];
9793       Store[x][y] = 0;
9794       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9795       TEST_DrawLevelField(x, y);
9796     }
9797   }
9798 }
9799
9800 static void CheckWallGrowing(int ax, int ay)
9801 {
9802   int element = Tile[ax][ay];
9803   int graphic = el2img(element);
9804   boolean free_top    = FALSE;
9805   boolean free_bottom = FALSE;
9806   boolean free_left   = FALSE;
9807   boolean free_right  = FALSE;
9808   boolean stop_top    = FALSE;
9809   boolean stop_bottom = FALSE;
9810   boolean stop_left   = FALSE;
9811   boolean stop_right  = FALSE;
9812   boolean new_wall    = FALSE;
9813
9814   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9815                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9816                            element == EL_EXPANDABLE_STEELWALL_ANY);
9817
9818   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9819                              element == EL_EXPANDABLE_WALL_ANY ||
9820                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9821                              element == EL_EXPANDABLE_STEELWALL_ANY);
9822
9823   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9824                              element == EL_EXPANDABLE_WALL_ANY ||
9825                              element == EL_EXPANDABLE_WALL ||
9826                              element == EL_BD_EXPANDABLE_WALL ||
9827                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9828                              element == EL_EXPANDABLE_STEELWALL_ANY);
9829
9830   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9831                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9832
9833   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9834                              element == EL_EXPANDABLE_WALL ||
9835                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9836
9837   int wall_growing = (is_steelwall ?
9838                       EL_EXPANDABLE_STEELWALL_GROWING :
9839                       EL_EXPANDABLE_WALL_GROWING);
9840
9841   int gfx_wall_growing_up    = (is_steelwall ?
9842                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9843                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9844   int gfx_wall_growing_down  = (is_steelwall ?
9845                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9846                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9847   int gfx_wall_growing_left  = (is_steelwall ?
9848                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9849                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9850   int gfx_wall_growing_right = (is_steelwall ?
9851                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9852                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9853
9854   if (IS_ANIMATED(graphic))
9855     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9856
9857   if (!MovDelay[ax][ay])        // start building new wall
9858     MovDelay[ax][ay] = 6;
9859
9860   if (MovDelay[ax][ay])         // wait some time before building new wall
9861   {
9862     MovDelay[ax][ay]--;
9863     if (MovDelay[ax][ay])
9864       return;
9865   }
9866
9867   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9868     free_top = TRUE;
9869   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9870     free_bottom = TRUE;
9871   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9872     free_left = TRUE;
9873   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9874     free_right = TRUE;
9875
9876   if (grow_vertical)
9877   {
9878     if (free_top)
9879     {
9880       Tile[ax][ay - 1] = wall_growing;
9881       Store[ax][ay - 1] = element;
9882       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9883
9884       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9885         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9886
9887       new_wall = TRUE;
9888     }
9889
9890     if (free_bottom)
9891     {
9892       Tile[ax][ay + 1] = wall_growing;
9893       Store[ax][ay + 1] = element;
9894       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9895
9896       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9897         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9898
9899       new_wall = TRUE;
9900     }
9901   }
9902
9903   if (grow_horizontal)
9904   {
9905     if (free_left)
9906     {
9907       Tile[ax - 1][ay] = wall_growing;
9908       Store[ax - 1][ay] = element;
9909       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9910
9911       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9912         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9913
9914       new_wall = TRUE;
9915     }
9916
9917     if (free_right)
9918     {
9919       Tile[ax + 1][ay] = wall_growing;
9920       Store[ax + 1][ay] = element;
9921       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9922
9923       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9924         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9925
9926       new_wall = TRUE;
9927     }
9928   }
9929
9930   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9931     TEST_DrawLevelField(ax, ay);
9932
9933   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9934     stop_top = TRUE;
9935   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9936     stop_bottom = TRUE;
9937   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9938     stop_left = TRUE;
9939   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9940     stop_right = TRUE;
9941
9942   if (((stop_top && stop_bottom) || stop_horizontal) &&
9943       ((stop_left && stop_right) || stop_vertical))
9944     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
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   struct XY *xy = xy_topdown;
9955
9956   for (i = 0; i < NUM_DIRECTIONS; i++)
9957   {
9958     for (j = 0; j < 4; j++)
9959     {
9960       int xx = x + j * xy[i].x;
9961       int yy = y + j * xy[i].y;
9962
9963       if (IN_LEV_FIELD(xx, yy) &&
9964           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9965       {
9966         if (Tile[xx][yy] == EL_DRAGON)
9967           dragon_found = TRUE;
9968       }
9969       else
9970         break;
9971     }
9972   }
9973
9974   if (!dragon_found)
9975   {
9976     for (i = 0; i < NUM_DIRECTIONS; i++)
9977     {
9978       for (j = 0; j < 3; j++)
9979       {
9980         int xx = x + j * xy[i].x;
9981         int yy = y + j * xy[i].y;
9982
9983         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9984         {
9985           Tile[xx][yy] = EL_EMPTY;
9986           TEST_DrawLevelField(xx, yy);
9987         }
9988         else
9989           break;
9990       }
9991     }
9992   }
9993 }
9994
9995 static void InitBuggyBase(int x, int y)
9996 {
9997   int element = Tile[x][y];
9998   int activating_delay = FRAMES_PER_SECOND / 4;
9999
10000   ChangeDelay[x][y] =
10001     (element == EL_SP_BUGGY_BASE ?
10002      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10003      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10004      activating_delay :
10005      element == EL_SP_BUGGY_BASE_ACTIVE ?
10006      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10007 }
10008
10009 static void WarnBuggyBase(int x, int y)
10010 {
10011   int i;
10012   struct XY *xy = xy_topdown;
10013
10014   for (i = 0; i < NUM_DIRECTIONS; i++)
10015   {
10016     int xx = x + xy[i].x;
10017     int yy = y + xy[i].y;
10018
10019     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10020     {
10021       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10022
10023       break;
10024     }
10025   }
10026 }
10027
10028 static void InitTrap(int x, int y)
10029 {
10030   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10031 }
10032
10033 static void ActivateTrap(int x, int y)
10034 {
10035   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10036 }
10037
10038 static void ChangeActiveTrap(int x, int y)
10039 {
10040   int graphic = IMG_TRAP_ACTIVE;
10041
10042   // if new animation frame was drawn, correct crumbled sand border
10043   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10044     TEST_DrawLevelFieldCrumbled(x, y);
10045 }
10046
10047 static int getSpecialActionElement(int element, int number, int base_element)
10048 {
10049   return (element != EL_EMPTY ? element :
10050           number != -1 ? base_element + number - 1 :
10051           EL_EMPTY);
10052 }
10053
10054 static int getModifiedActionNumber(int value_old, int operator, int operand,
10055                                    int value_min, int value_max)
10056 {
10057   int value_new = (operator == CA_MODE_SET      ? operand :
10058                    operator == CA_MODE_ADD      ? value_old + operand :
10059                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10060                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10061                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10062                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10063                    value_old);
10064
10065   return (value_new < value_min ? value_min :
10066           value_new > value_max ? value_max :
10067           value_new);
10068 }
10069
10070 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10071 {
10072   struct ElementInfo *ei = &element_info[element];
10073   struct ElementChangeInfo *change = &ei->change_page[page];
10074   int target_element = change->target_element;
10075   int action_type = change->action_type;
10076   int action_mode = change->action_mode;
10077   int action_arg = change->action_arg;
10078   int action_element = change->action_element;
10079   int i;
10080
10081   if (!change->has_action)
10082     return;
10083
10084   // ---------- determine action paramater values -----------------------------
10085
10086   int level_time_value =
10087     (level.time > 0 ? TimeLeft :
10088      TimePlayed);
10089
10090   int action_arg_element_raw =
10091     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10092      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10093      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10094      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10095      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10096      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10097      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10098      EL_EMPTY);
10099   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10100
10101   int action_arg_direction =
10102     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10103      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10104      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10105      change->actual_trigger_side :
10106      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10107      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10108      MV_NONE);
10109
10110   int action_arg_number_min =
10111     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10112      CA_ARG_MIN);
10113
10114   int action_arg_number_max =
10115     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10116      action_type == CA_SET_LEVEL_GEMS ? 999 :
10117      action_type == CA_SET_LEVEL_TIME ? 9999 :
10118      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10119      action_type == CA_SET_CE_VALUE ? 9999 :
10120      action_type == CA_SET_CE_SCORE ? 9999 :
10121      CA_ARG_MAX);
10122
10123   int action_arg_number_reset =
10124     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10125      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10126      action_type == CA_SET_LEVEL_TIME ? level.time :
10127      action_type == CA_SET_LEVEL_SCORE ? 0 :
10128      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10129      action_type == CA_SET_CE_SCORE ? 0 :
10130      0);
10131
10132   int action_arg_number =
10133     (action_arg <= CA_ARG_MAX ? action_arg :
10134      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10135      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10136      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10137      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10138      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10139      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10140      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10141      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10142      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10143      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10144      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10145      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10146      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10147      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10148      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10149      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10150      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10151      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10152      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10153      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10154      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10155      -1);
10156
10157   int action_arg_number_old =
10158     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10159      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10160      action_type == CA_SET_LEVEL_SCORE ? game.score :
10161      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10162      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10163      0);
10164
10165   int action_arg_number_new =
10166     getModifiedActionNumber(action_arg_number_old,
10167                             action_mode, action_arg_number,
10168                             action_arg_number_min, action_arg_number_max);
10169
10170   int trigger_player_bits =
10171     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10172      change->actual_trigger_player_bits : change->trigger_player);
10173
10174   int action_arg_player_bits =
10175     (action_arg >= CA_ARG_PLAYER_1 &&
10176      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10177      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10178      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10179      PLAYER_BITS_ANY);
10180
10181   // ---------- execute action  -----------------------------------------------
10182
10183   switch (action_type)
10184   {
10185     case CA_NO_ACTION:
10186     {
10187       return;
10188     }
10189
10190     // ---------- level actions  ----------------------------------------------
10191
10192     case CA_RESTART_LEVEL:
10193     {
10194       game.restart_level = TRUE;
10195
10196       break;
10197     }
10198
10199     case CA_SHOW_ENVELOPE:
10200     {
10201       int element = getSpecialActionElement(action_arg_element,
10202                                             action_arg_number, EL_ENVELOPE_1);
10203
10204       if (IS_ENVELOPE(element))
10205         local_player->show_envelope = element;
10206
10207       break;
10208     }
10209
10210     case CA_SET_LEVEL_TIME:
10211     {
10212       if (level.time > 0)       // only modify limited time value
10213       {
10214         TimeLeft = action_arg_number_new;
10215
10216         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10217
10218         DisplayGameControlValues();
10219
10220         if (!TimeLeft && game.time_limit)
10221           for (i = 0; i < MAX_PLAYERS; i++)
10222             KillPlayer(&stored_player[i]);
10223       }
10224
10225       break;
10226     }
10227
10228     case CA_SET_LEVEL_SCORE:
10229     {
10230       game.score = action_arg_number_new;
10231
10232       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10233
10234       DisplayGameControlValues();
10235
10236       break;
10237     }
10238
10239     case CA_SET_LEVEL_GEMS:
10240     {
10241       game.gems_still_needed = action_arg_number_new;
10242
10243       game.snapshot.collected_item = TRUE;
10244
10245       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10246
10247       DisplayGameControlValues();
10248
10249       break;
10250     }
10251
10252     case CA_SET_LEVEL_WIND:
10253     {
10254       game.wind_direction = action_arg_direction;
10255
10256       break;
10257     }
10258
10259     case CA_SET_LEVEL_RANDOM_SEED:
10260     {
10261       // ensure that setting a new random seed while playing is predictable
10262       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10263
10264       break;
10265     }
10266
10267     // ---------- player actions  ---------------------------------------------
10268
10269     case CA_MOVE_PLAYER:
10270     case CA_MOVE_PLAYER_NEW:
10271     {
10272       // automatically move to the next field in specified direction
10273       for (i = 0; i < MAX_PLAYERS; i++)
10274         if (trigger_player_bits & (1 << i))
10275           if (action_type == CA_MOVE_PLAYER ||
10276               stored_player[i].MovPos == 0)
10277             stored_player[i].programmed_action = action_arg_direction;
10278
10279       break;
10280     }
10281
10282     case CA_EXIT_PLAYER:
10283     {
10284       for (i = 0; i < MAX_PLAYERS; i++)
10285         if (action_arg_player_bits & (1 << i))
10286           ExitPlayer(&stored_player[i]);
10287
10288       if (game.players_still_needed == 0)
10289         LevelSolved();
10290
10291       break;
10292     }
10293
10294     case CA_KILL_PLAYER:
10295     {
10296       for (i = 0; i < MAX_PLAYERS; i++)
10297         if (action_arg_player_bits & (1 << i))
10298           KillPlayer(&stored_player[i]);
10299
10300       break;
10301     }
10302
10303     case CA_SET_PLAYER_KEYS:
10304     {
10305       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10306       int element = getSpecialActionElement(action_arg_element,
10307                                             action_arg_number, EL_KEY_1);
10308
10309       if (IS_KEY(element))
10310       {
10311         for (i = 0; i < MAX_PLAYERS; i++)
10312         {
10313           if (trigger_player_bits & (1 << i))
10314           {
10315             stored_player[i].key[KEY_NR(element)] = key_state;
10316
10317             DrawGameDoorValues();
10318           }
10319         }
10320       }
10321
10322       break;
10323     }
10324
10325     case CA_SET_PLAYER_SPEED:
10326     {
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328       {
10329         if (trigger_player_bits & (1 << i))
10330         {
10331           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10332
10333           if (action_arg == CA_ARG_SPEED_FASTER &&
10334               stored_player[i].cannot_move)
10335           {
10336             action_arg_number = STEPSIZE_VERY_SLOW;
10337           }
10338           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10339                    action_arg == CA_ARG_SPEED_FASTER)
10340           {
10341             action_arg_number = 2;
10342             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10343                            CA_MODE_MULTIPLY);
10344           }
10345           else if (action_arg == CA_ARG_NUMBER_RESET)
10346           {
10347             action_arg_number = level.initial_player_stepsize[i];
10348           }
10349
10350           move_stepsize =
10351             getModifiedActionNumber(move_stepsize,
10352                                     action_mode,
10353                                     action_arg_number,
10354                                     action_arg_number_min,
10355                                     action_arg_number_max);
10356
10357           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10358         }
10359       }
10360
10361       break;
10362     }
10363
10364     case CA_SET_PLAYER_SHIELD:
10365     {
10366       for (i = 0; i < MAX_PLAYERS; i++)
10367       {
10368         if (trigger_player_bits & (1 << i))
10369         {
10370           if (action_arg == CA_ARG_SHIELD_OFF)
10371           {
10372             stored_player[i].shield_normal_time_left = 0;
10373             stored_player[i].shield_deadly_time_left = 0;
10374           }
10375           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10376           {
10377             stored_player[i].shield_normal_time_left = 999999;
10378           }
10379           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10380           {
10381             stored_player[i].shield_normal_time_left = 999999;
10382             stored_player[i].shield_deadly_time_left = 999999;
10383           }
10384         }
10385       }
10386
10387       break;
10388     }
10389
10390     case CA_SET_PLAYER_GRAVITY:
10391     {
10392       for (i = 0; i < MAX_PLAYERS; i++)
10393       {
10394         if (trigger_player_bits & (1 << i))
10395         {
10396           stored_player[i].gravity =
10397             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10398              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10399              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10400              stored_player[i].gravity);
10401         }
10402       }
10403
10404       break;
10405     }
10406
10407     case CA_SET_PLAYER_ARTWORK:
10408     {
10409       for (i = 0; i < MAX_PLAYERS; i++)
10410       {
10411         if (trigger_player_bits & (1 << i))
10412         {
10413           int artwork_element = action_arg_element;
10414
10415           if (action_arg == CA_ARG_ELEMENT_RESET)
10416             artwork_element =
10417               (level.use_artwork_element[i] ? level.artwork_element[i] :
10418                stored_player[i].element_nr);
10419
10420           if (stored_player[i].artwork_element != artwork_element)
10421             stored_player[i].Frame = 0;
10422
10423           stored_player[i].artwork_element = artwork_element;
10424
10425           SetPlayerWaiting(&stored_player[i], FALSE);
10426
10427           // set number of special actions for bored and sleeping animation
10428           stored_player[i].num_special_action_bored =
10429             get_num_special_action(artwork_element,
10430                                    ACTION_BORING_1, ACTION_BORING_LAST);
10431           stored_player[i].num_special_action_sleeping =
10432             get_num_special_action(artwork_element,
10433                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10434         }
10435       }
10436
10437       break;
10438     }
10439
10440     case CA_SET_PLAYER_INVENTORY:
10441     {
10442       for (i = 0; i < MAX_PLAYERS; i++)
10443       {
10444         struct PlayerInfo *player = &stored_player[i];
10445         int j, k;
10446
10447         if (trigger_player_bits & (1 << i))
10448         {
10449           int inventory_element = action_arg_element;
10450
10451           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10452               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10453               action_arg == CA_ARG_ELEMENT_ACTION)
10454           {
10455             int element = inventory_element;
10456             int collect_count = element_info[element].collect_count_initial;
10457
10458             if (!IS_CUSTOM_ELEMENT(element))
10459               collect_count = 1;
10460
10461             if (collect_count == 0)
10462               player->inventory_infinite_element = element;
10463             else
10464               for (k = 0; k < collect_count; k++)
10465                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10466                   player->inventory_element[player->inventory_size++] =
10467                     element;
10468           }
10469           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10470                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10471                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10472           {
10473             if (player->inventory_infinite_element != EL_UNDEFINED &&
10474                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10475                                      action_arg_element_raw))
10476               player->inventory_infinite_element = EL_UNDEFINED;
10477
10478             for (k = 0, j = 0; j < player->inventory_size; j++)
10479             {
10480               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10481                                         action_arg_element_raw))
10482                 player->inventory_element[k++] = player->inventory_element[j];
10483             }
10484
10485             player->inventory_size = k;
10486           }
10487           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10488           {
10489             if (player->inventory_size > 0)
10490             {
10491               for (j = 0; j < player->inventory_size - 1; j++)
10492                 player->inventory_element[j] = player->inventory_element[j + 1];
10493
10494               player->inventory_size--;
10495             }
10496           }
10497           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10498           {
10499             if (player->inventory_size > 0)
10500               player->inventory_size--;
10501           }
10502           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10503           {
10504             player->inventory_infinite_element = EL_UNDEFINED;
10505             player->inventory_size = 0;
10506           }
10507           else if (action_arg == CA_ARG_INVENTORY_RESET)
10508           {
10509             player->inventory_infinite_element = EL_UNDEFINED;
10510             player->inventory_size = 0;
10511
10512             if (level.use_initial_inventory[i])
10513             {
10514               for (j = 0; j < level.initial_inventory_size[i]; j++)
10515               {
10516                 int element = level.initial_inventory_content[i][j];
10517                 int collect_count = element_info[element].collect_count_initial;
10518
10519                 if (!IS_CUSTOM_ELEMENT(element))
10520                   collect_count = 1;
10521
10522                 if (collect_count == 0)
10523                   player->inventory_infinite_element = element;
10524                 else
10525                   for (k = 0; k < collect_count; k++)
10526                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10527                       player->inventory_element[player->inventory_size++] =
10528                         element;
10529               }
10530             }
10531           }
10532         }
10533       }
10534
10535       break;
10536     }
10537
10538     // ---------- CE actions  -------------------------------------------------
10539
10540     case CA_SET_CE_VALUE:
10541     {
10542       int last_ce_value = CustomValue[x][y];
10543
10544       CustomValue[x][y] = action_arg_number_new;
10545
10546       if (CustomValue[x][y] != last_ce_value)
10547       {
10548         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10549         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10550
10551         if (CustomValue[x][y] == 0)
10552         {
10553           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10554           ChangeCount[x][y] = 0;        // allow at least one more change
10555
10556           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10557           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10558         }
10559       }
10560
10561       break;
10562     }
10563
10564     case CA_SET_CE_SCORE:
10565     {
10566       int last_ce_score = ei->collect_score;
10567
10568       ei->collect_score = action_arg_number_new;
10569
10570       if (ei->collect_score != last_ce_score)
10571       {
10572         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10573         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10574
10575         if (ei->collect_score == 0)
10576         {
10577           int xx, yy;
10578
10579           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10580           ChangeCount[x][y] = 0;        // allow at least one more change
10581
10582           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10583           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10584
10585           /*
10586             This is a very special case that seems to be a mixture between
10587             CheckElementChange() and CheckTriggeredElementChange(): while
10588             the first one only affects single elements that are triggered
10589             directly, the second one affects multiple elements in the playfield
10590             that are triggered indirectly by another element. This is a third
10591             case: Changing the CE score always affects multiple identical CEs,
10592             so every affected CE must be checked, not only the single CE for
10593             which the CE score was changed in the first place (as every instance
10594             of that CE shares the same CE score, and therefore also can change)!
10595           */
10596           SCAN_PLAYFIELD(xx, yy)
10597           {
10598             if (Tile[xx][yy] == element)
10599               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10600                                  CE_SCORE_GETS_ZERO);
10601           }
10602         }
10603       }
10604
10605       break;
10606     }
10607
10608     case CA_SET_CE_ARTWORK:
10609     {
10610       int artwork_element = action_arg_element;
10611       boolean reset_frame = FALSE;
10612       int xx, yy;
10613
10614       if (action_arg == CA_ARG_ELEMENT_RESET)
10615         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10616                            element);
10617
10618       if (ei->gfx_element != artwork_element)
10619         reset_frame = TRUE;
10620
10621       ei->gfx_element = artwork_element;
10622
10623       SCAN_PLAYFIELD(xx, yy)
10624       {
10625         if (Tile[xx][yy] == element)
10626         {
10627           if (reset_frame)
10628           {
10629             ResetGfxAnimation(xx, yy);
10630             ResetRandomAnimationValue(xx, yy);
10631           }
10632
10633           TEST_DrawLevelField(xx, yy);
10634         }
10635       }
10636
10637       break;
10638     }
10639
10640     // ---------- engine actions  ---------------------------------------------
10641
10642     case CA_SET_ENGINE_SCAN_MODE:
10643     {
10644       InitPlayfieldScanMode(action_arg);
10645
10646       break;
10647     }
10648
10649     default:
10650       break;
10651   }
10652 }
10653
10654 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10655 {
10656   int old_element = Tile[x][y];
10657   int new_element = GetElementFromGroupElement(element);
10658   int previous_move_direction = MovDir[x][y];
10659   int last_ce_value = CustomValue[x][y];
10660   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10661   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10662   boolean add_player_onto_element = (new_element_is_player &&
10663                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10664                                      IS_WALKABLE(old_element));
10665
10666   if (!add_player_onto_element)
10667   {
10668     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10669       RemoveMovingField(x, y);
10670     else
10671       RemoveField(x, y);
10672
10673     Tile[x][y] = new_element;
10674
10675     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10676       MovDir[x][y] = previous_move_direction;
10677
10678     if (element_info[new_element].use_last_ce_value)
10679       CustomValue[x][y] = last_ce_value;
10680
10681     InitField_WithBug1(x, y, FALSE);
10682
10683     new_element = Tile[x][y];   // element may have changed
10684
10685     ResetGfxAnimation(x, y);
10686     ResetRandomAnimationValue(x, y);
10687
10688     TEST_DrawLevelField(x, y);
10689
10690     if (GFX_CRUMBLED(new_element))
10691       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10692
10693     if (old_element == EL_EXPLOSION)
10694     {
10695       Store[x][y] = Store2[x][y] = 0;
10696
10697       // check if new element replaces an exploding player, requiring cleanup
10698       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10699         StorePlayer[x][y] = 0;
10700     }
10701
10702     // check if element under the player changes from accessible to unaccessible
10703     // (needed for special case of dropping element which then changes)
10704     // (must be checked after creating new element for walkable group elements)
10705     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10706         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10707     {
10708       KillPlayer(PLAYERINFO(x, y));
10709
10710       return;
10711     }
10712   }
10713
10714   // "ChangeCount" not set yet to allow "entered by player" change one time
10715   if (new_element_is_player)
10716     RelocatePlayer(x, y, new_element);
10717
10718   if (is_change)
10719     ChangeCount[x][y]++;        // count number of changes in the same frame
10720
10721   TestIfBadThingTouchesPlayer(x, y);
10722   TestIfPlayerTouchesCustomElement(x, y);
10723   TestIfElementTouchesCustomElement(x, y);
10724 }
10725
10726 static void CreateField(int x, int y, int element)
10727 {
10728   CreateFieldExt(x, y, element, FALSE);
10729 }
10730
10731 static void CreateElementFromChange(int x, int y, int element)
10732 {
10733   element = GET_VALID_RUNTIME_ELEMENT(element);
10734
10735   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10736   {
10737     int old_element = Tile[x][y];
10738
10739     // prevent changed element from moving in same engine frame
10740     // unless both old and new element can either fall or move
10741     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10742         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10743       Stop[x][y] = TRUE;
10744   }
10745
10746   CreateFieldExt(x, y, element, TRUE);
10747 }
10748
10749 static boolean ChangeElement(int x, int y, int element, int page)
10750 {
10751   struct ElementInfo *ei = &element_info[element];
10752   struct ElementChangeInfo *change = &ei->change_page[page];
10753   int ce_value = CustomValue[x][y];
10754   int ce_score = ei->collect_score;
10755   int target_element;
10756   int old_element = Tile[x][y];
10757
10758   // always use default change event to prevent running into a loop
10759   if (ChangeEvent[x][y] == -1)
10760     ChangeEvent[x][y] = CE_DELAY;
10761
10762   if (ChangeEvent[x][y] == CE_DELAY)
10763   {
10764     // reset actual trigger element, trigger player and action element
10765     change->actual_trigger_element = EL_EMPTY;
10766     change->actual_trigger_player = EL_EMPTY;
10767     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10768     change->actual_trigger_side = CH_SIDE_NONE;
10769     change->actual_trigger_ce_value = 0;
10770     change->actual_trigger_ce_score = 0;
10771     change->actual_trigger_x = -1;
10772     change->actual_trigger_y = -1;
10773   }
10774
10775   // do not change elements more than a specified maximum number of changes
10776   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10777     return FALSE;
10778
10779   ChangeCount[x][y]++;          // count number of changes in the same frame
10780
10781   if (ei->has_anim_event)
10782     HandleGlobalAnimEventByElementChange(element, page, x, y,
10783                                          change->actual_trigger_x,
10784                                          change->actual_trigger_y);
10785
10786   if (change->explode)
10787   {
10788     Bang(x, y);
10789
10790     return TRUE;
10791   }
10792
10793   if (change->use_target_content)
10794   {
10795     boolean complete_replace = TRUE;
10796     boolean can_replace[3][3];
10797     int xx, yy;
10798
10799     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10800     {
10801       boolean is_empty;
10802       boolean is_walkable;
10803       boolean is_diggable;
10804       boolean is_collectible;
10805       boolean is_removable;
10806       boolean is_destructible;
10807       int ex = x + xx - 1;
10808       int ey = y + yy - 1;
10809       int content_element = change->target_content.e[xx][yy];
10810       int e;
10811
10812       can_replace[xx][yy] = TRUE;
10813
10814       if (ex == x && ey == y)   // do not check changing element itself
10815         continue;
10816
10817       if (content_element == EL_EMPTY_SPACE)
10818       {
10819         can_replace[xx][yy] = FALSE;    // do not replace border with space
10820
10821         continue;
10822       }
10823
10824       if (!IN_LEV_FIELD(ex, ey))
10825       {
10826         can_replace[xx][yy] = FALSE;
10827         complete_replace = FALSE;
10828
10829         continue;
10830       }
10831
10832       e = Tile[ex][ey];
10833
10834       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10835         e = MovingOrBlocked2Element(ex, ey);
10836
10837       is_empty = (IS_FREE(ex, ey) ||
10838                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10839
10840       is_walkable     = (is_empty || IS_WALKABLE(e));
10841       is_diggable     = (is_empty || IS_DIGGABLE(e));
10842       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10843       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10844       is_removable    = (is_diggable || is_collectible);
10845
10846       can_replace[xx][yy] =
10847         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10848           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10849           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10850           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10851           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10852           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10853          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10854
10855       if (!can_replace[xx][yy])
10856         complete_replace = FALSE;
10857     }
10858
10859     if (!change->only_if_complete || complete_replace)
10860     {
10861       boolean something_has_changed = FALSE;
10862
10863       if (change->only_if_complete && change->use_random_replace &&
10864           RND(100) < change->random_percentage)
10865         return FALSE;
10866
10867       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10868       {
10869         int ex = x + xx - 1;
10870         int ey = y + yy - 1;
10871         int content_element;
10872
10873         if (can_replace[xx][yy] && (!change->use_random_replace ||
10874                                     RND(100) < change->random_percentage))
10875         {
10876           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10877             RemoveMovingField(ex, ey);
10878
10879           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10880
10881           content_element = change->target_content.e[xx][yy];
10882           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10883                                               ce_value, ce_score);
10884
10885           CreateElementFromChange(ex, ey, target_element);
10886
10887           something_has_changed = TRUE;
10888
10889           // for symmetry reasons, freeze newly created border elements
10890           if (ex != x || ey != y)
10891             Stop[ex][ey] = TRUE;        // no more moving in this frame
10892         }
10893       }
10894
10895       if (something_has_changed)
10896       {
10897         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10898         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10899       }
10900     }
10901   }
10902   else
10903   {
10904     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10905                                         ce_value, ce_score);
10906
10907     if (element == EL_DIAGONAL_GROWING ||
10908         element == EL_DIAGONAL_SHRINKING)
10909     {
10910       target_element = Store[x][y];
10911
10912       Store[x][y] = EL_EMPTY;
10913     }
10914
10915     // special case: element changes to player (and may be kept if walkable)
10916     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10917       CreateElementFromChange(x, y, EL_EMPTY);
10918
10919     CreateElementFromChange(x, y, target_element);
10920
10921     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10922     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10923   }
10924
10925   // this uses direct change before indirect change
10926   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10927
10928   return TRUE;
10929 }
10930
10931 static void HandleElementChange(int x, int y, int page)
10932 {
10933   int element = MovingOrBlocked2Element(x, y);
10934   struct ElementInfo *ei = &element_info[element];
10935   struct ElementChangeInfo *change = &ei->change_page[page];
10936   boolean handle_action_before_change = FALSE;
10937
10938 #ifdef DEBUG
10939   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10940       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10941   {
10942     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10943           x, y, element, element_info[element].token_name);
10944     Debug("game:playing:HandleElementChange", "This should never happen!");
10945   }
10946 #endif
10947
10948   // this can happen with classic bombs on walkable, changing elements
10949   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10950   {
10951     return;
10952   }
10953
10954   if (ChangeDelay[x][y] == 0)           // initialize element change
10955   {
10956     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10957
10958     if (change->can_change)
10959     {
10960       // !!! not clear why graphic animation should be reset at all here !!!
10961       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10962       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10963
10964       /*
10965         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10966
10967         When using an animation frame delay of 1 (this only happens with
10968         "sp_zonk.moving.left/right" in the classic graphics), the default
10969         (non-moving) animation shows wrong animation frames (while the
10970         moving animation, like "sp_zonk.moving.left/right", is correct,
10971         so this graphical bug never shows up with the classic graphics).
10972         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10973         be drawn instead of the correct frames 0,1,2,3. This is caused by
10974         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10975         an element change: First when the change delay ("ChangeDelay[][]")
10976         counter has reached zero after decrementing, then a second time in
10977         the next frame (after "GfxFrame[][]" was already incremented) when
10978         "ChangeDelay[][]" is reset to the initial delay value again.
10979
10980         This causes frame 0 to be drawn twice, while the last frame won't
10981         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10982
10983         As some animations may already be cleverly designed around this bug
10984         (at least the "Snake Bite" snake tail animation does this), it cannot
10985         simply be fixed here without breaking such existing animations.
10986         Unfortunately, it cannot easily be detected if a graphics set was
10987         designed "before" or "after" the bug was fixed. As a workaround,
10988         a new graphics set option "game.graphics_engine_version" was added
10989         to be able to specify the game's major release version for which the
10990         graphics set was designed, which can then be used to decide if the
10991         bugfix should be used (version 4 and above) or not (version 3 or
10992         below, or if no version was specified at all, as with old sets).
10993
10994         (The wrong/fixed animation frames can be tested with the test level set
10995         "test_gfxframe" and level "000", which contains a specially prepared
10996         custom element at level position (x/y) == (11/9) which uses the zonk
10997         animation mentioned above. Using "game.graphics_engine_version: 4"
10998         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10999         This can also be seen from the debug output for this test element.)
11000       */
11001
11002       // when a custom element is about to change (for example by change delay),
11003       // do not reset graphic animation when the custom element is moving
11004       if (game.graphics_engine_version < 4 &&
11005           !IS_MOVING(x, y))
11006       {
11007         ResetGfxAnimation(x, y);
11008         ResetRandomAnimationValue(x, y);
11009       }
11010
11011       if (change->pre_change_function)
11012         change->pre_change_function(x, y);
11013     }
11014   }
11015
11016   ChangeDelay[x][y]--;
11017
11018   if (ChangeDelay[x][y] != 0)           // continue element change
11019   {
11020     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11021
11022     // also needed if CE can not change, but has CE delay with CE action
11023     if (IS_ANIMATED(graphic))
11024       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11025
11026     if (change->can_change)
11027     {
11028       if (change->change_function)
11029         change->change_function(x, y);
11030     }
11031   }
11032   else                                  // finish element change
11033   {
11034     if (ChangePage[x][y] != -1)         // remember page from delayed change
11035     {
11036       page = ChangePage[x][y];
11037       ChangePage[x][y] = -1;
11038
11039       change = &ei->change_page[page];
11040     }
11041
11042     if (IS_MOVING(x, y))                // never change a running system ;-)
11043     {
11044       ChangeDelay[x][y] = 1;            // try change after next move step
11045       ChangePage[x][y] = page;          // remember page to use for change
11046
11047       return;
11048     }
11049
11050     // special case: set new level random seed before changing element
11051     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11052       handle_action_before_change = TRUE;
11053
11054     if (change->has_action && handle_action_before_change)
11055       ExecuteCustomElementAction(x, y, element, page);
11056
11057     if (change->can_change)
11058     {
11059       if (ChangeElement(x, y, element, page))
11060       {
11061         if (change->post_change_function)
11062           change->post_change_function(x, y);
11063       }
11064     }
11065
11066     if (change->has_action && !handle_action_before_change)
11067       ExecuteCustomElementAction(x, y, element, page);
11068   }
11069 }
11070
11071 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11072                                               int trigger_element,
11073                                               int trigger_event,
11074                                               int trigger_player,
11075                                               int trigger_side,
11076                                               int trigger_page)
11077 {
11078   boolean change_done_any = FALSE;
11079   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11080   int i;
11081
11082   if (!(trigger_events[trigger_element][trigger_event]))
11083     return FALSE;
11084
11085   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11086
11087   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11088   {
11089     int element = EL_CUSTOM_START + i;
11090     boolean change_done = FALSE;
11091     int p;
11092
11093     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11094         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11095       continue;
11096
11097     for (p = 0; p < element_info[element].num_change_pages; p++)
11098     {
11099       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11100
11101       if (change->can_change_or_has_action &&
11102           change->has_event[trigger_event] &&
11103           change->trigger_side & trigger_side &&
11104           change->trigger_player & trigger_player &&
11105           change->trigger_page & trigger_page_bits &&
11106           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11107       {
11108         change->actual_trigger_element = trigger_element;
11109         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11110         change->actual_trigger_player_bits = trigger_player;
11111         change->actual_trigger_side = trigger_side;
11112         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11113         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11114         change->actual_trigger_x = trigger_x;
11115         change->actual_trigger_y = trigger_y;
11116
11117         if ((change->can_change && !change_done) || change->has_action)
11118         {
11119           int x, y;
11120
11121           SCAN_PLAYFIELD(x, y)
11122           {
11123             if (Tile[x][y] == element)
11124             {
11125               if (change->can_change && !change_done)
11126               {
11127                 // if element already changed in this frame, not only prevent
11128                 // another element change (checked in ChangeElement()), but
11129                 // also prevent additional element actions for this element
11130
11131                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11132                     !level.use_action_after_change_bug)
11133                   continue;
11134
11135                 ChangeDelay[x][y] = 1;
11136                 ChangeEvent[x][y] = trigger_event;
11137
11138                 HandleElementChange(x, y, p);
11139               }
11140               else if (change->has_action)
11141               {
11142                 // if element already changed in this frame, not only prevent
11143                 // another element change (checked in ChangeElement()), but
11144                 // also prevent additional element actions for this element
11145
11146                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11147                     !level.use_action_after_change_bug)
11148                   continue;
11149
11150                 ExecuteCustomElementAction(x, y, element, p);
11151                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11152               }
11153             }
11154           }
11155
11156           if (change->can_change)
11157           {
11158             change_done = TRUE;
11159             change_done_any = TRUE;
11160           }
11161         }
11162       }
11163     }
11164   }
11165
11166   RECURSION_LOOP_DETECTION_END();
11167
11168   return change_done_any;
11169 }
11170
11171 static boolean CheckElementChangeExt(int x, int y,
11172                                      int element,
11173                                      int trigger_element,
11174                                      int trigger_event,
11175                                      int trigger_player,
11176                                      int trigger_side)
11177 {
11178   boolean change_done = FALSE;
11179   int p;
11180
11181   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11182       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11183     return FALSE;
11184
11185   if (Tile[x][y] == EL_BLOCKED)
11186   {
11187     Blocked2Moving(x, y, &x, &y);
11188     element = Tile[x][y];
11189   }
11190
11191   // check if element has already changed or is about to change after moving
11192   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11193        Tile[x][y] != element) ||
11194
11195       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11196        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11197         ChangePage[x][y] != -1)))
11198     return FALSE;
11199
11200   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11201
11202   for (p = 0; p < element_info[element].num_change_pages; p++)
11203   {
11204     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11205
11206     /* check trigger element for all events where the element that is checked
11207        for changing interacts with a directly adjacent element -- this is
11208        different to element changes that affect other elements to change on the
11209        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11210     boolean check_trigger_element =
11211       (trigger_event == CE_NEXT_TO_X ||
11212        trigger_event == CE_TOUCHING_X ||
11213        trigger_event == CE_HITTING_X ||
11214        trigger_event == CE_HIT_BY_X ||
11215        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11216
11217     if (change->can_change_or_has_action &&
11218         change->has_event[trigger_event] &&
11219         change->trigger_side & trigger_side &&
11220         change->trigger_player & trigger_player &&
11221         (!check_trigger_element ||
11222          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11223     {
11224       change->actual_trigger_element = trigger_element;
11225       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11226       change->actual_trigger_player_bits = trigger_player;
11227       change->actual_trigger_side = trigger_side;
11228       change->actual_trigger_ce_value = CustomValue[x][y];
11229       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11230       change->actual_trigger_x = x;
11231       change->actual_trigger_y = y;
11232
11233       // special case: trigger element not at (x,y) position for some events
11234       if (check_trigger_element)
11235       {
11236         static struct
11237         {
11238           int dx, dy;
11239         } move_xy[] =
11240           {
11241             {  0,  0 },
11242             { -1,  0 },
11243             { +1,  0 },
11244             {  0,  0 },
11245             {  0, -1 },
11246             {  0,  0 }, { 0, 0 }, { 0, 0 },
11247             {  0, +1 }
11248           };
11249
11250         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11251         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11252
11253         change->actual_trigger_ce_value = CustomValue[xx][yy];
11254         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11255         change->actual_trigger_x = xx;
11256         change->actual_trigger_y = yy;
11257       }
11258
11259       if (change->can_change && !change_done)
11260       {
11261         ChangeDelay[x][y] = 1;
11262         ChangeEvent[x][y] = trigger_event;
11263
11264         HandleElementChange(x, y, p);
11265
11266         change_done = TRUE;
11267       }
11268       else if (change->has_action)
11269       {
11270         ExecuteCustomElementAction(x, y, element, p);
11271         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11272       }
11273     }
11274   }
11275
11276   RECURSION_LOOP_DETECTION_END();
11277
11278   return change_done;
11279 }
11280
11281 static void PlayPlayerSound(struct PlayerInfo *player)
11282 {
11283   int jx = player->jx, jy = player->jy;
11284   int sound_element = player->artwork_element;
11285   int last_action = player->last_action_waiting;
11286   int action = player->action_waiting;
11287
11288   if (player->is_waiting)
11289   {
11290     if (action != last_action)
11291       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11292     else
11293       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11294   }
11295   else
11296   {
11297     if (action != last_action)
11298       StopSound(element_info[sound_element].sound[last_action]);
11299
11300     if (last_action == ACTION_SLEEPING)
11301       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11302   }
11303 }
11304
11305 static void PlayAllPlayersSound(void)
11306 {
11307   int i;
11308
11309   for (i = 0; i < MAX_PLAYERS; i++)
11310     if (stored_player[i].active)
11311       PlayPlayerSound(&stored_player[i]);
11312 }
11313
11314 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11315 {
11316   boolean last_waiting = player->is_waiting;
11317   int move_dir = player->MovDir;
11318
11319   player->dir_waiting = move_dir;
11320   player->last_action_waiting = player->action_waiting;
11321
11322   if (is_waiting)
11323   {
11324     if (!last_waiting)          // not waiting -> waiting
11325     {
11326       player->is_waiting = TRUE;
11327
11328       player->frame_counter_bored =
11329         FrameCounter +
11330         game.player_boring_delay_fixed +
11331         GetSimpleRandom(game.player_boring_delay_random);
11332       player->frame_counter_sleeping =
11333         FrameCounter +
11334         game.player_sleeping_delay_fixed +
11335         GetSimpleRandom(game.player_sleeping_delay_random);
11336
11337       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11338     }
11339
11340     if (game.player_sleeping_delay_fixed +
11341         game.player_sleeping_delay_random > 0 &&
11342         player->anim_delay_counter == 0 &&
11343         player->post_delay_counter == 0 &&
11344         FrameCounter >= player->frame_counter_sleeping)
11345       player->is_sleeping = TRUE;
11346     else if (game.player_boring_delay_fixed +
11347              game.player_boring_delay_random > 0 &&
11348              FrameCounter >= player->frame_counter_bored)
11349       player->is_bored = TRUE;
11350
11351     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11352                               player->is_bored ? ACTION_BORING :
11353                               ACTION_WAITING);
11354
11355     if (player->is_sleeping && player->use_murphy)
11356     {
11357       // special case for sleeping Murphy when leaning against non-free tile
11358
11359       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11360           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11361            !IS_MOVING(player->jx - 1, player->jy)))
11362         move_dir = MV_LEFT;
11363       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11364                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11365                 !IS_MOVING(player->jx + 1, player->jy)))
11366         move_dir = MV_RIGHT;
11367       else
11368         player->is_sleeping = FALSE;
11369
11370       player->dir_waiting = move_dir;
11371     }
11372
11373     if (player->is_sleeping)
11374     {
11375       if (player->num_special_action_sleeping > 0)
11376       {
11377         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11378         {
11379           int last_special_action = player->special_action_sleeping;
11380           int num_special_action = player->num_special_action_sleeping;
11381           int special_action =
11382             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11383              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11384              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11385              last_special_action + 1 : ACTION_SLEEPING);
11386           int special_graphic =
11387             el_act_dir2img(player->artwork_element, special_action, move_dir);
11388
11389           player->anim_delay_counter =
11390             graphic_info[special_graphic].anim_delay_fixed +
11391             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11392           player->post_delay_counter =
11393             graphic_info[special_graphic].post_delay_fixed +
11394             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11395
11396           player->special_action_sleeping = special_action;
11397         }
11398
11399         if (player->anim_delay_counter > 0)
11400         {
11401           player->action_waiting = player->special_action_sleeping;
11402           player->anim_delay_counter--;
11403         }
11404         else if (player->post_delay_counter > 0)
11405         {
11406           player->post_delay_counter--;
11407         }
11408       }
11409     }
11410     else if (player->is_bored)
11411     {
11412       if (player->num_special_action_bored > 0)
11413       {
11414         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11415         {
11416           int special_action =
11417             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11418           int special_graphic =
11419             el_act_dir2img(player->artwork_element, special_action, move_dir);
11420
11421           player->anim_delay_counter =
11422             graphic_info[special_graphic].anim_delay_fixed +
11423             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11424           player->post_delay_counter =
11425             graphic_info[special_graphic].post_delay_fixed +
11426             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11427
11428           player->special_action_bored = special_action;
11429         }
11430
11431         if (player->anim_delay_counter > 0)
11432         {
11433           player->action_waiting = player->special_action_bored;
11434           player->anim_delay_counter--;
11435         }
11436         else if (player->post_delay_counter > 0)
11437         {
11438           player->post_delay_counter--;
11439         }
11440       }
11441     }
11442   }
11443   else if (last_waiting)        // waiting -> not waiting
11444   {
11445     player->is_waiting = FALSE;
11446     player->is_bored = FALSE;
11447     player->is_sleeping = FALSE;
11448
11449     player->frame_counter_bored = -1;
11450     player->frame_counter_sleeping = -1;
11451
11452     player->anim_delay_counter = 0;
11453     player->post_delay_counter = 0;
11454
11455     player->dir_waiting = player->MovDir;
11456     player->action_waiting = ACTION_DEFAULT;
11457
11458     player->special_action_bored = ACTION_DEFAULT;
11459     player->special_action_sleeping = ACTION_DEFAULT;
11460   }
11461 }
11462
11463 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11464 {
11465   if ((!player->is_moving  && player->was_moving) ||
11466       (player->MovPos == 0 && player->was_moving) ||
11467       (player->is_snapping && !player->was_snapping) ||
11468       (player->is_dropping && !player->was_dropping))
11469   {
11470     if (!CheckSaveEngineSnapshotToList())
11471       return;
11472
11473     player->was_moving = FALSE;
11474     player->was_snapping = TRUE;
11475     player->was_dropping = TRUE;
11476   }
11477   else
11478   {
11479     if (player->is_moving)
11480       player->was_moving = TRUE;
11481
11482     if (!player->is_snapping)
11483       player->was_snapping = FALSE;
11484
11485     if (!player->is_dropping)
11486       player->was_dropping = FALSE;
11487   }
11488
11489   static struct MouseActionInfo mouse_action_last = { 0 };
11490   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11491   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11492
11493   if (new_released)
11494     CheckSaveEngineSnapshotToList();
11495
11496   mouse_action_last = mouse_action;
11497 }
11498
11499 static void CheckSingleStepMode(struct PlayerInfo *player)
11500 {
11501   if (tape.single_step && tape.recording && !tape.pausing)
11502   {
11503     // as it is called "single step mode", just return to pause mode when the
11504     // player stopped moving after one tile (or never starts moving at all)
11505     // (reverse logic needed here in case single step mode used in team mode)
11506     if (player->is_moving ||
11507         player->is_pushing ||
11508         player->is_dropping_pressed ||
11509         player->effective_mouse_action.button)
11510       game.enter_single_step_mode = FALSE;
11511   }
11512
11513   CheckSaveEngineSnapshot(player);
11514 }
11515
11516 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11517 {
11518   int left      = player_action & JOY_LEFT;
11519   int right     = player_action & JOY_RIGHT;
11520   int up        = player_action & JOY_UP;
11521   int down      = player_action & JOY_DOWN;
11522   int button1   = player_action & JOY_BUTTON_1;
11523   int button2   = player_action & JOY_BUTTON_2;
11524   int dx        = (left ? -1 : right ? 1 : 0);
11525   int dy        = (up   ? -1 : down  ? 1 : 0);
11526
11527   if (!player->active || tape.pausing)
11528     return 0;
11529
11530   if (player_action)
11531   {
11532     if (button1)
11533       SnapField(player, dx, dy);
11534     else
11535     {
11536       if (button2)
11537         DropElement(player);
11538
11539       MovePlayer(player, dx, dy);
11540     }
11541
11542     CheckSingleStepMode(player);
11543
11544     SetPlayerWaiting(player, FALSE);
11545
11546     return player_action;
11547   }
11548   else
11549   {
11550     // no actions for this player (no input at player's configured device)
11551
11552     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11553     SnapField(player, 0, 0);
11554     CheckGravityMovementWhenNotMoving(player);
11555
11556     if (player->MovPos == 0)
11557       SetPlayerWaiting(player, TRUE);
11558
11559     if (player->MovPos == 0)    // needed for tape.playing
11560       player->is_moving = FALSE;
11561
11562     player->is_dropping = FALSE;
11563     player->is_dropping_pressed = FALSE;
11564     player->drop_pressed_delay = 0;
11565
11566     CheckSingleStepMode(player);
11567
11568     return 0;
11569   }
11570 }
11571
11572 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11573                                          byte *tape_action)
11574 {
11575   if (!tape.use_mouse_actions)
11576     return;
11577
11578   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11579   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11580   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11581 }
11582
11583 static void SetTapeActionFromMouseAction(byte *tape_action,
11584                                          struct MouseActionInfo *mouse_action)
11585 {
11586   if (!tape.use_mouse_actions)
11587     return;
11588
11589   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11590   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11591   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11592 }
11593
11594 static void CheckLevelSolved(void)
11595 {
11596   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11597   {
11598     if (game_em.level_solved &&
11599         !game_em.game_over)                             // game won
11600     {
11601       LevelSolved();
11602
11603       game_em.game_over = TRUE;
11604
11605       game.all_players_gone = TRUE;
11606     }
11607
11608     if (game_em.game_over)                              // game lost
11609       game.all_players_gone = TRUE;
11610   }
11611   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11612   {
11613     if (game_sp.level_solved &&
11614         !game_sp.game_over)                             // game won
11615     {
11616       LevelSolved();
11617
11618       game_sp.game_over = TRUE;
11619
11620       game.all_players_gone = TRUE;
11621     }
11622
11623     if (game_sp.game_over)                              // game lost
11624       game.all_players_gone = TRUE;
11625   }
11626   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11627   {
11628     if (game_mm.level_solved &&
11629         !game_mm.game_over)                             // game won
11630     {
11631       LevelSolved();
11632
11633       game_mm.game_over = TRUE;
11634
11635       game.all_players_gone = TRUE;
11636     }
11637
11638     if (game_mm.game_over)                              // game lost
11639       game.all_players_gone = TRUE;
11640   }
11641 }
11642
11643 static void CheckLevelTime_StepCounter(void)
11644 {
11645   int i;
11646
11647   TimePlayed++;
11648
11649   if (TimeLeft > 0)
11650   {
11651     TimeLeft--;
11652
11653     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11654       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11655
11656     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11657
11658     DisplayGameControlValues();
11659
11660     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11661       for (i = 0; i < MAX_PLAYERS; i++)
11662         KillPlayer(&stored_player[i]);
11663   }
11664   else if (game.no_level_time_limit && !game.all_players_gone)
11665   {
11666     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11667
11668     DisplayGameControlValues();
11669   }
11670 }
11671
11672 static void CheckLevelTime(void)
11673 {
11674   int i;
11675
11676   if (TimeFrames >= FRAMES_PER_SECOND)
11677   {
11678     TimeFrames = 0;
11679     TapeTime++;
11680
11681     for (i = 0; i < MAX_PLAYERS; i++)
11682     {
11683       struct PlayerInfo *player = &stored_player[i];
11684
11685       if (SHIELD_ON(player))
11686       {
11687         player->shield_normal_time_left--;
11688
11689         if (player->shield_deadly_time_left > 0)
11690           player->shield_deadly_time_left--;
11691       }
11692     }
11693
11694     if (!game.LevelSolved && !level.use_step_counter)
11695     {
11696       TimePlayed++;
11697
11698       if (TimeLeft > 0)
11699       {
11700         TimeLeft--;
11701
11702         if (TimeLeft <= 10 && game.time_limit)
11703           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11704
11705         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11706            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11707
11708         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11709
11710         if (!TimeLeft && game.time_limit)
11711         {
11712           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11713             game_em.lev->killed_out_of_time = TRUE;
11714           else
11715             for (i = 0; i < MAX_PLAYERS; i++)
11716               KillPlayer(&stored_player[i]);
11717         }
11718       }
11719       else if (game.no_level_time_limit && !game.all_players_gone)
11720       {
11721         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11722       }
11723
11724       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11725     }
11726
11727     if (tape.recording || tape.playing)
11728       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11729   }
11730
11731   if (tape.recording || tape.playing)
11732     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11733
11734   UpdateAndDisplayGameControlValues();
11735 }
11736
11737 void AdvanceFrameAndPlayerCounters(int player_nr)
11738 {
11739   int i;
11740
11741   // advance frame counters (global frame counter and time frame counter)
11742   FrameCounter++;
11743   TimeFrames++;
11744
11745   // advance player counters (counters for move delay, move animation etc.)
11746   for (i = 0; i < MAX_PLAYERS; i++)
11747   {
11748     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11749     int move_delay_value = stored_player[i].move_delay_value;
11750     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11751
11752     if (!advance_player_counters)       // not all players may be affected
11753       continue;
11754
11755     if (move_frames == 0)       // less than one move per game frame
11756     {
11757       int stepsize = TILEX / move_delay_value;
11758       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11759       int count = (stored_player[i].is_moving ?
11760                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11761
11762       if (count % delay == 0)
11763         move_frames = 1;
11764     }
11765
11766     stored_player[i].Frame += move_frames;
11767
11768     if (stored_player[i].MovPos != 0)
11769       stored_player[i].StepFrame += move_frames;
11770
11771     if (stored_player[i].move_delay > 0)
11772       stored_player[i].move_delay--;
11773
11774     // due to bugs in previous versions, counter must count up, not down
11775     if (stored_player[i].push_delay != -1)
11776       stored_player[i].push_delay++;
11777
11778     if (stored_player[i].drop_delay > 0)
11779       stored_player[i].drop_delay--;
11780
11781     if (stored_player[i].is_dropping_pressed)
11782       stored_player[i].drop_pressed_delay++;
11783   }
11784 }
11785
11786 void AdvanceFrameCounter(void)
11787 {
11788   FrameCounter++;
11789 }
11790
11791 void AdvanceGfxFrame(void)
11792 {
11793   int x, y;
11794
11795   SCAN_PLAYFIELD(x, y)
11796   {
11797     GfxFrame[x][y]++;
11798   }
11799 }
11800
11801 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11802                               struct MouseActionInfo *mouse_action_last)
11803 {
11804   if (mouse_action->button)
11805   {
11806     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11807     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11808     int x = mouse_action->lx;
11809     int y = mouse_action->ly;
11810     int element = Tile[x][y];
11811
11812     if (new_button)
11813     {
11814       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11815       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11816                                          ch_button);
11817     }
11818
11819     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11820     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11821                                        ch_button);
11822
11823     if (level.use_step_counter)
11824     {
11825       boolean counted_click = FALSE;
11826
11827       // element clicked that can change when clicked/pressed
11828       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11829           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11830            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11831         counted_click = TRUE;
11832
11833       // element clicked that can trigger change when clicked/pressed
11834       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11835           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11836         counted_click = TRUE;
11837
11838       if (new_button && counted_click)
11839         CheckLevelTime_StepCounter();
11840     }
11841   }
11842 }
11843
11844 void StartGameActions(boolean init_network_game, boolean record_tape,
11845                       int random_seed)
11846 {
11847   unsigned int new_random_seed = InitRND(random_seed);
11848
11849   if (record_tape)
11850     TapeStartRecording(new_random_seed);
11851
11852   if (setup.auto_pause_on_start && !tape.pausing)
11853     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11854
11855   if (init_network_game)
11856   {
11857     SendToServer_LevelFile();
11858     SendToServer_StartPlaying();
11859
11860     return;
11861   }
11862
11863   InitGame();
11864 }
11865
11866 static void GameActionsExt(void)
11867 {
11868 #if 0
11869   static unsigned int game_frame_delay = 0;
11870 #endif
11871   unsigned int game_frame_delay_value;
11872   byte *recorded_player_action;
11873   byte summarized_player_action = 0;
11874   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11875   int i;
11876
11877   // detect endless loops, caused by custom element programming
11878   if (recursion_loop_detected && recursion_loop_depth == 0)
11879   {
11880     char *message = getStringCat3("Internal Error! Element ",
11881                                   EL_NAME(recursion_loop_element),
11882                                   " caused endless loop! Quit the game?");
11883
11884     Warn("element '%s' caused endless loop in game engine",
11885          EL_NAME(recursion_loop_element));
11886
11887     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11888
11889     recursion_loop_detected = FALSE;    // if game should be continued
11890
11891     free(message);
11892
11893     return;
11894   }
11895
11896   if (game.restart_level)
11897     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11898
11899   CheckLevelSolved();
11900
11901   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11902     GameWon();
11903
11904   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11905     TapeStop();
11906
11907   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11908     return;
11909
11910   game_frame_delay_value =
11911     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11912
11913   if (tape.playing && tape.warp_forward && !tape.pausing)
11914     game_frame_delay_value = 0;
11915
11916   SetVideoFrameDelay(game_frame_delay_value);
11917
11918   // (de)activate virtual buttons depending on current game status
11919   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11920   {
11921     if (game.all_players_gone)  // if no players there to be controlled anymore
11922       SetOverlayActive(FALSE);
11923     else if (!tape.playing)     // if game continues after tape stopped playing
11924       SetOverlayActive(TRUE);
11925   }
11926
11927 #if 0
11928 #if 0
11929   // ---------- main game synchronization point ----------
11930
11931   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11932
11933   Debug("game:playing:skip", "skip == %d", skip);
11934
11935 #else
11936   // ---------- main game synchronization point ----------
11937
11938   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11939 #endif
11940 #endif
11941
11942   if (network_playing && !network_player_action_received)
11943   {
11944     // try to get network player actions in time
11945
11946     // last chance to get network player actions without main loop delay
11947     HandleNetworking();
11948
11949     // game was quit by network peer
11950     if (game_status != GAME_MODE_PLAYING)
11951       return;
11952
11953     // check if network player actions still missing and game still running
11954     if (!network_player_action_received && !checkGameEnded())
11955       return;           // failed to get network player actions in time
11956
11957     // do not yet reset "network_player_action_received" (for tape.pausing)
11958   }
11959
11960   if (tape.pausing)
11961     return;
11962
11963   // at this point we know that we really continue executing the game
11964
11965   network_player_action_received = FALSE;
11966
11967   // when playing tape, read previously recorded player input from tape data
11968   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11969
11970   local_player->effective_mouse_action = local_player->mouse_action;
11971
11972   if (recorded_player_action != NULL)
11973     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11974                                  recorded_player_action);
11975
11976   // TapePlayAction() may return NULL when toggling to "pause before death"
11977   if (tape.pausing)
11978     return;
11979
11980   if (tape.set_centered_player)
11981   {
11982     game.centered_player_nr_next = tape.centered_player_nr_next;
11983     game.set_centered_player = TRUE;
11984   }
11985
11986   for (i = 0; i < MAX_PLAYERS; i++)
11987   {
11988     summarized_player_action |= stored_player[i].action;
11989
11990     if (!network_playing && (game.team_mode || tape.playing))
11991       stored_player[i].effective_action = stored_player[i].action;
11992   }
11993
11994   if (network_playing && !checkGameEnded())
11995     SendToServer_MovePlayer(summarized_player_action);
11996
11997   // summarize all actions at local players mapped input device position
11998   // (this allows using different input devices in single player mode)
11999   if (!network.enabled && !game.team_mode)
12000     stored_player[map_player_action[local_player->index_nr]].effective_action =
12001       summarized_player_action;
12002
12003   // summarize all actions at centered player in local team mode
12004   if (tape.recording &&
12005       setup.team_mode && !network.enabled &&
12006       setup.input_on_focus &&
12007       game.centered_player_nr != -1)
12008   {
12009     for (i = 0; i < MAX_PLAYERS; i++)
12010       stored_player[map_player_action[i]].effective_action =
12011         (i == game.centered_player_nr ? summarized_player_action : 0);
12012   }
12013
12014   if (recorded_player_action != NULL)
12015     for (i = 0; i < MAX_PLAYERS; i++)
12016       stored_player[i].effective_action = recorded_player_action[i];
12017
12018   for (i = 0; i < MAX_PLAYERS; i++)
12019   {
12020     tape_action[i] = stored_player[i].effective_action;
12021
12022     /* (this may happen in the RND game engine if a player was not present on
12023        the playfield on level start, but appeared later from a custom element */
12024     if (setup.team_mode &&
12025         tape.recording &&
12026         tape_action[i] &&
12027         !tape.player_participates[i])
12028       tape.player_participates[i] = TRUE;
12029   }
12030
12031   SetTapeActionFromMouseAction(tape_action,
12032                                &local_player->effective_mouse_action);
12033
12034   // only record actions from input devices, but not programmed actions
12035   if (tape.recording)
12036     TapeRecordAction(tape_action);
12037
12038   // remember if game was played (especially after tape stopped playing)
12039   if (!tape.playing && summarized_player_action && !checkGameFailed())
12040     game.GamePlayed = TRUE;
12041
12042 #if USE_NEW_PLAYER_ASSIGNMENTS
12043   // !!! also map player actions in single player mode !!!
12044   // if (game.team_mode)
12045   if (1)
12046   {
12047     byte mapped_action[MAX_PLAYERS];
12048
12049 #if DEBUG_PLAYER_ACTIONS
12050     for (i = 0; i < MAX_PLAYERS; i++)
12051       DebugContinued("", "%d, ", stored_player[i].effective_action);
12052 #endif
12053
12054     for (i = 0; i < MAX_PLAYERS; i++)
12055       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12056
12057     for (i = 0; i < MAX_PLAYERS; i++)
12058       stored_player[i].effective_action = mapped_action[i];
12059
12060 #if DEBUG_PLAYER_ACTIONS
12061     DebugContinued("", "=> ");
12062     for (i = 0; i < MAX_PLAYERS; i++)
12063       DebugContinued("", "%d, ", stored_player[i].effective_action);
12064     DebugContinued("game:playing:player", "\n");
12065 #endif
12066   }
12067 #if DEBUG_PLAYER_ACTIONS
12068   else
12069   {
12070     for (i = 0; i < MAX_PLAYERS; i++)
12071       DebugContinued("", "%d, ", stored_player[i].effective_action);
12072     DebugContinued("game:playing:player", "\n");
12073   }
12074 #endif
12075 #endif
12076
12077   for (i = 0; i < MAX_PLAYERS; i++)
12078   {
12079     // allow engine snapshot in case of changed movement attempt
12080     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12081         (stored_player[i].effective_action & KEY_MOTION))
12082       game.snapshot.changed_action = TRUE;
12083
12084     // allow engine snapshot in case of snapping/dropping attempt
12085     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12086         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12087       game.snapshot.changed_action = TRUE;
12088
12089     game.snapshot.last_action[i] = stored_player[i].effective_action;
12090   }
12091
12092   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12093   {
12094     GameActions_EM_Main();
12095   }
12096   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12097   {
12098     GameActions_SP_Main();
12099   }
12100   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12101   {
12102     GameActions_MM_Main();
12103   }
12104   else
12105   {
12106     GameActions_RND_Main();
12107   }
12108
12109   BlitScreenToBitmap(backbuffer);
12110
12111   CheckLevelSolved();
12112   CheckLevelTime();
12113
12114   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12115
12116   if (global.show_frames_per_second)
12117   {
12118     static unsigned int fps_counter = 0;
12119     static int fps_frames = 0;
12120     unsigned int fps_delay_ms = Counter() - fps_counter;
12121
12122     fps_frames++;
12123
12124     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12125     {
12126       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12127
12128       fps_frames = 0;
12129       fps_counter = Counter();
12130
12131       // always draw FPS to screen after FPS value was updated
12132       redraw_mask |= REDRAW_FPS;
12133     }
12134
12135     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12136     if (GetDrawDeactivationMask() == REDRAW_NONE)
12137       redraw_mask |= REDRAW_FPS;
12138   }
12139 }
12140
12141 static void GameActions_CheckSaveEngineSnapshot(void)
12142 {
12143   if (!game.snapshot.save_snapshot)
12144     return;
12145
12146   // clear flag for saving snapshot _before_ saving snapshot
12147   game.snapshot.save_snapshot = FALSE;
12148
12149   SaveEngineSnapshotToList();
12150 }
12151
12152 void GameActions(void)
12153 {
12154   GameActionsExt();
12155
12156   GameActions_CheckSaveEngineSnapshot();
12157 }
12158
12159 void GameActions_EM_Main(void)
12160 {
12161   byte effective_action[MAX_PLAYERS];
12162   int i;
12163
12164   for (i = 0; i < MAX_PLAYERS; i++)
12165     effective_action[i] = stored_player[i].effective_action;
12166
12167   GameActions_EM(effective_action);
12168 }
12169
12170 void GameActions_SP_Main(void)
12171 {
12172   byte effective_action[MAX_PLAYERS];
12173   int i;
12174
12175   for (i = 0; i < MAX_PLAYERS; i++)
12176     effective_action[i] = stored_player[i].effective_action;
12177
12178   GameActions_SP(effective_action);
12179
12180   for (i = 0; i < MAX_PLAYERS; i++)
12181   {
12182     if (stored_player[i].force_dropping)
12183       stored_player[i].action |= KEY_BUTTON_DROP;
12184
12185     stored_player[i].force_dropping = FALSE;
12186   }
12187 }
12188
12189 void GameActions_MM_Main(void)
12190 {
12191   AdvanceGfxFrame();
12192
12193   GameActions_MM(local_player->effective_mouse_action);
12194 }
12195
12196 void GameActions_RND_Main(void)
12197 {
12198   GameActions_RND();
12199 }
12200
12201 void GameActions_RND(void)
12202 {
12203   static struct MouseActionInfo mouse_action_last = { 0 };
12204   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12205   int magic_wall_x = 0, magic_wall_y = 0;
12206   int i, x, y, element, graphic, last_gfx_frame;
12207
12208   InitPlayfieldScanModeVars();
12209
12210   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12211   {
12212     SCAN_PLAYFIELD(x, y)
12213     {
12214       ChangeCount[x][y] = 0;
12215       ChangeEvent[x][y] = -1;
12216     }
12217   }
12218
12219   if (game.set_centered_player)
12220   {
12221     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12222
12223     // switching to "all players" only possible if all players fit to screen
12224     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12225     {
12226       game.centered_player_nr_next = game.centered_player_nr;
12227       game.set_centered_player = FALSE;
12228     }
12229
12230     // do not switch focus to non-existing (or non-active) player
12231     if (game.centered_player_nr_next >= 0 &&
12232         !stored_player[game.centered_player_nr_next].active)
12233     {
12234       game.centered_player_nr_next = game.centered_player_nr;
12235       game.set_centered_player = FALSE;
12236     }
12237   }
12238
12239   if (game.set_centered_player &&
12240       ScreenMovPos == 0)        // screen currently aligned at tile position
12241   {
12242     int sx, sy;
12243
12244     if (game.centered_player_nr_next == -1)
12245     {
12246       setScreenCenteredToAllPlayers(&sx, &sy);
12247     }
12248     else
12249     {
12250       sx = stored_player[game.centered_player_nr_next].jx;
12251       sy = stored_player[game.centered_player_nr_next].jy;
12252     }
12253
12254     game.centered_player_nr = game.centered_player_nr_next;
12255     game.set_centered_player = FALSE;
12256
12257     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12258     DrawGameDoorValues();
12259   }
12260
12261   // check single step mode (set flag and clear again if any player is active)
12262   game.enter_single_step_mode =
12263     (tape.single_step && tape.recording && !tape.pausing);
12264
12265   for (i = 0; i < MAX_PLAYERS; i++)
12266   {
12267     int actual_player_action = stored_player[i].effective_action;
12268
12269 #if 1
12270     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12271        - rnd_equinox_tetrachloride 048
12272        - rnd_equinox_tetrachloride_ii 096
12273        - rnd_emanuel_schmieg 002
12274        - doctor_sloan_ww 001, 020
12275     */
12276     if (stored_player[i].MovPos == 0)
12277       CheckGravityMovement(&stored_player[i]);
12278 #endif
12279
12280     // overwrite programmed action with tape action
12281     if (stored_player[i].programmed_action)
12282       actual_player_action = stored_player[i].programmed_action;
12283
12284     PlayerActions(&stored_player[i], actual_player_action);
12285
12286     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12287   }
12288
12289   // single step pause mode may already have been toggled by "ScrollPlayer()"
12290   if (game.enter_single_step_mode && !tape.pausing)
12291     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12292
12293   ScrollScreen(NULL, SCROLL_GO_ON);
12294
12295   /* for backwards compatibility, the following code emulates a fixed bug that
12296      occured when pushing elements (causing elements that just made their last
12297      pushing step to already (if possible) make their first falling step in the
12298      same game frame, which is bad); this code is also needed to use the famous
12299      "spring push bug" which is used in older levels and might be wanted to be
12300      used also in newer levels, but in this case the buggy pushing code is only
12301      affecting the "spring" element and no other elements */
12302
12303   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12304   {
12305     for (i = 0; i < MAX_PLAYERS; i++)
12306     {
12307       struct PlayerInfo *player = &stored_player[i];
12308       int x = player->jx;
12309       int y = player->jy;
12310
12311       if (player->active && player->is_pushing && player->is_moving &&
12312           IS_MOVING(x, y) &&
12313           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12314            Tile[x][y] == EL_SPRING))
12315       {
12316         ContinueMoving(x, y);
12317
12318         // continue moving after pushing (this is actually a bug)
12319         if (!IS_MOVING(x, y))
12320           Stop[x][y] = FALSE;
12321       }
12322     }
12323   }
12324
12325   SCAN_PLAYFIELD(x, y)
12326   {
12327     Last[x][y] = Tile[x][y];
12328
12329     ChangeCount[x][y] = 0;
12330     ChangeEvent[x][y] = -1;
12331
12332     // this must be handled before main playfield loop
12333     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12334     {
12335       MovDelay[x][y]--;
12336       if (MovDelay[x][y] <= 0)
12337         RemoveField(x, y);
12338     }
12339
12340     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12341     {
12342       MovDelay[x][y]--;
12343       if (MovDelay[x][y] <= 0)
12344       {
12345         int element = Store[x][y];
12346         int move_direction = MovDir[x][y];
12347         int player_index_bit = Store2[x][y];
12348
12349         Store[x][y] = 0;
12350         Store2[x][y] = 0;
12351
12352         RemoveField(x, y);
12353         TEST_DrawLevelField(x, y);
12354
12355         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12356
12357         if (IS_ENVELOPE(element))
12358           local_player->show_envelope = element;
12359       }
12360     }
12361
12362 #if DEBUG
12363     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12364     {
12365       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12366             x, y);
12367       Debug("game:playing:GameActions_RND", "This should never happen!");
12368
12369       ChangePage[x][y] = -1;
12370     }
12371 #endif
12372
12373     Stop[x][y] = FALSE;
12374     if (WasJustMoving[x][y] > 0)
12375       WasJustMoving[x][y]--;
12376     if (WasJustFalling[x][y] > 0)
12377       WasJustFalling[x][y]--;
12378     if (CheckCollision[x][y] > 0)
12379       CheckCollision[x][y]--;
12380     if (CheckImpact[x][y] > 0)
12381       CheckImpact[x][y]--;
12382
12383     GfxFrame[x][y]++;
12384
12385     /* reset finished pushing action (not done in ContinueMoving() to allow
12386        continuous pushing animation for elements with zero push delay) */
12387     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12388     {
12389       ResetGfxAnimation(x, y);
12390       TEST_DrawLevelField(x, y);
12391     }
12392
12393 #if DEBUG
12394     if (IS_BLOCKED(x, y))
12395     {
12396       int oldx, oldy;
12397
12398       Blocked2Moving(x, y, &oldx, &oldy);
12399       if (!IS_MOVING(oldx, oldy))
12400       {
12401         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12402         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12403         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12404         Debug("game:playing:GameActions_RND", "This should never happen!");
12405       }
12406     }
12407 #endif
12408   }
12409
12410   HandleMouseAction(&mouse_action, &mouse_action_last);
12411
12412   SCAN_PLAYFIELD(x, y)
12413   {
12414     element = Tile[x][y];
12415     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12416     last_gfx_frame = GfxFrame[x][y];
12417
12418     if (element == EL_EMPTY)
12419       graphic = el2img(GfxElementEmpty[x][y]);
12420
12421     ResetGfxFrame(x, y);
12422
12423     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12424       DrawLevelGraphicAnimation(x, y, graphic);
12425
12426     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12427         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12428       ResetRandomAnimationValue(x, y);
12429
12430     SetRandomAnimationValue(x, y);
12431
12432     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12433
12434     if (IS_INACTIVE(element))
12435     {
12436       if (IS_ANIMATED(graphic))
12437         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12438
12439       continue;
12440     }
12441
12442     // this may take place after moving, so 'element' may have changed
12443     if (IS_CHANGING(x, y) &&
12444         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12445     {
12446       int page = element_info[element].event_page_nr[CE_DELAY];
12447
12448       HandleElementChange(x, y, page);
12449
12450       element = Tile[x][y];
12451       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12452     }
12453
12454     CheckNextToConditions(x, y);
12455
12456     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12457     {
12458       StartMoving(x, y);
12459
12460       element = Tile[x][y];
12461       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12462
12463       if (IS_ANIMATED(graphic) &&
12464           !IS_MOVING(x, y) &&
12465           !Stop[x][y])
12466         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12467
12468       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12469         TEST_DrawTwinkleOnField(x, y);
12470     }
12471     else if (element == EL_ACID)
12472     {
12473       if (!Stop[x][y])
12474         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12475     }
12476     else if ((element == EL_EXIT_OPEN ||
12477               element == EL_EM_EXIT_OPEN ||
12478               element == EL_SP_EXIT_OPEN ||
12479               element == EL_STEEL_EXIT_OPEN ||
12480               element == EL_EM_STEEL_EXIT_OPEN ||
12481               element == EL_SP_TERMINAL ||
12482               element == EL_SP_TERMINAL_ACTIVE ||
12483               element == EL_EXTRA_TIME ||
12484               element == EL_SHIELD_NORMAL ||
12485               element == EL_SHIELD_DEADLY) &&
12486              IS_ANIMATED(graphic))
12487       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12488     else if (IS_MOVING(x, y))
12489       ContinueMoving(x, y);
12490     else if (IS_ACTIVE_BOMB(element))
12491       CheckDynamite(x, y);
12492     else if (element == EL_AMOEBA_GROWING)
12493       AmoebaGrowing(x, y);
12494     else if (element == EL_AMOEBA_SHRINKING)
12495       AmoebaShrinking(x, y);
12496
12497 #if !USE_NEW_AMOEBA_CODE
12498     else if (IS_AMOEBALIVE(element))
12499       AmoebaReproduce(x, y);
12500 #endif
12501
12502     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12503       Life(x, y);
12504     else if (element == EL_EXIT_CLOSED)
12505       CheckExit(x, y);
12506     else if (element == EL_EM_EXIT_CLOSED)
12507       CheckExitEM(x, y);
12508     else if (element == EL_STEEL_EXIT_CLOSED)
12509       CheckExitSteel(x, y);
12510     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12511       CheckExitSteelEM(x, y);
12512     else if (element == EL_SP_EXIT_CLOSED)
12513       CheckExitSP(x, y);
12514     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12515              element == EL_EXPANDABLE_STEELWALL_GROWING)
12516       WallGrowing(x, y);
12517     else if (element == EL_EXPANDABLE_WALL ||
12518              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12519              element == EL_EXPANDABLE_WALL_VERTICAL ||
12520              element == EL_EXPANDABLE_WALL_ANY ||
12521              element == EL_BD_EXPANDABLE_WALL ||
12522              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12523              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12524              element == EL_EXPANDABLE_STEELWALL_ANY)
12525       CheckWallGrowing(x, y);
12526     else if (element == EL_FLAMES)
12527       CheckForDragon(x, y);
12528     else if (element == EL_EXPLOSION)
12529       ; // drawing of correct explosion animation is handled separately
12530     else if (element == EL_ELEMENT_SNAPPING ||
12531              element == EL_DIAGONAL_SHRINKING ||
12532              element == EL_DIAGONAL_GROWING)
12533     {
12534       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12535
12536       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12537     }
12538     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12539       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12540
12541     if (IS_BELT_ACTIVE(element))
12542       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12543
12544     if (game.magic_wall_active)
12545     {
12546       int jx = local_player->jx, jy = local_player->jy;
12547
12548       // play the element sound at the position nearest to the player
12549       if ((element == EL_MAGIC_WALL_FULL ||
12550            element == EL_MAGIC_WALL_ACTIVE ||
12551            element == EL_MAGIC_WALL_EMPTYING ||
12552            element == EL_BD_MAGIC_WALL_FULL ||
12553            element == EL_BD_MAGIC_WALL_ACTIVE ||
12554            element == EL_BD_MAGIC_WALL_EMPTYING ||
12555            element == EL_DC_MAGIC_WALL_FULL ||
12556            element == EL_DC_MAGIC_WALL_ACTIVE ||
12557            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12558           ABS(x - jx) + ABS(y - jy) <
12559           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12560       {
12561         magic_wall_x = x;
12562         magic_wall_y = y;
12563       }
12564     }
12565   }
12566
12567 #if USE_NEW_AMOEBA_CODE
12568   // new experimental amoeba growth stuff
12569   if (!(FrameCounter % 8))
12570   {
12571     static unsigned int random = 1684108901;
12572
12573     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12574     {
12575       x = RND(lev_fieldx);
12576       y = RND(lev_fieldy);
12577       element = Tile[x][y];
12578
12579       if (!IS_PLAYER(x, y) &&
12580           (element == EL_EMPTY ||
12581            CAN_GROW_INTO(element) ||
12582            element == EL_QUICKSAND_EMPTY ||
12583            element == EL_QUICKSAND_FAST_EMPTY ||
12584            element == EL_ACID_SPLASH_LEFT ||
12585            element == EL_ACID_SPLASH_RIGHT))
12586       {
12587         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12588             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12589             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12590             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12591           Tile[x][y] = EL_AMOEBA_DROP;
12592       }
12593
12594       random = random * 129 + 1;
12595     }
12596   }
12597 #endif
12598
12599   game.explosions_delayed = FALSE;
12600
12601   SCAN_PLAYFIELD(x, y)
12602   {
12603     element = Tile[x][y];
12604
12605     if (ExplodeField[x][y])
12606       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12607     else if (element == EL_EXPLOSION)
12608       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12609
12610     ExplodeField[x][y] = EX_TYPE_NONE;
12611   }
12612
12613   game.explosions_delayed = TRUE;
12614
12615   if (game.magic_wall_active)
12616   {
12617     if (!(game.magic_wall_time_left % 4))
12618     {
12619       int element = Tile[magic_wall_x][magic_wall_y];
12620
12621       if (element == EL_BD_MAGIC_WALL_FULL ||
12622           element == EL_BD_MAGIC_WALL_ACTIVE ||
12623           element == EL_BD_MAGIC_WALL_EMPTYING)
12624         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12625       else if (element == EL_DC_MAGIC_WALL_FULL ||
12626                element == EL_DC_MAGIC_WALL_ACTIVE ||
12627                element == EL_DC_MAGIC_WALL_EMPTYING)
12628         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12629       else
12630         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12631     }
12632
12633     if (game.magic_wall_time_left > 0)
12634     {
12635       game.magic_wall_time_left--;
12636
12637       if (!game.magic_wall_time_left)
12638       {
12639         SCAN_PLAYFIELD(x, y)
12640         {
12641           element = Tile[x][y];
12642
12643           if (element == EL_MAGIC_WALL_ACTIVE ||
12644               element == EL_MAGIC_WALL_FULL)
12645           {
12646             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12647             TEST_DrawLevelField(x, y);
12648           }
12649           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12650                    element == EL_BD_MAGIC_WALL_FULL)
12651           {
12652             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12653             TEST_DrawLevelField(x, y);
12654           }
12655           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12656                    element == EL_DC_MAGIC_WALL_FULL)
12657           {
12658             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12659             TEST_DrawLevelField(x, y);
12660           }
12661         }
12662
12663         game.magic_wall_active = FALSE;
12664       }
12665     }
12666   }
12667
12668   if (game.light_time_left > 0)
12669   {
12670     game.light_time_left--;
12671
12672     if (game.light_time_left == 0)
12673       RedrawAllLightSwitchesAndInvisibleElements();
12674   }
12675
12676   if (game.timegate_time_left > 0)
12677   {
12678     game.timegate_time_left--;
12679
12680     if (game.timegate_time_left == 0)
12681       CloseAllOpenTimegates();
12682   }
12683
12684   if (game.lenses_time_left > 0)
12685   {
12686     game.lenses_time_left--;
12687
12688     if (game.lenses_time_left == 0)
12689       RedrawAllInvisibleElementsForLenses();
12690   }
12691
12692   if (game.magnify_time_left > 0)
12693   {
12694     game.magnify_time_left--;
12695
12696     if (game.magnify_time_left == 0)
12697       RedrawAllInvisibleElementsForMagnifier();
12698   }
12699
12700   for (i = 0; i < MAX_PLAYERS; i++)
12701   {
12702     struct PlayerInfo *player = &stored_player[i];
12703
12704     if (SHIELD_ON(player))
12705     {
12706       if (player->shield_deadly_time_left)
12707         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12708       else if (player->shield_normal_time_left)
12709         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12710     }
12711   }
12712
12713 #if USE_DELAYED_GFX_REDRAW
12714   SCAN_PLAYFIELD(x, y)
12715   {
12716     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12717     {
12718       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12719          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12720
12721       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12722         DrawLevelField(x, y);
12723
12724       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12725         DrawLevelFieldCrumbled(x, y);
12726
12727       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12728         DrawLevelFieldCrumbledNeighbours(x, y);
12729
12730       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12731         DrawTwinkleOnField(x, y);
12732     }
12733
12734     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12735   }
12736 #endif
12737
12738   DrawAllPlayers();
12739   PlayAllPlayersSound();
12740
12741   for (i = 0; i < MAX_PLAYERS; i++)
12742   {
12743     struct PlayerInfo *player = &stored_player[i];
12744
12745     if (player->show_envelope != 0 && (!player->active ||
12746                                        player->MovPos == 0))
12747     {
12748       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12749
12750       player->show_envelope = 0;
12751     }
12752   }
12753
12754   // use random number generator in every frame to make it less predictable
12755   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12756     RND(1);
12757
12758   mouse_action_last = mouse_action;
12759 }
12760
12761 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12762 {
12763   int min_x = x, min_y = y, max_x = x, max_y = y;
12764   int scr_fieldx = getScreenFieldSizeX();
12765   int scr_fieldy = getScreenFieldSizeY();
12766   int i;
12767
12768   for (i = 0; i < MAX_PLAYERS; i++)
12769   {
12770     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12771
12772     if (!stored_player[i].active || &stored_player[i] == player)
12773       continue;
12774
12775     min_x = MIN(min_x, jx);
12776     min_y = MIN(min_y, jy);
12777     max_x = MAX(max_x, jx);
12778     max_y = MAX(max_y, jy);
12779   }
12780
12781   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12782 }
12783
12784 static boolean AllPlayersInVisibleScreen(void)
12785 {
12786   int i;
12787
12788   for (i = 0; i < MAX_PLAYERS; i++)
12789   {
12790     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12791
12792     if (!stored_player[i].active)
12793       continue;
12794
12795     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12796       return FALSE;
12797   }
12798
12799   return TRUE;
12800 }
12801
12802 void ScrollLevel(int dx, int dy)
12803 {
12804   int scroll_offset = 2 * TILEX_VAR;
12805   int x, y;
12806
12807   BlitBitmap(drawto_field, drawto_field,
12808              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12809              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12810              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12811              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12812              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12813              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12814
12815   if (dx != 0)
12816   {
12817     x = (dx == 1 ? BX1 : BX2);
12818     for (y = BY1; y <= BY2; y++)
12819       DrawScreenField(x, y);
12820   }
12821
12822   if (dy != 0)
12823   {
12824     y = (dy == 1 ? BY1 : BY2);
12825     for (x = BX1; x <= BX2; x++)
12826       DrawScreenField(x, y);
12827   }
12828
12829   redraw_mask |= REDRAW_FIELD;
12830 }
12831
12832 static boolean canFallDown(struct PlayerInfo *player)
12833 {
12834   int jx = player->jx, jy = player->jy;
12835
12836   return (IN_LEV_FIELD(jx, jy + 1) &&
12837           (IS_FREE(jx, jy + 1) ||
12838            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12839           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12840           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12841 }
12842
12843 static boolean canPassField(int x, int y, int move_dir)
12844 {
12845   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12846   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12847   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12848   int nextx = x + dx;
12849   int nexty = y + dy;
12850   int element = Tile[x][y];
12851
12852   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12853           !CAN_MOVE(element) &&
12854           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12855           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12856           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12857 }
12858
12859 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12860 {
12861   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12862   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12863   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12864   int newx = x + dx;
12865   int newy = y + dy;
12866
12867   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12868           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12869           (IS_DIGGABLE(Tile[newx][newy]) ||
12870            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12871            canPassField(newx, newy, move_dir)));
12872 }
12873
12874 static void CheckGravityMovement(struct PlayerInfo *player)
12875 {
12876   if (player->gravity && !player->programmed_action)
12877   {
12878     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12879     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12880     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12881     int jx = player->jx, jy = player->jy;
12882     boolean player_is_moving_to_valid_field =
12883       (!player_is_snapping &&
12884        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12885         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12886     boolean player_can_fall_down = canFallDown(player);
12887
12888     if (player_can_fall_down &&
12889         !player_is_moving_to_valid_field)
12890       player->programmed_action = MV_DOWN;
12891   }
12892 }
12893
12894 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12895 {
12896   return CheckGravityMovement(player);
12897
12898   if (player->gravity && !player->programmed_action)
12899   {
12900     int jx = player->jx, jy = player->jy;
12901     boolean field_under_player_is_free =
12902       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12903     boolean player_is_standing_on_valid_field =
12904       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12905        (IS_WALKABLE(Tile[jx][jy]) &&
12906         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12907
12908     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12909       player->programmed_action = MV_DOWN;
12910   }
12911 }
12912
12913 /*
12914   MovePlayerOneStep()
12915   -----------------------------------------------------------------------------
12916   dx, dy:               direction (non-diagonal) to try to move the player to
12917   real_dx, real_dy:     direction as read from input device (can be diagonal)
12918 */
12919
12920 boolean MovePlayerOneStep(struct PlayerInfo *player,
12921                           int dx, int dy, int real_dx, int real_dy)
12922 {
12923   int jx = player->jx, jy = player->jy;
12924   int new_jx = jx + dx, new_jy = jy + dy;
12925   int can_move;
12926   boolean player_can_move = !player->cannot_move;
12927
12928   if (!player->active || (!dx && !dy))
12929     return MP_NO_ACTION;
12930
12931   player->MovDir = (dx < 0 ? MV_LEFT :
12932                     dx > 0 ? MV_RIGHT :
12933                     dy < 0 ? MV_UP :
12934                     dy > 0 ? MV_DOWN :  MV_NONE);
12935
12936   if (!IN_LEV_FIELD(new_jx, new_jy))
12937     return MP_NO_ACTION;
12938
12939   if (!player_can_move)
12940   {
12941     if (player->MovPos == 0)
12942     {
12943       player->is_moving = FALSE;
12944       player->is_digging = FALSE;
12945       player->is_collecting = FALSE;
12946       player->is_snapping = FALSE;
12947       player->is_pushing = FALSE;
12948     }
12949   }
12950
12951   if (!network.enabled && game.centered_player_nr == -1 &&
12952       !AllPlayersInSight(player, new_jx, new_jy))
12953     return MP_NO_ACTION;
12954
12955   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12956   if (can_move != MP_MOVING)
12957     return can_move;
12958
12959   // check if DigField() has caused relocation of the player
12960   if (player->jx != jx || player->jy != jy)
12961     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12962
12963   StorePlayer[jx][jy] = 0;
12964   player->last_jx = jx;
12965   player->last_jy = jy;
12966   player->jx = new_jx;
12967   player->jy = new_jy;
12968   StorePlayer[new_jx][new_jy] = player->element_nr;
12969
12970   if (player->move_delay_value_next != -1)
12971   {
12972     player->move_delay_value = player->move_delay_value_next;
12973     player->move_delay_value_next = -1;
12974   }
12975
12976   player->MovPos =
12977     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12978
12979   player->step_counter++;
12980
12981   PlayerVisit[jx][jy] = FrameCounter;
12982
12983   player->is_moving = TRUE;
12984
12985 #if 1
12986   // should better be called in MovePlayer(), but this breaks some tapes
12987   ScrollPlayer(player, SCROLL_INIT);
12988 #endif
12989
12990   return MP_MOVING;
12991 }
12992
12993 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12994 {
12995   int jx = player->jx, jy = player->jy;
12996   int old_jx = jx, old_jy = jy;
12997   int moved = MP_NO_ACTION;
12998
12999   if (!player->active)
13000     return FALSE;
13001
13002   if (!dx && !dy)
13003   {
13004     if (player->MovPos == 0)
13005     {
13006       player->is_moving = FALSE;
13007       player->is_digging = FALSE;
13008       player->is_collecting = FALSE;
13009       player->is_snapping = FALSE;
13010       player->is_pushing = FALSE;
13011     }
13012
13013     return FALSE;
13014   }
13015
13016   if (player->move_delay > 0)
13017     return FALSE;
13018
13019   player->move_delay = -1;              // set to "uninitialized" value
13020
13021   // store if player is automatically moved to next field
13022   player->is_auto_moving = (player->programmed_action != MV_NONE);
13023
13024   // remove the last programmed player action
13025   player->programmed_action = 0;
13026
13027   if (player->MovPos)
13028   {
13029     // should only happen if pre-1.2 tape recordings are played
13030     // this is only for backward compatibility
13031
13032     int original_move_delay_value = player->move_delay_value;
13033
13034 #if DEBUG
13035     Debug("game:playing:MovePlayer",
13036           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13037           tape.counter);
13038 #endif
13039
13040     // scroll remaining steps with finest movement resolution
13041     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13042
13043     while (player->MovPos)
13044     {
13045       ScrollPlayer(player, SCROLL_GO_ON);
13046       ScrollScreen(NULL, SCROLL_GO_ON);
13047
13048       AdvanceFrameAndPlayerCounters(player->index_nr);
13049
13050       DrawAllPlayers();
13051       BackToFront_WithFrameDelay(0);
13052     }
13053
13054     player->move_delay_value = original_move_delay_value;
13055   }
13056
13057   player->is_active = FALSE;
13058
13059   if (player->last_move_dir & MV_HORIZONTAL)
13060   {
13061     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13062       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13063   }
13064   else
13065   {
13066     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13067       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13068   }
13069
13070   if (!moved && !player->is_active)
13071   {
13072     player->is_moving = FALSE;
13073     player->is_digging = FALSE;
13074     player->is_collecting = FALSE;
13075     player->is_snapping = FALSE;
13076     player->is_pushing = FALSE;
13077   }
13078
13079   jx = player->jx;
13080   jy = player->jy;
13081
13082   if (moved & MP_MOVING && !ScreenMovPos &&
13083       (player->index_nr == game.centered_player_nr ||
13084        game.centered_player_nr == -1))
13085   {
13086     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13087
13088     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13089     {
13090       // actual player has left the screen -- scroll in that direction
13091       if (jx != old_jx)         // player has moved horizontally
13092         scroll_x += (jx - old_jx);
13093       else                      // player has moved vertically
13094         scroll_y += (jy - old_jy);
13095     }
13096     else
13097     {
13098       int offset_raw = game.scroll_delay_value;
13099
13100       if (jx != old_jx)         // player has moved horizontally
13101       {
13102         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13103         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13104         int new_scroll_x = jx - MIDPOSX + offset_x;
13105
13106         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13107             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13108           scroll_x = new_scroll_x;
13109
13110         // don't scroll over playfield boundaries
13111         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13112
13113         // don't scroll more than one field at a time
13114         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13115
13116         // don't scroll against the player's moving direction
13117         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13118             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13119           scroll_x = old_scroll_x;
13120       }
13121       else                      // player has moved vertically
13122       {
13123         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13124         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13125         int new_scroll_y = jy - MIDPOSY + offset_y;
13126
13127         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13128             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13129           scroll_y = new_scroll_y;
13130
13131         // don't scroll over playfield boundaries
13132         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13133
13134         // don't scroll more than one field at a time
13135         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13136
13137         // don't scroll against the player's moving direction
13138         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13139             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13140           scroll_y = old_scroll_y;
13141       }
13142     }
13143
13144     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13145     {
13146       if (!network.enabled && game.centered_player_nr == -1 &&
13147           !AllPlayersInVisibleScreen())
13148       {
13149         scroll_x = old_scroll_x;
13150         scroll_y = old_scroll_y;
13151       }
13152       else
13153       {
13154         ScrollScreen(player, SCROLL_INIT);
13155         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13156       }
13157     }
13158   }
13159
13160   player->StepFrame = 0;
13161
13162   if (moved & MP_MOVING)
13163   {
13164     if (old_jx != jx && old_jy == jy)
13165       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13166     else if (old_jx == jx && old_jy != jy)
13167       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13168
13169     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13170
13171     player->last_move_dir = player->MovDir;
13172     player->is_moving = TRUE;
13173     player->is_snapping = FALSE;
13174     player->is_switching = FALSE;
13175     player->is_dropping = FALSE;
13176     player->is_dropping_pressed = FALSE;
13177     player->drop_pressed_delay = 0;
13178
13179 #if 0
13180     // should better be called here than above, but this breaks some tapes
13181     ScrollPlayer(player, SCROLL_INIT);
13182 #endif
13183   }
13184   else
13185   {
13186     CheckGravityMovementWhenNotMoving(player);
13187
13188     player->is_moving = FALSE;
13189
13190     /* at this point, the player is allowed to move, but cannot move right now
13191        (e.g. because of something blocking the way) -- ensure that the player
13192        is also allowed to move in the next frame (in old versions before 3.1.1,
13193        the player was forced to wait again for eight frames before next try) */
13194
13195     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13196       player->move_delay = 0;   // allow direct movement in the next frame
13197   }
13198
13199   if (player->move_delay == -1)         // not yet initialized by DigField()
13200     player->move_delay = player->move_delay_value;
13201
13202   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13203   {
13204     TestIfPlayerTouchesBadThing(jx, jy);
13205     TestIfPlayerTouchesCustomElement(jx, jy);
13206   }
13207
13208   if (!player->active)
13209     RemovePlayer(player);
13210
13211   return moved;
13212 }
13213
13214 void ScrollPlayer(struct PlayerInfo *player, int mode)
13215 {
13216   int jx = player->jx, jy = player->jy;
13217   int last_jx = player->last_jx, last_jy = player->last_jy;
13218   int move_stepsize = TILEX / player->move_delay_value;
13219
13220   if (!player->active)
13221     return;
13222
13223   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13224     return;
13225
13226   if (mode == SCROLL_INIT)
13227   {
13228     player->actual_frame_counter.count = FrameCounter;
13229     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13230
13231     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13232         Tile[last_jx][last_jy] == EL_EMPTY)
13233     {
13234       int last_field_block_delay = 0;   // start with no blocking at all
13235       int block_delay_adjustment = player->block_delay_adjustment;
13236
13237       // if player blocks last field, add delay for exactly one move
13238       if (player->block_last_field)
13239       {
13240         last_field_block_delay += player->move_delay_value;
13241
13242         // when blocking enabled, prevent moving up despite gravity
13243         if (player->gravity && player->MovDir == MV_UP)
13244           block_delay_adjustment = -1;
13245       }
13246
13247       // add block delay adjustment (also possible when not blocking)
13248       last_field_block_delay += block_delay_adjustment;
13249
13250       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13251       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13252     }
13253
13254     if (player->MovPos != 0)    // player has not yet reached destination
13255       return;
13256   }
13257   else if (!FrameReached(&player->actual_frame_counter))
13258     return;
13259
13260   if (player->MovPos != 0)
13261   {
13262     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13263     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13264
13265     // before DrawPlayer() to draw correct player graphic for this case
13266     if (player->MovPos == 0)
13267       CheckGravityMovement(player);
13268   }
13269
13270   if (player->MovPos == 0)      // player reached destination field
13271   {
13272     if (player->move_delay_reset_counter > 0)
13273     {
13274       player->move_delay_reset_counter--;
13275
13276       if (player->move_delay_reset_counter == 0)
13277       {
13278         // continue with normal speed after quickly moving through gate
13279         HALVE_PLAYER_SPEED(player);
13280
13281         // be able to make the next move without delay
13282         player->move_delay = 0;
13283       }
13284     }
13285
13286     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13287         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13288         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13289         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13290         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13291         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13292         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13293         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13294     {
13295       ExitPlayer(player);
13296
13297       if (game.players_still_needed == 0 &&
13298           (game.friends_still_needed == 0 ||
13299            IS_SP_ELEMENT(Tile[jx][jy])))
13300         LevelSolved();
13301     }
13302
13303     player->last_jx = jx;
13304     player->last_jy = jy;
13305
13306     // this breaks one level: "machine", level 000
13307     {
13308       int move_direction = player->MovDir;
13309       int enter_side = MV_DIR_OPPOSITE(move_direction);
13310       int leave_side = move_direction;
13311       int old_jx = last_jx;
13312       int old_jy = last_jy;
13313       int old_element = Tile[old_jx][old_jy];
13314       int new_element = Tile[jx][jy];
13315
13316       if (IS_CUSTOM_ELEMENT(old_element))
13317         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13318                                    CE_LEFT_BY_PLAYER,
13319                                    player->index_bit, leave_side);
13320
13321       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13322                                           CE_PLAYER_LEAVES_X,
13323                                           player->index_bit, leave_side);
13324
13325       // needed because pushed element has not yet reached its destination,
13326       // so it would trigger a change event at its previous field location
13327       if (!player->is_pushing)
13328       {
13329         if (IS_CUSTOM_ELEMENT(new_element))
13330           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13331                                      player->index_bit, enter_side);
13332
13333         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13334                                             CE_PLAYER_ENTERS_X,
13335                                             player->index_bit, enter_side);
13336       }
13337
13338       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13339                                         CE_MOVE_OF_X, move_direction);
13340     }
13341
13342     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13343     {
13344       TestIfPlayerTouchesBadThing(jx, jy);
13345       TestIfPlayerTouchesCustomElement(jx, jy);
13346
13347       // needed because pushed element has not yet reached its destination,
13348       // so it would trigger a change event at its previous field location
13349       if (!player->is_pushing)
13350         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13351
13352       if (level.finish_dig_collect &&
13353           (player->is_digging || player->is_collecting))
13354       {
13355         int last_element = player->last_removed_element;
13356         int move_direction = player->MovDir;
13357         int enter_side = MV_DIR_OPPOSITE(move_direction);
13358         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13359                             CE_PLAYER_COLLECTS_X);
13360
13361         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13362                                             player->index_bit, enter_side);
13363
13364         player->last_removed_element = EL_UNDEFINED;
13365       }
13366
13367       if (!player->active)
13368         RemovePlayer(player);
13369     }
13370
13371     if (level.use_step_counter)
13372       CheckLevelTime_StepCounter();
13373
13374     if (tape.single_step && tape.recording && !tape.pausing &&
13375         !player->programmed_action)
13376       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13377
13378     if (!player->programmed_action)
13379       CheckSaveEngineSnapshot(player);
13380   }
13381 }
13382
13383 void ScrollScreen(struct PlayerInfo *player, int mode)
13384 {
13385   static DelayCounter screen_frame_counter = { 0 };
13386
13387   if (mode == SCROLL_INIT)
13388   {
13389     // set scrolling step size according to actual player's moving speed
13390     ScrollStepSize = TILEX / player->move_delay_value;
13391
13392     screen_frame_counter.count = FrameCounter;
13393     screen_frame_counter.value = 1;
13394
13395     ScreenMovDir = player->MovDir;
13396     ScreenMovPos = player->MovPos;
13397     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13398     return;
13399   }
13400   else if (!FrameReached(&screen_frame_counter))
13401     return;
13402
13403   if (ScreenMovPos)
13404   {
13405     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13406     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13407     redraw_mask |= REDRAW_FIELD;
13408   }
13409   else
13410     ScreenMovDir = MV_NONE;
13411 }
13412
13413 void CheckNextToConditions(int x, int y)
13414 {
13415   int element = Tile[x][y];
13416
13417   if (IS_PLAYER(x, y))
13418     TestIfPlayerNextToCustomElement(x, y);
13419
13420   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13421       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13422     TestIfElementNextToCustomElement(x, y);
13423 }
13424
13425 void TestIfPlayerNextToCustomElement(int x, int y)
13426 {
13427   struct XY *xy = xy_topdown;
13428   static int trigger_sides[4][2] =
13429   {
13430     // center side       border side
13431     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13432     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13433     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13434     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13435   };
13436   int i;
13437
13438   if (!IS_PLAYER(x, y))
13439     return;
13440
13441   struct PlayerInfo *player = PLAYERINFO(x, y);
13442
13443   if (player->is_moving)
13444     return;
13445
13446   for (i = 0; i < NUM_DIRECTIONS; i++)
13447   {
13448     int xx = x + xy[i].x;
13449     int yy = y + xy[i].y;
13450     int border_side = trigger_sides[i][1];
13451     int border_element;
13452
13453     if (!IN_LEV_FIELD(xx, yy))
13454       continue;
13455
13456     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13457       continue;         // center and border element not connected
13458
13459     border_element = Tile[xx][yy];
13460
13461     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13462                                player->index_bit, border_side);
13463     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13464                                         CE_PLAYER_NEXT_TO_X,
13465                                         player->index_bit, border_side);
13466
13467     /* use player element that is initially defined in the level playfield,
13468        not the player element that corresponds to the runtime player number
13469        (example: a level that contains EL_PLAYER_3 as the only player would
13470        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13471
13472     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13473                              CE_NEXT_TO_X, border_side);
13474   }
13475 }
13476
13477 void TestIfPlayerTouchesCustomElement(int x, int y)
13478 {
13479   struct XY *xy = xy_topdown;
13480   static int trigger_sides[4][2] =
13481   {
13482     // center side       border side
13483     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13484     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13485     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13486     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13487   };
13488   static int touch_dir[4] =
13489   {
13490     MV_LEFT | MV_RIGHT,
13491     MV_UP   | MV_DOWN,
13492     MV_UP   | MV_DOWN,
13493     MV_LEFT | MV_RIGHT
13494   };
13495   int center_element = Tile[x][y];      // should always be non-moving!
13496   int i;
13497
13498   for (i = 0; i < NUM_DIRECTIONS; i++)
13499   {
13500     int xx = x + xy[i].x;
13501     int yy = y + xy[i].y;
13502     int center_side = trigger_sides[i][0];
13503     int border_side = trigger_sides[i][1];
13504     int border_element;
13505
13506     if (!IN_LEV_FIELD(xx, yy))
13507       continue;
13508
13509     if (IS_PLAYER(x, y))                // player found at center element
13510     {
13511       struct PlayerInfo *player = PLAYERINFO(x, y);
13512
13513       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13514         border_element = Tile[xx][yy];          // may be moving!
13515       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13516         border_element = Tile[xx][yy];
13517       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13518         border_element = MovingOrBlocked2Element(xx, yy);
13519       else
13520         continue;               // center and border element do not touch
13521
13522       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13523                                  player->index_bit, border_side);
13524       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13525                                           CE_PLAYER_TOUCHES_X,
13526                                           player->index_bit, border_side);
13527
13528       {
13529         /* use player element that is initially defined in the level playfield,
13530            not the player element that corresponds to the runtime player number
13531            (example: a level that contains EL_PLAYER_3 as the only player would
13532            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13533         int player_element = PLAYERINFO(x, y)->initial_element;
13534
13535         // as element "X" is the player here, check opposite (center) side
13536         CheckElementChangeBySide(xx, yy, border_element, player_element,
13537                                  CE_TOUCHING_X, center_side);
13538       }
13539     }
13540     else if (IS_PLAYER(xx, yy))         // player found at border element
13541     {
13542       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13543
13544       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13545       {
13546         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13547           continue;             // center and border element do not touch
13548       }
13549
13550       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13551                                  player->index_bit, center_side);
13552       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13553                                           CE_PLAYER_TOUCHES_X,
13554                                           player->index_bit, center_side);
13555
13556       {
13557         /* use player element that is initially defined in the level playfield,
13558            not the player element that corresponds to the runtime player number
13559            (example: a level that contains EL_PLAYER_3 as the only player would
13560            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13561         int player_element = PLAYERINFO(xx, yy)->initial_element;
13562
13563         // as element "X" is the player here, check opposite (border) side
13564         CheckElementChangeBySide(x, y, center_element, player_element,
13565                                  CE_TOUCHING_X, border_side);
13566       }
13567
13568       break;
13569     }
13570   }
13571 }
13572
13573 void TestIfElementNextToCustomElement(int x, int y)
13574 {
13575   struct XY *xy = xy_topdown;
13576   static int trigger_sides[4][2] =
13577   {
13578     // center side      border side
13579     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13580     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13581     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13582     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13583   };
13584   int center_element = Tile[x][y];      // should always be non-moving!
13585   int i;
13586
13587   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13588     return;
13589
13590   for (i = 0; i < NUM_DIRECTIONS; i++)
13591   {
13592     int xx = x + xy[i].x;
13593     int yy = y + xy[i].y;
13594     int border_side = trigger_sides[i][1];
13595     int border_element;
13596
13597     if (!IN_LEV_FIELD(xx, yy))
13598       continue;
13599
13600     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13601       continue;                 // center and border element not connected
13602
13603     border_element = Tile[xx][yy];
13604
13605     // check for change of center element (but change it only once)
13606     if (CheckElementChangeBySide(x, y, center_element, border_element,
13607                                  CE_NEXT_TO_X, border_side))
13608       break;
13609   }
13610 }
13611
13612 void TestIfElementTouchesCustomElement(int x, int y)
13613 {
13614   struct XY *xy = xy_topdown;
13615   static int trigger_sides[4][2] =
13616   {
13617     // center side      border side
13618     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13619     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13620     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13621     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13622   };
13623   static int touch_dir[4] =
13624   {
13625     MV_LEFT | MV_RIGHT,
13626     MV_UP   | MV_DOWN,
13627     MV_UP   | MV_DOWN,
13628     MV_LEFT | MV_RIGHT
13629   };
13630   boolean change_center_element = FALSE;
13631   int center_element = Tile[x][y];      // should always be non-moving!
13632   int border_element_old[NUM_DIRECTIONS];
13633   int i;
13634
13635   for (i = 0; i < NUM_DIRECTIONS; i++)
13636   {
13637     int xx = x + xy[i].x;
13638     int yy = y + xy[i].y;
13639     int border_element;
13640
13641     border_element_old[i] = -1;
13642
13643     if (!IN_LEV_FIELD(xx, yy))
13644       continue;
13645
13646     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13647       border_element = Tile[xx][yy];    // may be moving!
13648     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13649       border_element = Tile[xx][yy];
13650     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13651       border_element = MovingOrBlocked2Element(xx, yy);
13652     else
13653       continue;                 // center and border element do not touch
13654
13655     border_element_old[i] = border_element;
13656   }
13657
13658   for (i = 0; i < NUM_DIRECTIONS; i++)
13659   {
13660     int xx = x + xy[i].x;
13661     int yy = y + xy[i].y;
13662     int center_side = trigger_sides[i][0];
13663     int border_element = border_element_old[i];
13664
13665     if (border_element == -1)
13666       continue;
13667
13668     // check for change of border element
13669     CheckElementChangeBySide(xx, yy, border_element, center_element,
13670                              CE_TOUCHING_X, center_side);
13671
13672     // (center element cannot be player, so we don't have to check this here)
13673   }
13674
13675   for (i = 0; i < NUM_DIRECTIONS; i++)
13676   {
13677     int xx = x + xy[i].x;
13678     int yy = y + xy[i].y;
13679     int border_side = trigger_sides[i][1];
13680     int border_element = border_element_old[i];
13681
13682     if (border_element == -1)
13683       continue;
13684
13685     // check for change of center element (but change it only once)
13686     if (!change_center_element)
13687       change_center_element =
13688         CheckElementChangeBySide(x, y, center_element, border_element,
13689                                  CE_TOUCHING_X, border_side);
13690
13691     if (IS_PLAYER(xx, yy))
13692     {
13693       /* use player element that is initially defined in the level playfield,
13694          not the player element that corresponds to the runtime player number
13695          (example: a level that contains EL_PLAYER_3 as the only player would
13696          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13697       int player_element = PLAYERINFO(xx, yy)->initial_element;
13698
13699       // as element "X" is the player here, check opposite (border) side
13700       CheckElementChangeBySide(x, y, center_element, player_element,
13701                                CE_TOUCHING_X, border_side);
13702     }
13703   }
13704 }
13705
13706 void TestIfElementHitsCustomElement(int x, int y, int direction)
13707 {
13708   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13709   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13710   int hitx = x + dx, hity = y + dy;
13711   int hitting_element = Tile[x][y];
13712   int touched_element;
13713
13714   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13715     return;
13716
13717   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13718                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13719
13720   if (IN_LEV_FIELD(hitx, hity))
13721   {
13722     int opposite_direction = MV_DIR_OPPOSITE(direction);
13723     int hitting_side = direction;
13724     int touched_side = opposite_direction;
13725     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13726                           MovDir[hitx][hity] != direction ||
13727                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13728
13729     object_hit = TRUE;
13730
13731     if (object_hit)
13732     {
13733       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13734                                CE_HITTING_X, touched_side);
13735
13736       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13737                                CE_HIT_BY_X, hitting_side);
13738
13739       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13740                                CE_HIT_BY_SOMETHING, opposite_direction);
13741
13742       if (IS_PLAYER(hitx, hity))
13743       {
13744         /* use player element that is initially defined in the level playfield,
13745            not the player element that corresponds to the runtime player number
13746            (example: a level that contains EL_PLAYER_3 as the only player would
13747            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13748         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13749
13750         CheckElementChangeBySide(x, y, hitting_element, player_element,
13751                                  CE_HITTING_X, touched_side);
13752       }
13753     }
13754   }
13755
13756   // "hitting something" is also true when hitting the playfield border
13757   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13758                            CE_HITTING_SOMETHING, direction);
13759 }
13760
13761 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13762 {
13763   int i, kill_x = -1, kill_y = -1;
13764
13765   int bad_element = -1;
13766   struct XY *test_xy = xy_topdown;
13767   static int test_dir[4] =
13768   {
13769     MV_UP,
13770     MV_LEFT,
13771     MV_RIGHT,
13772     MV_DOWN
13773   };
13774
13775   for (i = 0; i < NUM_DIRECTIONS; i++)
13776   {
13777     int test_x, test_y, test_move_dir, test_element;
13778
13779     test_x = good_x + test_xy[i].x;
13780     test_y = good_y + test_xy[i].y;
13781
13782     if (!IN_LEV_FIELD(test_x, test_y))
13783       continue;
13784
13785     test_move_dir =
13786       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13787
13788     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13789
13790     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13791        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13792     */
13793     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13794         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13795     {
13796       kill_x = test_x;
13797       kill_y = test_y;
13798       bad_element = test_element;
13799
13800       break;
13801     }
13802   }
13803
13804   if (kill_x != -1 || kill_y != -1)
13805   {
13806     if (IS_PLAYER(good_x, good_y))
13807     {
13808       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13809
13810       if (player->shield_deadly_time_left > 0 &&
13811           !IS_INDESTRUCTIBLE(bad_element))
13812         Bang(kill_x, kill_y);
13813       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13814         KillPlayer(player);
13815     }
13816     else
13817       Bang(good_x, good_y);
13818   }
13819 }
13820
13821 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13822 {
13823   int i, kill_x = -1, kill_y = -1;
13824   int bad_element = Tile[bad_x][bad_y];
13825   struct XY *test_xy = xy_topdown;
13826   static int touch_dir[4] =
13827   {
13828     MV_LEFT | MV_RIGHT,
13829     MV_UP   | MV_DOWN,
13830     MV_UP   | MV_DOWN,
13831     MV_LEFT | MV_RIGHT
13832   };
13833   static int test_dir[4] =
13834   {
13835     MV_UP,
13836     MV_LEFT,
13837     MV_RIGHT,
13838     MV_DOWN
13839   };
13840
13841   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13842     return;
13843
13844   for (i = 0; i < NUM_DIRECTIONS; i++)
13845   {
13846     int test_x, test_y, test_move_dir, test_element;
13847
13848     test_x = bad_x + test_xy[i].x;
13849     test_y = bad_y + test_xy[i].y;
13850
13851     if (!IN_LEV_FIELD(test_x, test_y))
13852       continue;
13853
13854     test_move_dir =
13855       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13856
13857     test_element = Tile[test_x][test_y];
13858
13859     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13860        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13861     */
13862     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13863         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13864     {
13865       // good thing is player or penguin that does not move away
13866       if (IS_PLAYER(test_x, test_y))
13867       {
13868         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13869
13870         if (bad_element == EL_ROBOT && player->is_moving)
13871           continue;     // robot does not kill player if he is moving
13872
13873         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13874         {
13875           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13876             continue;           // center and border element do not touch
13877         }
13878
13879         kill_x = test_x;
13880         kill_y = test_y;
13881
13882         break;
13883       }
13884       else if (test_element == EL_PENGUIN)
13885       {
13886         kill_x = test_x;
13887         kill_y = test_y;
13888
13889         break;
13890       }
13891     }
13892   }
13893
13894   if (kill_x != -1 || kill_y != -1)
13895   {
13896     if (IS_PLAYER(kill_x, kill_y))
13897     {
13898       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13899
13900       if (player->shield_deadly_time_left > 0 &&
13901           !IS_INDESTRUCTIBLE(bad_element))
13902         Bang(bad_x, bad_y);
13903       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13904         KillPlayer(player);
13905     }
13906     else
13907       Bang(kill_x, kill_y);
13908   }
13909 }
13910
13911 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13912 {
13913   int bad_element = Tile[bad_x][bad_y];
13914   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13915   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13916   int test_x = bad_x + dx, test_y = bad_y + dy;
13917   int test_move_dir, test_element;
13918   int kill_x = -1, kill_y = -1;
13919
13920   if (!IN_LEV_FIELD(test_x, test_y))
13921     return;
13922
13923   test_move_dir =
13924     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13925
13926   test_element = Tile[test_x][test_y];
13927
13928   if (test_move_dir != bad_move_dir)
13929   {
13930     // good thing can be player or penguin that does not move away
13931     if (IS_PLAYER(test_x, test_y))
13932     {
13933       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13934
13935       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13936          player as being hit when he is moving towards the bad thing, because
13937          the "get hit by" condition would be lost after the player stops) */
13938       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13939         return;         // player moves away from bad thing
13940
13941       kill_x = test_x;
13942       kill_y = test_y;
13943     }
13944     else if (test_element == EL_PENGUIN)
13945     {
13946       kill_x = test_x;
13947       kill_y = test_y;
13948     }
13949   }
13950
13951   if (kill_x != -1 || kill_y != -1)
13952   {
13953     if (IS_PLAYER(kill_x, kill_y))
13954     {
13955       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13956
13957       if (player->shield_deadly_time_left > 0 &&
13958           !IS_INDESTRUCTIBLE(bad_element))
13959         Bang(bad_x, bad_y);
13960       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13961         KillPlayer(player);
13962     }
13963     else
13964       Bang(kill_x, kill_y);
13965   }
13966 }
13967
13968 void TestIfPlayerTouchesBadThing(int x, int y)
13969 {
13970   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13971 }
13972
13973 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13974 {
13975   TestIfGoodThingHitsBadThing(x, y, move_dir);
13976 }
13977
13978 void TestIfBadThingTouchesPlayer(int x, int y)
13979 {
13980   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13981 }
13982
13983 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13984 {
13985   TestIfBadThingHitsGoodThing(x, y, move_dir);
13986 }
13987
13988 void TestIfFriendTouchesBadThing(int x, int y)
13989 {
13990   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13991 }
13992
13993 void TestIfBadThingTouchesFriend(int x, int y)
13994 {
13995   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13996 }
13997
13998 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13999 {
14000   int i, kill_x = bad_x, kill_y = bad_y;
14001   struct XY *xy = xy_topdown;
14002
14003   for (i = 0; i < NUM_DIRECTIONS; i++)
14004   {
14005     int x, y, element;
14006
14007     x = bad_x + xy[i].x;
14008     y = bad_y + xy[i].y;
14009     if (!IN_LEV_FIELD(x, y))
14010       continue;
14011
14012     element = Tile[x][y];
14013     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14014         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14015     {
14016       kill_x = x;
14017       kill_y = y;
14018       break;
14019     }
14020   }
14021
14022   if (kill_x != bad_x || kill_y != bad_y)
14023     Bang(bad_x, bad_y);
14024 }
14025
14026 void KillPlayer(struct PlayerInfo *player)
14027 {
14028   int jx = player->jx, jy = player->jy;
14029
14030   if (!player->active)
14031     return;
14032
14033 #if 0
14034   Debug("game:playing:KillPlayer",
14035         "0: killed == %d, active == %d, reanimated == %d",
14036         player->killed, player->active, player->reanimated);
14037 #endif
14038
14039   /* the following code was introduced to prevent an infinite loop when calling
14040      -> Bang()
14041      -> CheckTriggeredElementChangeExt()
14042      -> ExecuteCustomElementAction()
14043      -> KillPlayer()
14044      -> (infinitely repeating the above sequence of function calls)
14045      which occurs when killing the player while having a CE with the setting
14046      "kill player X when explosion of <player X>"; the solution using a new
14047      field "player->killed" was chosen for backwards compatibility, although
14048      clever use of the fields "player->active" etc. would probably also work */
14049 #if 1
14050   if (player->killed)
14051     return;
14052 #endif
14053
14054   player->killed = TRUE;
14055
14056   // remove accessible field at the player's position
14057   RemoveField(jx, jy);
14058
14059   // deactivate shield (else Bang()/Explode() would not work right)
14060   player->shield_normal_time_left = 0;
14061   player->shield_deadly_time_left = 0;
14062
14063 #if 0
14064   Debug("game:playing:KillPlayer",
14065         "1: killed == %d, active == %d, reanimated == %d",
14066         player->killed, player->active, player->reanimated);
14067 #endif
14068
14069   Bang(jx, jy);
14070
14071 #if 0
14072   Debug("game:playing:KillPlayer",
14073         "2: killed == %d, active == %d, reanimated == %d",
14074         player->killed, player->active, player->reanimated);
14075 #endif
14076
14077   if (player->reanimated)       // killed player may have been reanimated
14078     player->killed = player->reanimated = FALSE;
14079   else
14080     BuryPlayer(player);
14081 }
14082
14083 static void KillPlayerUnlessEnemyProtected(int x, int y)
14084 {
14085   if (!PLAYER_ENEMY_PROTECTED(x, y))
14086     KillPlayer(PLAYERINFO(x, y));
14087 }
14088
14089 static void KillPlayerUnlessExplosionProtected(int x, int y)
14090 {
14091   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14092     KillPlayer(PLAYERINFO(x, y));
14093 }
14094
14095 void BuryPlayer(struct PlayerInfo *player)
14096 {
14097   int jx = player->jx, jy = player->jy;
14098
14099   if (!player->active)
14100     return;
14101
14102   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14103
14104   RemovePlayer(player);
14105
14106   player->buried = TRUE;
14107
14108   if (game.all_players_gone)
14109     game.GameOver = TRUE;
14110 }
14111
14112 void RemovePlayer(struct PlayerInfo *player)
14113 {
14114   int jx = player->jx, jy = player->jy;
14115   int i, found = FALSE;
14116
14117   player->present = FALSE;
14118   player->active = FALSE;
14119
14120   // required for some CE actions (even if the player is not active anymore)
14121   player->MovPos = 0;
14122
14123   if (!ExplodeField[jx][jy])
14124     StorePlayer[jx][jy] = 0;
14125
14126   if (player->is_moving)
14127     TEST_DrawLevelField(player->last_jx, player->last_jy);
14128
14129   for (i = 0; i < MAX_PLAYERS; i++)
14130     if (stored_player[i].active)
14131       found = TRUE;
14132
14133   if (!found)
14134   {
14135     game.all_players_gone = TRUE;
14136     game.GameOver = TRUE;
14137   }
14138
14139   game.exit_x = game.robot_wheel_x = jx;
14140   game.exit_y = game.robot_wheel_y = jy;
14141 }
14142
14143 void ExitPlayer(struct PlayerInfo *player)
14144 {
14145   DrawPlayer(player);   // needed here only to cleanup last field
14146   RemovePlayer(player);
14147
14148   if (game.players_still_needed > 0)
14149     game.players_still_needed--;
14150 }
14151
14152 static void SetFieldForSnapping(int x, int y, int element, int direction,
14153                                 int player_index_bit)
14154 {
14155   struct ElementInfo *ei = &element_info[element];
14156   int direction_bit = MV_DIR_TO_BIT(direction);
14157   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14158   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14159                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14160
14161   Tile[x][y] = EL_ELEMENT_SNAPPING;
14162   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14163   MovDir[x][y] = direction;
14164   Store[x][y] = element;
14165   Store2[x][y] = player_index_bit;
14166
14167   ResetGfxAnimation(x, y);
14168
14169   GfxElement[x][y] = element;
14170   GfxAction[x][y] = action;
14171   GfxDir[x][y] = direction;
14172   GfxFrame[x][y] = -1;
14173 }
14174
14175 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14176                                    int player_index_bit)
14177 {
14178   TestIfElementTouchesCustomElement(x, y);      // for empty space
14179
14180   if (level.finish_dig_collect)
14181   {
14182     int dig_side = MV_DIR_OPPOSITE(direction);
14183     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14184                         CE_PLAYER_COLLECTS_X);
14185
14186     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14187                                         player_index_bit, dig_side);
14188     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14189                                         player_index_bit, dig_side);
14190   }
14191 }
14192
14193 /*
14194   =============================================================================
14195   checkDiagonalPushing()
14196   -----------------------------------------------------------------------------
14197   check if diagonal input device direction results in pushing of object
14198   (by checking if the alternative direction is walkable, diggable, ...)
14199   =============================================================================
14200 */
14201
14202 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14203                                     int x, int y, int real_dx, int real_dy)
14204 {
14205   int jx, jy, dx, dy, xx, yy;
14206
14207   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14208     return TRUE;
14209
14210   // diagonal direction: check alternative direction
14211   jx = player->jx;
14212   jy = player->jy;
14213   dx = x - jx;
14214   dy = y - jy;
14215   xx = jx + (dx == 0 ? real_dx : 0);
14216   yy = jy + (dy == 0 ? real_dy : 0);
14217
14218   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14219 }
14220
14221 /*
14222   =============================================================================
14223   DigField()
14224   -----------------------------------------------------------------------------
14225   x, y:                 field next to player (non-diagonal) to try to dig to
14226   real_dx, real_dy:     direction as read from input device (can be diagonal)
14227   =============================================================================
14228 */
14229
14230 static int DigField(struct PlayerInfo *player,
14231                     int oldx, int oldy, int x, int y,
14232                     int real_dx, int real_dy, int mode)
14233 {
14234   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14235   boolean player_was_pushing = player->is_pushing;
14236   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14237   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14238   int jx = oldx, jy = oldy;
14239   int dx = x - jx, dy = y - jy;
14240   int nextx = x + dx, nexty = y + dy;
14241   int move_direction = (dx == -1 ? MV_LEFT  :
14242                         dx == +1 ? MV_RIGHT :
14243                         dy == -1 ? MV_UP    :
14244                         dy == +1 ? MV_DOWN  : MV_NONE);
14245   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14246   int dig_side = MV_DIR_OPPOSITE(move_direction);
14247   int old_element = Tile[jx][jy];
14248   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14249   int collect_count;
14250
14251   if (is_player)                // function can also be called by EL_PENGUIN
14252   {
14253     if (player->MovPos == 0)
14254     {
14255       player->is_digging = FALSE;
14256       player->is_collecting = FALSE;
14257     }
14258
14259     if (player->MovPos == 0)    // last pushing move finished
14260       player->is_pushing = FALSE;
14261
14262     if (mode == DF_NO_PUSH)     // player just stopped pushing
14263     {
14264       player->is_switching = FALSE;
14265       player->push_delay = -1;
14266
14267       return MP_NO_ACTION;
14268     }
14269   }
14270   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14271     old_element = Back[jx][jy];
14272
14273   // in case of element dropped at player position, check background
14274   else if (Back[jx][jy] != EL_EMPTY &&
14275            game.engine_version >= VERSION_IDENT(2,2,0,0))
14276     old_element = Back[jx][jy];
14277
14278   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14279     return MP_NO_ACTION;        // field has no opening in this direction
14280
14281   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14282     return MP_NO_ACTION;        // field has no opening in this direction
14283
14284   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14285   {
14286     SplashAcid(x, y);
14287
14288     Tile[jx][jy] = player->artwork_element;
14289     InitMovingField(jx, jy, MV_DOWN);
14290     Store[jx][jy] = EL_ACID;
14291     ContinueMoving(jx, jy);
14292     BuryPlayer(player);
14293
14294     return MP_DONT_RUN_INTO;
14295   }
14296
14297   if (player_can_move && DONT_RUN_INTO(element))
14298   {
14299     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14300
14301     return MP_DONT_RUN_INTO;
14302   }
14303
14304   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14305     return MP_NO_ACTION;
14306
14307   collect_count = element_info[element].collect_count_initial;
14308
14309   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14310     return MP_NO_ACTION;
14311
14312   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14313     player_can_move = player_can_move_or_snap;
14314
14315   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14316       game.engine_version >= VERSION_IDENT(2,2,0,0))
14317   {
14318     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14319                                player->index_bit, dig_side);
14320     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14321                                         player->index_bit, dig_side);
14322
14323     if (element == EL_DC_LANDMINE)
14324       Bang(x, y);
14325
14326     if (Tile[x][y] != element)          // field changed by snapping
14327       return MP_ACTION;
14328
14329     return MP_NO_ACTION;
14330   }
14331
14332   if (player->gravity && is_player && !player->is_auto_moving &&
14333       canFallDown(player) && move_direction != MV_DOWN &&
14334       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14335     return MP_NO_ACTION;        // player cannot walk here due to gravity
14336
14337   if (player_can_move &&
14338       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14339   {
14340     int sound_element = SND_ELEMENT(element);
14341     int sound_action = ACTION_WALKING;
14342
14343     if (IS_RND_GATE(element))
14344     {
14345       if (!player->key[RND_GATE_NR(element)])
14346         return MP_NO_ACTION;
14347     }
14348     else if (IS_RND_GATE_GRAY(element))
14349     {
14350       if (!player->key[RND_GATE_GRAY_NR(element)])
14351         return MP_NO_ACTION;
14352     }
14353     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14354     {
14355       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14356         return MP_NO_ACTION;
14357     }
14358     else if (element == EL_EXIT_OPEN ||
14359              element == EL_EM_EXIT_OPEN ||
14360              element == EL_EM_EXIT_OPENING ||
14361              element == EL_STEEL_EXIT_OPEN ||
14362              element == EL_EM_STEEL_EXIT_OPEN ||
14363              element == EL_EM_STEEL_EXIT_OPENING ||
14364              element == EL_SP_EXIT_OPEN ||
14365              element == EL_SP_EXIT_OPENING)
14366     {
14367       sound_action = ACTION_PASSING;    // player is passing exit
14368     }
14369     else if (element == EL_EMPTY)
14370     {
14371       sound_action = ACTION_MOVING;             // nothing to walk on
14372     }
14373
14374     // play sound from background or player, whatever is available
14375     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14376       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14377     else
14378       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14379   }
14380   else if (player_can_move &&
14381            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14382   {
14383     if (!ACCESS_FROM(element, opposite_direction))
14384       return MP_NO_ACTION;      // field not accessible from this direction
14385
14386     if (CAN_MOVE(element))      // only fixed elements can be passed!
14387       return MP_NO_ACTION;
14388
14389     if (IS_EM_GATE(element))
14390     {
14391       if (!player->key[EM_GATE_NR(element)])
14392         return MP_NO_ACTION;
14393     }
14394     else if (IS_EM_GATE_GRAY(element))
14395     {
14396       if (!player->key[EM_GATE_GRAY_NR(element)])
14397         return MP_NO_ACTION;
14398     }
14399     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14400     {
14401       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14402         return MP_NO_ACTION;
14403     }
14404     else if (IS_EMC_GATE(element))
14405     {
14406       if (!player->key[EMC_GATE_NR(element)])
14407         return MP_NO_ACTION;
14408     }
14409     else if (IS_EMC_GATE_GRAY(element))
14410     {
14411       if (!player->key[EMC_GATE_GRAY_NR(element)])
14412         return MP_NO_ACTION;
14413     }
14414     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14415     {
14416       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14417         return MP_NO_ACTION;
14418     }
14419     else if (element == EL_DC_GATE_WHITE ||
14420              element == EL_DC_GATE_WHITE_GRAY ||
14421              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14422     {
14423       if (player->num_white_keys == 0)
14424         return MP_NO_ACTION;
14425
14426       player->num_white_keys--;
14427     }
14428     else if (IS_SP_PORT(element))
14429     {
14430       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14431           element == EL_SP_GRAVITY_PORT_RIGHT ||
14432           element == EL_SP_GRAVITY_PORT_UP ||
14433           element == EL_SP_GRAVITY_PORT_DOWN)
14434         player->gravity = !player->gravity;
14435       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14436                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14437                element == EL_SP_GRAVITY_ON_PORT_UP ||
14438                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14439         player->gravity = TRUE;
14440       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14441                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14442                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14443                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14444         player->gravity = FALSE;
14445     }
14446
14447     // automatically move to the next field with double speed
14448     player->programmed_action = move_direction;
14449
14450     if (player->move_delay_reset_counter == 0)
14451     {
14452       player->move_delay_reset_counter = 2;     // two double speed steps
14453
14454       DOUBLE_PLAYER_SPEED(player);
14455     }
14456
14457     PlayLevelSoundAction(x, y, ACTION_PASSING);
14458   }
14459   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14460   {
14461     RemoveField(x, y);
14462
14463     if (mode != DF_SNAP)
14464     {
14465       GfxElement[x][y] = GFX_ELEMENT(element);
14466       player->is_digging = TRUE;
14467     }
14468
14469     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14470
14471     // use old behaviour for old levels (digging)
14472     if (!level.finish_dig_collect)
14473     {
14474       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14475                                           player->index_bit, dig_side);
14476
14477       // if digging triggered player relocation, finish digging tile
14478       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14479         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14480     }
14481
14482     if (mode == DF_SNAP)
14483     {
14484       if (level.block_snap_field)
14485         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14486       else
14487         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14488
14489       // use old behaviour for old levels (snapping)
14490       if (!level.finish_dig_collect)
14491         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14492                                             player->index_bit, dig_side);
14493     }
14494   }
14495   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14496   {
14497     RemoveField(x, y);
14498
14499     if (is_player && mode != DF_SNAP)
14500     {
14501       GfxElement[x][y] = element;
14502       player->is_collecting = TRUE;
14503     }
14504
14505     if (element == EL_SPEED_PILL)
14506     {
14507       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14508     }
14509     else if (element == EL_EXTRA_TIME && level.time > 0)
14510     {
14511       TimeLeft += level.extra_time;
14512
14513       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14514
14515       DisplayGameControlValues();
14516     }
14517     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14518     {
14519       int shield_time = (element == EL_SHIELD_DEADLY ?
14520                          level.shield_deadly_time :
14521                          level.shield_normal_time);
14522
14523       player->shield_normal_time_left += shield_time;
14524       if (element == EL_SHIELD_DEADLY)
14525         player->shield_deadly_time_left += shield_time;
14526     }
14527     else if (element == EL_DYNAMITE ||
14528              element == EL_EM_DYNAMITE ||
14529              element == EL_SP_DISK_RED)
14530     {
14531       if (player->inventory_size < MAX_INVENTORY_SIZE)
14532         player->inventory_element[player->inventory_size++] = element;
14533
14534       DrawGameDoorValues();
14535     }
14536     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14537     {
14538       player->dynabomb_count++;
14539       player->dynabombs_left++;
14540     }
14541     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14542     {
14543       player->dynabomb_size++;
14544     }
14545     else if (element == EL_DYNABOMB_INCREASE_POWER)
14546     {
14547       player->dynabomb_xl = TRUE;
14548     }
14549     else if (IS_KEY(element))
14550     {
14551       player->key[KEY_NR(element)] = TRUE;
14552
14553       DrawGameDoorValues();
14554     }
14555     else if (element == EL_DC_KEY_WHITE)
14556     {
14557       player->num_white_keys++;
14558
14559       // display white keys?
14560       // DrawGameDoorValues();
14561     }
14562     else if (IS_ENVELOPE(element))
14563     {
14564       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14565
14566       if (!wait_for_snapping)
14567         player->show_envelope = element;
14568     }
14569     else if (element == EL_EMC_LENSES)
14570     {
14571       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14572
14573       RedrawAllInvisibleElementsForLenses();
14574     }
14575     else if (element == EL_EMC_MAGNIFIER)
14576     {
14577       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14578
14579       RedrawAllInvisibleElementsForMagnifier();
14580     }
14581     else if (IS_DROPPABLE(element) ||
14582              IS_THROWABLE(element))     // can be collected and dropped
14583     {
14584       int i;
14585
14586       if (collect_count == 0)
14587         player->inventory_infinite_element = element;
14588       else
14589         for (i = 0; i < collect_count; i++)
14590           if (player->inventory_size < MAX_INVENTORY_SIZE)
14591             player->inventory_element[player->inventory_size++] = element;
14592
14593       DrawGameDoorValues();
14594     }
14595     else if (collect_count > 0)
14596     {
14597       game.gems_still_needed -= collect_count;
14598       if (game.gems_still_needed < 0)
14599         game.gems_still_needed = 0;
14600
14601       game.snapshot.collected_item = TRUE;
14602
14603       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14604
14605       DisplayGameControlValues();
14606     }
14607
14608     RaiseScoreElement(element);
14609     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14610
14611     // use old behaviour for old levels (collecting)
14612     if (!level.finish_dig_collect && is_player)
14613     {
14614       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14615                                           player->index_bit, dig_side);
14616
14617       // if collecting triggered player relocation, finish collecting tile
14618       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14619         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14620     }
14621
14622     if (mode == DF_SNAP)
14623     {
14624       if (level.block_snap_field)
14625         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14626       else
14627         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14628
14629       // use old behaviour for old levels (snapping)
14630       if (!level.finish_dig_collect)
14631         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14632                                             player->index_bit, dig_side);
14633     }
14634   }
14635   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14636   {
14637     if (mode == DF_SNAP && element != EL_BD_ROCK)
14638       return MP_NO_ACTION;
14639
14640     if (CAN_FALL(element) && dy)
14641       return MP_NO_ACTION;
14642
14643     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14644         !(element == EL_SPRING && level.use_spring_bug))
14645       return MP_NO_ACTION;
14646
14647     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14648         ((move_direction & MV_VERTICAL &&
14649           ((element_info[element].move_pattern & MV_LEFT &&
14650             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14651            (element_info[element].move_pattern & MV_RIGHT &&
14652             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14653          (move_direction & MV_HORIZONTAL &&
14654           ((element_info[element].move_pattern & MV_UP &&
14655             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14656            (element_info[element].move_pattern & MV_DOWN &&
14657             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14658       return MP_NO_ACTION;
14659
14660     // do not push elements already moving away faster than player
14661     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14662         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14663       return MP_NO_ACTION;
14664
14665     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14666     {
14667       if (player->push_delay_value == -1 || !player_was_pushing)
14668         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14669     }
14670     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14671     {
14672       if (player->push_delay_value == -1)
14673         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14674     }
14675     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14676     {
14677       if (!player->is_pushing)
14678         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14679     }
14680
14681     player->is_pushing = TRUE;
14682     player->is_active = TRUE;
14683
14684     if (!(IN_LEV_FIELD(nextx, nexty) &&
14685           (IS_FREE(nextx, nexty) ||
14686            (IS_SB_ELEMENT(element) &&
14687             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14688            (IS_CUSTOM_ELEMENT(element) &&
14689             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14690       return MP_NO_ACTION;
14691
14692     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14693       return MP_NO_ACTION;
14694
14695     if (player->push_delay == -1)       // new pushing; restart delay
14696       player->push_delay = 0;
14697
14698     if (player->push_delay < player->push_delay_value &&
14699         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14700         element != EL_SPRING && element != EL_BALLOON)
14701     {
14702       // make sure that there is no move delay before next try to push
14703       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14704         player->move_delay = 0;
14705
14706       return MP_NO_ACTION;
14707     }
14708
14709     if (IS_CUSTOM_ELEMENT(element) &&
14710         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14711     {
14712       if (!DigFieldByCE(nextx, nexty, element))
14713         return MP_NO_ACTION;
14714     }
14715
14716     if (IS_SB_ELEMENT(element))
14717     {
14718       boolean sokoban_task_solved = FALSE;
14719
14720       if (element == EL_SOKOBAN_FIELD_FULL)
14721       {
14722         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14723
14724         IncrementSokobanFieldsNeeded();
14725         IncrementSokobanObjectsNeeded();
14726       }
14727
14728       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14729       {
14730         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14731
14732         DecrementSokobanFieldsNeeded();
14733         DecrementSokobanObjectsNeeded();
14734
14735         // sokoban object was pushed from empty field to sokoban field
14736         if (Back[x][y] == EL_EMPTY)
14737           sokoban_task_solved = TRUE;
14738       }
14739
14740       Tile[x][y] = EL_SOKOBAN_OBJECT;
14741
14742       if (Back[x][y] == Back[nextx][nexty])
14743         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14744       else if (Back[x][y] != 0)
14745         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14746                                     ACTION_EMPTYING);
14747       else
14748         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14749                                     ACTION_FILLING);
14750
14751       if (sokoban_task_solved &&
14752           game.sokoban_fields_still_needed == 0 &&
14753           game.sokoban_objects_still_needed == 0 &&
14754           level.auto_exit_sokoban)
14755       {
14756         game.players_still_needed = 0;
14757
14758         LevelSolved();
14759
14760         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14761       }
14762     }
14763     else
14764       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14765
14766     InitMovingField(x, y, move_direction);
14767     GfxAction[x][y] = ACTION_PUSHING;
14768
14769     if (mode == DF_SNAP)
14770       ContinueMoving(x, y);
14771     else
14772       MovPos[x][y] = (dx != 0 ? dx : dy);
14773
14774     Pushed[x][y] = TRUE;
14775     Pushed[nextx][nexty] = TRUE;
14776
14777     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14778       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14779     else
14780       player->push_delay_value = -1;    // get new value later
14781
14782     // check for element change _after_ element has been pushed
14783     if (game.use_change_when_pushing_bug)
14784     {
14785       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14786                                  player->index_bit, dig_side);
14787       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14788                                           player->index_bit, dig_side);
14789     }
14790   }
14791   else if (IS_SWITCHABLE(element))
14792   {
14793     if (PLAYER_SWITCHING(player, x, y))
14794     {
14795       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14796                                           player->index_bit, dig_side);
14797
14798       return MP_ACTION;
14799     }
14800
14801     player->is_switching = TRUE;
14802     player->switch_x = x;
14803     player->switch_y = y;
14804
14805     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14806
14807     if (element == EL_ROBOT_WHEEL)
14808     {
14809       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14810
14811       game.robot_wheel_x = x;
14812       game.robot_wheel_y = y;
14813       game.robot_wheel_active = TRUE;
14814
14815       TEST_DrawLevelField(x, y);
14816     }
14817     else if (element == EL_SP_TERMINAL)
14818     {
14819       int xx, yy;
14820
14821       SCAN_PLAYFIELD(xx, yy)
14822       {
14823         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14824         {
14825           Bang(xx, yy);
14826         }
14827         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14828         {
14829           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14830
14831           ResetGfxAnimation(xx, yy);
14832           TEST_DrawLevelField(xx, yy);
14833         }
14834       }
14835     }
14836     else if (IS_BELT_SWITCH(element))
14837     {
14838       ToggleBeltSwitch(x, y);
14839     }
14840     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14841              element == EL_SWITCHGATE_SWITCH_DOWN ||
14842              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14843              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14844     {
14845       ToggleSwitchgateSwitch();
14846     }
14847     else if (element == EL_LIGHT_SWITCH ||
14848              element == EL_LIGHT_SWITCH_ACTIVE)
14849     {
14850       ToggleLightSwitch(x, y);
14851     }
14852     else if (element == EL_TIMEGATE_SWITCH ||
14853              element == EL_DC_TIMEGATE_SWITCH)
14854     {
14855       ActivateTimegateSwitch(x, y);
14856     }
14857     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14858              element == EL_BALLOON_SWITCH_RIGHT ||
14859              element == EL_BALLOON_SWITCH_UP    ||
14860              element == EL_BALLOON_SWITCH_DOWN  ||
14861              element == EL_BALLOON_SWITCH_NONE  ||
14862              element == EL_BALLOON_SWITCH_ANY)
14863     {
14864       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14865                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14866                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14867                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14868                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14869                              move_direction);
14870     }
14871     else if (element == EL_LAMP)
14872     {
14873       Tile[x][y] = EL_LAMP_ACTIVE;
14874       game.lights_still_needed--;
14875
14876       ResetGfxAnimation(x, y);
14877       TEST_DrawLevelField(x, y);
14878     }
14879     else if (element == EL_TIME_ORB_FULL)
14880     {
14881       Tile[x][y] = EL_TIME_ORB_EMPTY;
14882
14883       if (level.time > 0 || level.use_time_orb_bug)
14884       {
14885         TimeLeft += level.time_orb_time;
14886         game.no_level_time_limit = FALSE;
14887
14888         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14889
14890         DisplayGameControlValues();
14891       }
14892
14893       ResetGfxAnimation(x, y);
14894       TEST_DrawLevelField(x, y);
14895     }
14896     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14897              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14898     {
14899       int xx, yy;
14900
14901       game.ball_active = !game.ball_active;
14902
14903       SCAN_PLAYFIELD(xx, yy)
14904       {
14905         int e = Tile[xx][yy];
14906
14907         if (game.ball_active)
14908         {
14909           if (e == EL_EMC_MAGIC_BALL)
14910             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14911           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14912             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14913         }
14914         else
14915         {
14916           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14917             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14918           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14919             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14920         }
14921       }
14922     }
14923
14924     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14925                                         player->index_bit, dig_side);
14926
14927     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14928                                         player->index_bit, dig_side);
14929
14930     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14931                                         player->index_bit, dig_side);
14932
14933     return MP_ACTION;
14934   }
14935   else
14936   {
14937     if (!PLAYER_SWITCHING(player, x, y))
14938     {
14939       player->is_switching = TRUE;
14940       player->switch_x = x;
14941       player->switch_y = y;
14942
14943       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14944                                  player->index_bit, dig_side);
14945       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14946                                           player->index_bit, dig_side);
14947
14948       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14949                                  player->index_bit, dig_side);
14950       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14951                                           player->index_bit, dig_side);
14952     }
14953
14954     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14955                                player->index_bit, dig_side);
14956     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14957                                         player->index_bit, dig_side);
14958
14959     return MP_NO_ACTION;
14960   }
14961
14962   player->push_delay = -1;
14963
14964   if (is_player)                // function can also be called by EL_PENGUIN
14965   {
14966     if (Tile[x][y] != element)          // really digged/collected something
14967     {
14968       player->is_collecting = !player->is_digging;
14969       player->is_active = TRUE;
14970
14971       player->last_removed_element = element;
14972     }
14973   }
14974
14975   return MP_MOVING;
14976 }
14977
14978 static boolean DigFieldByCE(int x, int y, int digging_element)
14979 {
14980   int element = Tile[x][y];
14981
14982   if (!IS_FREE(x, y))
14983   {
14984     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14985                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14986                   ACTION_BREAKING);
14987
14988     // no element can dig solid indestructible elements
14989     if (IS_INDESTRUCTIBLE(element) &&
14990         !IS_DIGGABLE(element) &&
14991         !IS_COLLECTIBLE(element))
14992       return FALSE;
14993
14994     if (AmoebaNr[x][y] &&
14995         (element == EL_AMOEBA_FULL ||
14996          element == EL_BD_AMOEBA ||
14997          element == EL_AMOEBA_GROWING))
14998     {
14999       AmoebaCnt[AmoebaNr[x][y]]--;
15000       AmoebaCnt2[AmoebaNr[x][y]]--;
15001     }
15002
15003     if (IS_MOVING(x, y))
15004       RemoveMovingField(x, y);
15005     else
15006     {
15007       RemoveField(x, y);
15008       TEST_DrawLevelField(x, y);
15009     }
15010
15011     // if digged element was about to explode, prevent the explosion
15012     ExplodeField[x][y] = EX_TYPE_NONE;
15013
15014     PlayLevelSoundAction(x, y, action);
15015   }
15016
15017   Store[x][y] = EL_EMPTY;
15018
15019   // this makes it possible to leave the removed element again
15020   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15021     Store[x][y] = element;
15022
15023   return TRUE;
15024 }
15025
15026 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15027 {
15028   int jx = player->jx, jy = player->jy;
15029   int x = jx + dx, y = jy + dy;
15030   int snap_direction = (dx == -1 ? MV_LEFT  :
15031                         dx == +1 ? MV_RIGHT :
15032                         dy == -1 ? MV_UP    :
15033                         dy == +1 ? MV_DOWN  : MV_NONE);
15034   boolean can_continue_snapping = (level.continuous_snapping &&
15035                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15036
15037   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15038     return FALSE;
15039
15040   if (!player->active || !IN_LEV_FIELD(x, y))
15041     return FALSE;
15042
15043   if (dx && dy)
15044     return FALSE;
15045
15046   if (!dx && !dy)
15047   {
15048     if (player->MovPos == 0)
15049       player->is_pushing = FALSE;
15050
15051     player->is_snapping = FALSE;
15052
15053     if (player->MovPos == 0)
15054     {
15055       player->is_moving = FALSE;
15056       player->is_digging = FALSE;
15057       player->is_collecting = FALSE;
15058     }
15059
15060     return FALSE;
15061   }
15062
15063   // prevent snapping with already pressed snap key when not allowed
15064   if (player->is_snapping && !can_continue_snapping)
15065     return FALSE;
15066
15067   player->MovDir = snap_direction;
15068
15069   if (player->MovPos == 0)
15070   {
15071     player->is_moving = FALSE;
15072     player->is_digging = FALSE;
15073     player->is_collecting = FALSE;
15074   }
15075
15076   player->is_dropping = FALSE;
15077   player->is_dropping_pressed = FALSE;
15078   player->drop_pressed_delay = 0;
15079
15080   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15081     return FALSE;
15082
15083   player->is_snapping = TRUE;
15084   player->is_active = TRUE;
15085
15086   if (player->MovPos == 0)
15087   {
15088     player->is_moving = FALSE;
15089     player->is_digging = FALSE;
15090     player->is_collecting = FALSE;
15091   }
15092
15093   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15094     TEST_DrawLevelField(player->last_jx, player->last_jy);
15095
15096   TEST_DrawLevelField(x, y);
15097
15098   return TRUE;
15099 }
15100
15101 static boolean DropElement(struct PlayerInfo *player)
15102 {
15103   int old_element, new_element;
15104   int dropx = player->jx, dropy = player->jy;
15105   int drop_direction = player->MovDir;
15106   int drop_side = drop_direction;
15107   int drop_element = get_next_dropped_element(player);
15108
15109   /* do not drop an element on top of another element; when holding drop key
15110      pressed without moving, dropped element must move away before the next
15111      element can be dropped (this is especially important if the next element
15112      is dynamite, which can be placed on background for historical reasons) */
15113   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15114     return MP_ACTION;
15115
15116   if (IS_THROWABLE(drop_element))
15117   {
15118     dropx += GET_DX_FROM_DIR(drop_direction);
15119     dropy += GET_DY_FROM_DIR(drop_direction);
15120
15121     if (!IN_LEV_FIELD(dropx, dropy))
15122       return FALSE;
15123   }
15124
15125   old_element = Tile[dropx][dropy];     // old element at dropping position
15126   new_element = drop_element;           // default: no change when dropping
15127
15128   // check if player is active, not moving and ready to drop
15129   if (!player->active || player->MovPos || player->drop_delay > 0)
15130     return FALSE;
15131
15132   // check if player has anything that can be dropped
15133   if (new_element == EL_UNDEFINED)
15134     return FALSE;
15135
15136   // only set if player has anything that can be dropped
15137   player->is_dropping_pressed = TRUE;
15138
15139   // check if drop key was pressed long enough for EM style dynamite
15140   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15141     return FALSE;
15142
15143   // check if anything can be dropped at the current position
15144   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15145     return FALSE;
15146
15147   // collected custom elements can only be dropped on empty fields
15148   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15149     return FALSE;
15150
15151   if (old_element != EL_EMPTY)
15152     Back[dropx][dropy] = old_element;   // store old element on this field
15153
15154   ResetGfxAnimation(dropx, dropy);
15155   ResetRandomAnimationValue(dropx, dropy);
15156
15157   if (player->inventory_size > 0 ||
15158       player->inventory_infinite_element != EL_UNDEFINED)
15159   {
15160     if (player->inventory_size > 0)
15161     {
15162       player->inventory_size--;
15163
15164       DrawGameDoorValues();
15165
15166       if (new_element == EL_DYNAMITE)
15167         new_element = EL_DYNAMITE_ACTIVE;
15168       else if (new_element == EL_EM_DYNAMITE)
15169         new_element = EL_EM_DYNAMITE_ACTIVE;
15170       else if (new_element == EL_SP_DISK_RED)
15171         new_element = EL_SP_DISK_RED_ACTIVE;
15172     }
15173
15174     Tile[dropx][dropy] = new_element;
15175
15176     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15177       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15178                           el2img(Tile[dropx][dropy]), 0);
15179
15180     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15181
15182     // needed if previous element just changed to "empty" in the last frame
15183     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15184
15185     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15186                                player->index_bit, drop_side);
15187     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15188                                         CE_PLAYER_DROPS_X,
15189                                         player->index_bit, drop_side);
15190
15191     TestIfElementTouchesCustomElement(dropx, dropy);
15192   }
15193   else          // player is dropping a dyna bomb
15194   {
15195     player->dynabombs_left--;
15196
15197     Tile[dropx][dropy] = new_element;
15198
15199     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15200       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15201                           el2img(Tile[dropx][dropy]), 0);
15202
15203     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15204   }
15205
15206   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15207     InitField_WithBug1(dropx, dropy, FALSE);
15208
15209   new_element = Tile[dropx][dropy];     // element might have changed
15210
15211   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15212       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15213   {
15214     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15215       MovDir[dropx][dropy] = drop_direction;
15216
15217     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15218
15219     // do not cause impact style collision by dropping elements that can fall
15220     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15221   }
15222
15223   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15224   player->is_dropping = TRUE;
15225
15226   player->drop_pressed_delay = 0;
15227   player->is_dropping_pressed = FALSE;
15228
15229   player->drop_x = dropx;
15230   player->drop_y = dropy;
15231
15232   return TRUE;
15233 }
15234
15235 // ----------------------------------------------------------------------------
15236 // game sound playing functions
15237 // ----------------------------------------------------------------------------
15238
15239 static int *loop_sound_frame = NULL;
15240 static int *loop_sound_volume = NULL;
15241
15242 void InitPlayLevelSound(void)
15243 {
15244   int num_sounds = getSoundListSize();
15245
15246   checked_free(loop_sound_frame);
15247   checked_free(loop_sound_volume);
15248
15249   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15250   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15251 }
15252
15253 static void PlayLevelSound(int x, int y, int nr)
15254 {
15255   int sx = SCREENX(x), sy = SCREENY(y);
15256   int volume, stereo_position;
15257   int max_distance = 8;
15258   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15259
15260   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15261       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15262     return;
15263
15264   if (!IN_LEV_FIELD(x, y) ||
15265       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15266       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15267     return;
15268
15269   volume = SOUND_MAX_VOLUME;
15270
15271   if (!IN_SCR_FIELD(sx, sy))
15272   {
15273     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15274     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15275
15276     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15277   }
15278
15279   stereo_position = (SOUND_MAX_LEFT +
15280                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15281                      (SCR_FIELDX + 2 * max_distance));
15282
15283   if (IS_LOOP_SOUND(nr))
15284   {
15285     /* This assures that quieter loop sounds do not overwrite louder ones,
15286        while restarting sound volume comparison with each new game frame. */
15287
15288     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15289       return;
15290
15291     loop_sound_volume[nr] = volume;
15292     loop_sound_frame[nr] = FrameCounter;
15293   }
15294
15295   PlaySoundExt(nr, volume, stereo_position, type);
15296 }
15297
15298 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15299 {
15300   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15301                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15302                  y < LEVELY(BY1) ? LEVELY(BY1) :
15303                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15304                  sound_action);
15305 }
15306
15307 static void PlayLevelSoundAction(int x, int y, int action)
15308 {
15309   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15310 }
15311
15312 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15313 {
15314   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15315
15316   if (sound_effect != SND_UNDEFINED)
15317     PlayLevelSound(x, y, sound_effect);
15318 }
15319
15320 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15321                                               int action)
15322 {
15323   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15324
15325   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15326     PlayLevelSound(x, y, sound_effect);
15327 }
15328
15329 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15330 {
15331   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15332
15333   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15334     PlayLevelSound(x, y, sound_effect);
15335 }
15336
15337 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15338 {
15339   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15340
15341   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15342     StopSound(sound_effect);
15343 }
15344
15345 static int getLevelMusicNr(void)
15346 {
15347   int level_pos = level_nr - leveldir_current->first_level;
15348
15349   if (levelset.music[level_nr] != MUS_UNDEFINED)
15350     return levelset.music[level_nr];            // from config file
15351   else
15352     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15353 }
15354
15355 static void FadeLevelSounds(void)
15356 {
15357   FadeSounds();
15358 }
15359
15360 static void FadeLevelMusic(void)
15361 {
15362   int music_nr = getLevelMusicNr();
15363   char *curr_music = getCurrentlyPlayingMusicFilename();
15364   char *next_music = getMusicInfoEntryFilename(music_nr);
15365
15366   if (!strEqual(curr_music, next_music))
15367     FadeMusic();
15368 }
15369
15370 void FadeLevelSoundsAndMusic(void)
15371 {
15372   FadeLevelSounds();
15373   FadeLevelMusic();
15374 }
15375
15376 static void PlayLevelMusic(void)
15377 {
15378   int music_nr = getLevelMusicNr();
15379   char *curr_music = getCurrentlyPlayingMusicFilename();
15380   char *next_music = getMusicInfoEntryFilename(music_nr);
15381
15382   if (!strEqual(curr_music, next_music))
15383     PlayMusicLoop(music_nr);
15384 }
15385
15386 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15387 {
15388   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15389   int offset = 0;
15390   int x = xx - offset;
15391   int y = yy - offset;
15392
15393   switch (sample)
15394   {
15395     case SOUND_blank:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15397       break;
15398
15399     case SOUND_roll:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15401       break;
15402
15403     case SOUND_stone:
15404       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15405       break;
15406
15407     case SOUND_nut:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15409       break;
15410
15411     case SOUND_crack:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15413       break;
15414
15415     case SOUND_bug:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15417       break;
15418
15419     case SOUND_tank:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15421       break;
15422
15423     case SOUND_android_clone:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15425       break;
15426
15427     case SOUND_android_move:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15429       break;
15430
15431     case SOUND_spring:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15433       break;
15434
15435     case SOUND_slurp:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15437       break;
15438
15439     case SOUND_eater:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15441       break;
15442
15443     case SOUND_eater_eat:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15445       break;
15446
15447     case SOUND_alien:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15449       break;
15450
15451     case SOUND_collect:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15453       break;
15454
15455     case SOUND_diamond:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15457       break;
15458
15459     case SOUND_squash:
15460       // !!! CHECK THIS !!!
15461 #if 1
15462       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15463 #else
15464       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15465 #endif
15466       break;
15467
15468     case SOUND_wonderfall:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15470       break;
15471
15472     case SOUND_drip:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15474       break;
15475
15476     case SOUND_push:
15477       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15478       break;
15479
15480     case SOUND_dirt:
15481       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15482       break;
15483
15484     case SOUND_acid:
15485       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15486       break;
15487
15488     case SOUND_ball:
15489       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15490       break;
15491
15492     case SOUND_slide:
15493       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15494       break;
15495
15496     case SOUND_wonder:
15497       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15498       break;
15499
15500     case SOUND_door:
15501       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15502       break;
15503
15504     case SOUND_exit_open:
15505       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15506       break;
15507
15508     case SOUND_exit_leave:
15509       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15510       break;
15511
15512     case SOUND_dynamite:
15513       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15514       break;
15515
15516     case SOUND_tick:
15517       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15518       break;
15519
15520     case SOUND_press:
15521       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15522       break;
15523
15524     case SOUND_wheel:
15525       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15526       break;
15527
15528     case SOUND_boom:
15529       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15530       break;
15531
15532     case SOUND_die:
15533       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15534       break;
15535
15536     case SOUND_time:
15537       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15538       break;
15539
15540     default:
15541       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15542       break;
15543   }
15544 }
15545
15546 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15547 {
15548   int element = map_element_SP_to_RND(element_sp);
15549   int action = map_action_SP_to_RND(action_sp);
15550   int offset = (setup.sp_show_border_elements ? 0 : 1);
15551   int x = xx - offset;
15552   int y = yy - offset;
15553
15554   PlayLevelSoundElementAction(x, y, element, action);
15555 }
15556
15557 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15558 {
15559   int element = map_element_MM_to_RND(element_mm);
15560   int action = map_action_MM_to_RND(action_mm);
15561   int offset = 0;
15562   int x = xx - offset;
15563   int y = yy - offset;
15564
15565   if (!IS_MM_ELEMENT(element))
15566     element = EL_MM_DEFAULT;
15567
15568   PlayLevelSoundElementAction(x, y, element, action);
15569 }
15570
15571 void PlaySound_MM(int sound_mm)
15572 {
15573   int sound = map_sound_MM_to_RND(sound_mm);
15574
15575   if (sound == SND_UNDEFINED)
15576     return;
15577
15578   PlaySound(sound);
15579 }
15580
15581 void PlaySoundLoop_MM(int sound_mm)
15582 {
15583   int sound = map_sound_MM_to_RND(sound_mm);
15584
15585   if (sound == SND_UNDEFINED)
15586     return;
15587
15588   PlaySoundLoop(sound);
15589 }
15590
15591 void StopSound_MM(int sound_mm)
15592 {
15593   int sound = map_sound_MM_to_RND(sound_mm);
15594
15595   if (sound == SND_UNDEFINED)
15596     return;
15597
15598   StopSound(sound);
15599 }
15600
15601 void RaiseScore(int value)
15602 {
15603   game.score += value;
15604
15605   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15606
15607   DisplayGameControlValues();
15608 }
15609
15610 void RaiseScoreElement(int element)
15611 {
15612   switch (element)
15613   {
15614     case EL_EMERALD:
15615     case EL_BD_DIAMOND:
15616     case EL_EMERALD_YELLOW:
15617     case EL_EMERALD_RED:
15618     case EL_EMERALD_PURPLE:
15619     case EL_SP_INFOTRON:
15620       RaiseScore(level.score[SC_EMERALD]);
15621       break;
15622     case EL_DIAMOND:
15623       RaiseScore(level.score[SC_DIAMOND]);
15624       break;
15625     case EL_CRYSTAL:
15626       RaiseScore(level.score[SC_CRYSTAL]);
15627       break;
15628     case EL_PEARL:
15629       RaiseScore(level.score[SC_PEARL]);
15630       break;
15631     case EL_BUG:
15632     case EL_BD_BUTTERFLY:
15633     case EL_SP_ELECTRON:
15634       RaiseScore(level.score[SC_BUG]);
15635       break;
15636     case EL_SPACESHIP:
15637     case EL_BD_FIREFLY:
15638     case EL_SP_SNIKSNAK:
15639       RaiseScore(level.score[SC_SPACESHIP]);
15640       break;
15641     case EL_YAMYAM:
15642     case EL_DARK_YAMYAM:
15643       RaiseScore(level.score[SC_YAMYAM]);
15644       break;
15645     case EL_ROBOT:
15646       RaiseScore(level.score[SC_ROBOT]);
15647       break;
15648     case EL_PACMAN:
15649       RaiseScore(level.score[SC_PACMAN]);
15650       break;
15651     case EL_NUT:
15652       RaiseScore(level.score[SC_NUT]);
15653       break;
15654     case EL_DYNAMITE:
15655     case EL_EM_DYNAMITE:
15656     case EL_SP_DISK_RED:
15657     case EL_DYNABOMB_INCREASE_NUMBER:
15658     case EL_DYNABOMB_INCREASE_SIZE:
15659     case EL_DYNABOMB_INCREASE_POWER:
15660       RaiseScore(level.score[SC_DYNAMITE]);
15661       break;
15662     case EL_SHIELD_NORMAL:
15663     case EL_SHIELD_DEADLY:
15664       RaiseScore(level.score[SC_SHIELD]);
15665       break;
15666     case EL_EXTRA_TIME:
15667       RaiseScore(level.extra_time_score);
15668       break;
15669     case EL_KEY_1:
15670     case EL_KEY_2:
15671     case EL_KEY_3:
15672     case EL_KEY_4:
15673     case EL_EM_KEY_1:
15674     case EL_EM_KEY_2:
15675     case EL_EM_KEY_3:
15676     case EL_EM_KEY_4:
15677     case EL_EMC_KEY_5:
15678     case EL_EMC_KEY_6:
15679     case EL_EMC_KEY_7:
15680     case EL_EMC_KEY_8:
15681     case EL_DC_KEY_WHITE:
15682       RaiseScore(level.score[SC_KEY]);
15683       break;
15684     default:
15685       RaiseScore(element_info[element].collect_score);
15686       break;
15687   }
15688 }
15689
15690 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15691 {
15692   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15693   {
15694     if (!quick_quit)
15695     {
15696       // prevent short reactivation of overlay buttons while closing door
15697       SetOverlayActive(FALSE);
15698       UnmapGameButtons();
15699
15700       // door may still be open due to skipped or envelope style request
15701       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15702     }
15703
15704     if (network.enabled)
15705       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15706     else
15707     {
15708       if (quick_quit)
15709         FadeSkipNextFadeIn();
15710
15711       SetGameStatus(GAME_MODE_MAIN);
15712
15713       DrawMainMenu();
15714     }
15715   }
15716   else          // continue playing the game
15717   {
15718     if (tape.playing && tape.deactivate_display)
15719       TapeDeactivateDisplayOff(TRUE);
15720
15721     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15722
15723     if (tape.playing && tape.deactivate_display)
15724       TapeDeactivateDisplayOn();
15725   }
15726 }
15727
15728 void RequestQuitGame(boolean escape_key_pressed)
15729 {
15730   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15731   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15732                         level_editor_test_game);
15733   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15734                           quick_quit || score_info_tape_play);
15735
15736   RequestQuitGameExt(skip_request, quick_quit,
15737                      "Do you really want to quit the game?");
15738 }
15739
15740 static char *getRestartGameMessage(void)
15741 {
15742   boolean play_again = hasStartedNetworkGame();
15743   static char message[MAX_OUTPUT_LINESIZE];
15744   char *game_over_text = "Game over!";
15745   char *play_again_text = " Play it again?";
15746
15747   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15748       game_mm.game_over_message != NULL)
15749     game_over_text = game_mm.game_over_message;
15750
15751   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15752            (play_again ? play_again_text : ""));
15753
15754   return message;
15755 }
15756
15757 static void RequestRestartGame(void)
15758 {
15759   char *message = getRestartGameMessage();
15760   boolean has_started_game = hasStartedNetworkGame();
15761   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15762   int door_state = DOOR_CLOSE_1;
15763
15764   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15765   {
15766     CloseDoor(door_state);
15767
15768     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15769   }
15770   else
15771   {
15772     // if game was invoked from level editor, also close tape recorder door
15773     if (level_editor_test_game)
15774       door_state = DOOR_CLOSE_ALL;
15775
15776     CloseDoor(door_state);
15777
15778     SetGameStatus(GAME_MODE_MAIN);
15779
15780     DrawMainMenu();
15781   }
15782 }
15783
15784 boolean CheckRestartGame(void)
15785 {
15786   static int game_over_delay = 0;
15787   int game_over_delay_value = 50;
15788   boolean game_over = checkGameFailed();
15789
15790   if (!game_over)
15791   {
15792     game_over_delay = game_over_delay_value;
15793
15794     return FALSE;
15795   }
15796
15797   if (game_over_delay > 0)
15798   {
15799     if (game_over_delay == game_over_delay_value / 2)
15800       PlaySound(SND_GAME_LOSING);
15801
15802     game_over_delay--;
15803
15804     return FALSE;
15805   }
15806
15807   // do not ask to play again if request dialog is already active
15808   if (game.request_active)
15809     return FALSE;
15810
15811   // do not ask to play again if request dialog already handled
15812   if (game.RestartGameRequested)
15813     return FALSE;
15814
15815   // do not ask to play again if game was never actually played
15816   if (!game.GamePlayed)
15817     return FALSE;
15818
15819   // do not ask to play again if this was disabled in setup menu
15820   if (!setup.ask_on_game_over)
15821     return FALSE;
15822
15823   game.RestartGameRequested = TRUE;
15824
15825   RequestRestartGame();
15826
15827   return TRUE;
15828 }
15829
15830 boolean checkGameSolved(void)
15831 {
15832   // set for all game engines if level was solved
15833   return game.LevelSolved_GameEnd;
15834 }
15835
15836 boolean checkGameFailed(void)
15837 {
15838   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15839     return (game_em.game_over && !game_em.level_solved);
15840   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15841     return (game_sp.game_over && !game_sp.level_solved);
15842   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15843     return (game_mm.game_over && !game_mm.level_solved);
15844   else                          // GAME_ENGINE_TYPE_RND
15845     return (game.GameOver && !game.LevelSolved);
15846 }
15847
15848 boolean checkGameEnded(void)
15849 {
15850   return (checkGameSolved() || checkGameFailed());
15851 }
15852
15853
15854 // ----------------------------------------------------------------------------
15855 // random generator functions
15856 // ----------------------------------------------------------------------------
15857
15858 unsigned int InitEngineRandom_RND(int seed)
15859 {
15860   game.num_random_calls = 0;
15861
15862   return InitEngineRandom(seed);
15863 }
15864
15865 unsigned int RND(int max)
15866 {
15867   if (max > 0)
15868   {
15869     game.num_random_calls++;
15870
15871     return GetEngineRandom(max);
15872   }
15873
15874   return 0;
15875 }
15876
15877
15878 // ----------------------------------------------------------------------------
15879 // game engine snapshot handling functions
15880 // ----------------------------------------------------------------------------
15881
15882 struct EngineSnapshotInfo
15883 {
15884   // runtime values for custom element collect score
15885   int collect_score[NUM_CUSTOM_ELEMENTS];
15886
15887   // runtime values for group element choice position
15888   int choice_pos[NUM_GROUP_ELEMENTS];
15889
15890   // runtime values for belt position animations
15891   int belt_graphic[4][NUM_BELT_PARTS];
15892   int belt_anim_mode[4][NUM_BELT_PARTS];
15893 };
15894
15895 static struct EngineSnapshotInfo engine_snapshot_rnd;
15896 static char *snapshot_level_identifier = NULL;
15897 static int snapshot_level_nr = -1;
15898
15899 static void SaveEngineSnapshotValues_RND(void)
15900 {
15901   static int belt_base_active_element[4] =
15902   {
15903     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15904     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15905     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15906     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15907   };
15908   int i, j;
15909
15910   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15911   {
15912     int element = EL_CUSTOM_START + i;
15913
15914     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15915   }
15916
15917   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15918   {
15919     int element = EL_GROUP_START + i;
15920
15921     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15922   }
15923
15924   for (i = 0; i < 4; i++)
15925   {
15926     for (j = 0; j < NUM_BELT_PARTS; j++)
15927     {
15928       int element = belt_base_active_element[i] + j;
15929       int graphic = el2img(element);
15930       int anim_mode = graphic_info[graphic].anim_mode;
15931
15932       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15933       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15934     }
15935   }
15936 }
15937
15938 static void LoadEngineSnapshotValues_RND(void)
15939 {
15940   unsigned int num_random_calls = game.num_random_calls;
15941   int i, j;
15942
15943   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15944   {
15945     int element = EL_CUSTOM_START + i;
15946
15947     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15948   }
15949
15950   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15951   {
15952     int element = EL_GROUP_START + i;
15953
15954     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15955   }
15956
15957   for (i = 0; i < 4; i++)
15958   {
15959     for (j = 0; j < NUM_BELT_PARTS; j++)
15960     {
15961       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15962       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15963
15964       graphic_info[graphic].anim_mode = anim_mode;
15965     }
15966   }
15967
15968   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15969   {
15970     InitRND(tape.random_seed);
15971     for (i = 0; i < num_random_calls; i++)
15972       RND(1);
15973   }
15974
15975   if (game.num_random_calls != num_random_calls)
15976   {
15977     Error("number of random calls out of sync");
15978     Error("number of random calls should be %d", num_random_calls);
15979     Error("number of random calls is %d", game.num_random_calls);
15980
15981     Fail("this should not happen -- please debug");
15982   }
15983 }
15984
15985 void FreeEngineSnapshotSingle(void)
15986 {
15987   FreeSnapshotSingle();
15988
15989   setString(&snapshot_level_identifier, NULL);
15990   snapshot_level_nr = -1;
15991 }
15992
15993 void FreeEngineSnapshotList(void)
15994 {
15995   FreeSnapshotList();
15996 }
15997
15998 static ListNode *SaveEngineSnapshotBuffers(void)
15999 {
16000   ListNode *buffers = NULL;
16001
16002   // copy some special values to a structure better suited for the snapshot
16003
16004   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16005     SaveEngineSnapshotValues_RND();
16006   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16007     SaveEngineSnapshotValues_EM();
16008   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16009     SaveEngineSnapshotValues_SP(&buffers);
16010   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16011     SaveEngineSnapshotValues_MM();
16012
16013   // save values stored in special snapshot structure
16014
16015   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16016     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16017   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16018     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16019   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16020     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16021   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16022     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16023
16024   // save further RND engine values
16025
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16029
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16035
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16039
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16041
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16044
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16055   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16057   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16058   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16060   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16062   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16063
16064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16065   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16066
16067   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16069   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16070
16071   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16072   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16073
16074   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16075   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16076   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16077   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16078   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16079   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16080
16081   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16082   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16083
16084 #if 0
16085   ListNode *node = engine_snapshot_list_rnd;
16086   int num_bytes = 0;
16087
16088   while (node != NULL)
16089   {
16090     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16091
16092     node = node->next;
16093   }
16094
16095   Debug("game:playing:SaveEngineSnapshotBuffers",
16096         "size of engine snapshot: %d bytes", num_bytes);
16097 #endif
16098
16099   return buffers;
16100 }
16101
16102 void SaveEngineSnapshotSingle(void)
16103 {
16104   ListNode *buffers = SaveEngineSnapshotBuffers();
16105
16106   // finally save all snapshot buffers to single snapshot
16107   SaveSnapshotSingle(buffers);
16108
16109   // save level identification information
16110   setString(&snapshot_level_identifier, leveldir_current->identifier);
16111   snapshot_level_nr = level_nr;
16112 }
16113
16114 boolean CheckSaveEngineSnapshotToList(void)
16115 {
16116   boolean save_snapshot =
16117     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16118      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16119       game.snapshot.changed_action) ||
16120      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16121       game.snapshot.collected_item));
16122
16123   game.snapshot.changed_action = FALSE;
16124   game.snapshot.collected_item = FALSE;
16125   game.snapshot.save_snapshot = save_snapshot;
16126
16127   return save_snapshot;
16128 }
16129
16130 void SaveEngineSnapshotToList(void)
16131 {
16132   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16133       tape.quick_resume)
16134     return;
16135
16136   ListNode *buffers = SaveEngineSnapshotBuffers();
16137
16138   // finally save all snapshot buffers to snapshot list
16139   SaveSnapshotToList(buffers);
16140 }
16141
16142 void SaveEngineSnapshotToListInitial(void)
16143 {
16144   FreeEngineSnapshotList();
16145
16146   SaveEngineSnapshotToList();
16147 }
16148
16149 static void LoadEngineSnapshotValues(void)
16150 {
16151   // restore special values from snapshot structure
16152
16153   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16154     LoadEngineSnapshotValues_RND();
16155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16156     LoadEngineSnapshotValues_EM();
16157   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16158     LoadEngineSnapshotValues_SP();
16159   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16160     LoadEngineSnapshotValues_MM();
16161 }
16162
16163 void LoadEngineSnapshotSingle(void)
16164 {
16165   LoadSnapshotSingle();
16166
16167   LoadEngineSnapshotValues();
16168 }
16169
16170 static void LoadEngineSnapshot_Undo(int steps)
16171 {
16172   LoadSnapshotFromList_Older(steps);
16173
16174   LoadEngineSnapshotValues();
16175 }
16176
16177 static void LoadEngineSnapshot_Redo(int steps)
16178 {
16179   LoadSnapshotFromList_Newer(steps);
16180
16181   LoadEngineSnapshotValues();
16182 }
16183
16184 boolean CheckEngineSnapshotSingle(void)
16185 {
16186   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16187           snapshot_level_nr == level_nr);
16188 }
16189
16190 boolean CheckEngineSnapshotList(void)
16191 {
16192   return CheckSnapshotList();
16193 }
16194
16195
16196 // ---------- new game button stuff -------------------------------------------
16197
16198 static struct
16199 {
16200   int graphic;
16201   struct XY *pos;
16202   int gadget_id;
16203   boolean *setup_value;
16204   boolean allowed_on_tape;
16205   boolean is_touch_button;
16206   char *infotext;
16207 } gamebutton_info[NUM_GAME_BUTTONS] =
16208 {
16209   {
16210     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16211     GAME_CTRL_ID_STOP,                          NULL,
16212     TRUE, FALSE,                                "stop game"
16213   },
16214   {
16215     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16216     GAME_CTRL_ID_PAUSE,                         NULL,
16217     TRUE, FALSE,                                "pause game"
16218   },
16219   {
16220     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16221     GAME_CTRL_ID_PLAY,                          NULL,
16222     TRUE, FALSE,                                "play game"
16223   },
16224   {
16225     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16226     GAME_CTRL_ID_UNDO,                          NULL,
16227     TRUE, FALSE,                                "undo step"
16228   },
16229   {
16230     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16231     GAME_CTRL_ID_REDO,                          NULL,
16232     TRUE, FALSE,                                "redo step"
16233   },
16234   {
16235     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16236     GAME_CTRL_ID_SAVE,                          NULL,
16237     TRUE, FALSE,                                "save game"
16238   },
16239   {
16240     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16241     GAME_CTRL_ID_PAUSE2,                        NULL,
16242     TRUE, FALSE,                                "pause game"
16243   },
16244   {
16245     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16246     GAME_CTRL_ID_LOAD,                          NULL,
16247     TRUE, FALSE,                                "load game"
16248   },
16249   {
16250     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16251     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16252     FALSE, FALSE,                               "stop game"
16253   },
16254   {
16255     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16256     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16257     FALSE, FALSE,                               "pause game"
16258   },
16259   {
16260     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16261     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16262     FALSE, FALSE,                               "play game"
16263   },
16264   {
16265     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16266     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16267     FALSE, TRUE,                                "stop game"
16268   },
16269   {
16270     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16271     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16272     FALSE, TRUE,                                "pause game"
16273   },
16274   {
16275     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16276     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16277     TRUE, FALSE,                                "background music on/off"
16278   },
16279   {
16280     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16281     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16282     TRUE, FALSE,                                "sound loops on/off"
16283   },
16284   {
16285     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16286     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16287     TRUE, FALSE,                                "normal sounds on/off"
16288   },
16289   {
16290     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16291     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16292     FALSE, FALSE,                               "background music on/off"
16293   },
16294   {
16295     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16296     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16297     FALSE, FALSE,                               "sound loops on/off"
16298   },
16299   {
16300     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16301     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16302     FALSE, FALSE,                               "normal sounds on/off"
16303   }
16304 };
16305
16306 void CreateGameButtons(void)
16307 {
16308   int i;
16309
16310   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16311   {
16312     int graphic = gamebutton_info[i].graphic;
16313     struct GraphicInfo *gfx = &graphic_info[graphic];
16314     struct XY *pos = gamebutton_info[i].pos;
16315     struct GadgetInfo *gi;
16316     int button_type;
16317     boolean checked;
16318     unsigned int event_mask;
16319     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16320     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16321     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16322     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16323     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16324     int gd_x   = gfx->src_x;
16325     int gd_y   = gfx->src_y;
16326     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16327     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16328     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16329     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16330     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16331     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16332     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16333     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16334     int id = i;
16335
16336     // do not use touch buttons if overlay touch buttons are disabled
16337     if (is_touch_button && !setup.touch.overlay_buttons)
16338       continue;
16339
16340     if (gfx->bitmap == NULL)
16341     {
16342       game_gadget[id] = NULL;
16343
16344       continue;
16345     }
16346
16347     if (id == GAME_CTRL_ID_STOP ||
16348         id == GAME_CTRL_ID_PANEL_STOP ||
16349         id == GAME_CTRL_ID_TOUCH_STOP ||
16350         id == GAME_CTRL_ID_PLAY ||
16351         id == GAME_CTRL_ID_PANEL_PLAY ||
16352         id == GAME_CTRL_ID_SAVE ||
16353         id == GAME_CTRL_ID_LOAD)
16354     {
16355       button_type = GD_TYPE_NORMAL_BUTTON;
16356       checked = FALSE;
16357       event_mask = GD_EVENT_RELEASED;
16358     }
16359     else if (id == GAME_CTRL_ID_UNDO ||
16360              id == GAME_CTRL_ID_REDO)
16361     {
16362       button_type = GD_TYPE_NORMAL_BUTTON;
16363       checked = FALSE;
16364       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16365     }
16366     else
16367     {
16368       button_type = GD_TYPE_CHECK_BUTTON;
16369       checked = (gamebutton_info[i].setup_value != NULL ?
16370                  *gamebutton_info[i].setup_value : FALSE);
16371       event_mask = GD_EVENT_PRESSED;
16372     }
16373
16374     gi = CreateGadget(GDI_CUSTOM_ID, id,
16375                       GDI_IMAGE_ID, graphic,
16376                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16377                       GDI_X, base_x + x,
16378                       GDI_Y, base_y + y,
16379                       GDI_WIDTH, gfx->width,
16380                       GDI_HEIGHT, gfx->height,
16381                       GDI_TYPE, button_type,
16382                       GDI_STATE, GD_BUTTON_UNPRESSED,
16383                       GDI_CHECKED, checked,
16384                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16385                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16386                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16387                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16388                       GDI_DIRECT_DRAW, FALSE,
16389                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16390                       GDI_EVENT_MASK, event_mask,
16391                       GDI_CALLBACK_ACTION, HandleGameButtons,
16392                       GDI_END);
16393
16394     if (gi == NULL)
16395       Fail("cannot create gadget");
16396
16397     game_gadget[id] = gi;
16398   }
16399 }
16400
16401 void FreeGameButtons(void)
16402 {
16403   int i;
16404
16405   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16406     FreeGadget(game_gadget[i]);
16407 }
16408
16409 static void UnmapGameButtonsAtSamePosition(int id)
16410 {
16411   int i;
16412
16413   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16414     if (i != id &&
16415         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16416         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16417       UnmapGadget(game_gadget[i]);
16418 }
16419
16420 static void UnmapGameButtonsAtSamePosition_All(void)
16421 {
16422   if (setup.show_load_save_buttons)
16423   {
16424     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16425     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16426     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16427   }
16428   else if (setup.show_undo_redo_buttons)
16429   {
16430     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16431     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16432     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16433   }
16434   else
16435   {
16436     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16437     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16438     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16439
16440     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16441     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16442     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16443   }
16444 }
16445
16446 void MapLoadSaveButtons(void)
16447 {
16448   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16449   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16450
16451   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16452   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16453 }
16454
16455 void MapUndoRedoButtons(void)
16456 {
16457   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16458   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16459
16460   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16461   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16462 }
16463
16464 void ModifyPauseButtons(void)
16465 {
16466   static int ids[] =
16467   {
16468     GAME_CTRL_ID_PAUSE,
16469     GAME_CTRL_ID_PAUSE2,
16470     GAME_CTRL_ID_PANEL_PAUSE,
16471     GAME_CTRL_ID_TOUCH_PAUSE,
16472     -1
16473   };
16474   int i;
16475
16476   // do not redraw pause button on closed door (may happen when restarting game)
16477   if (!(GetDoorState() & DOOR_OPEN_1))
16478     return;
16479
16480   for (i = 0; ids[i] > -1; i++)
16481     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16482 }
16483
16484 static void MapGameButtonsExt(boolean on_tape)
16485 {
16486   int i;
16487
16488   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16489   {
16490     if ((i == GAME_CTRL_ID_UNDO ||
16491          i == GAME_CTRL_ID_REDO) &&
16492         game_status != GAME_MODE_PLAYING)
16493       continue;
16494
16495     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16496       MapGadget(game_gadget[i]);
16497   }
16498
16499   UnmapGameButtonsAtSamePosition_All();
16500
16501   RedrawGameButtons();
16502 }
16503
16504 static void UnmapGameButtonsExt(boolean on_tape)
16505 {
16506   int i;
16507
16508   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16509     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16510       UnmapGadget(game_gadget[i]);
16511 }
16512
16513 static void RedrawGameButtonsExt(boolean on_tape)
16514 {
16515   int i;
16516
16517   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16518     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16519       RedrawGadget(game_gadget[i]);
16520 }
16521
16522 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16523 {
16524   if (gi == NULL)
16525     return;
16526
16527   gi->checked = state;
16528 }
16529
16530 static void RedrawSoundButtonGadget(int id)
16531 {
16532   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16533              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16534              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16535              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16536              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16537              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16538              id);
16539
16540   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16541   RedrawGadget(game_gadget[id2]);
16542 }
16543
16544 void MapGameButtons(void)
16545 {
16546   MapGameButtonsExt(FALSE);
16547 }
16548
16549 void UnmapGameButtons(void)
16550 {
16551   UnmapGameButtonsExt(FALSE);
16552 }
16553
16554 void RedrawGameButtons(void)
16555 {
16556   RedrawGameButtonsExt(FALSE);
16557 }
16558
16559 void MapGameButtonsOnTape(void)
16560 {
16561   MapGameButtonsExt(TRUE);
16562 }
16563
16564 void UnmapGameButtonsOnTape(void)
16565 {
16566   UnmapGameButtonsExt(TRUE);
16567 }
16568
16569 void RedrawGameButtonsOnTape(void)
16570 {
16571   RedrawGameButtonsExt(TRUE);
16572 }
16573
16574 static void GameUndoRedoExt(void)
16575 {
16576   ClearPlayerAction();
16577
16578   tape.pausing = TRUE;
16579
16580   RedrawPlayfield();
16581   UpdateAndDisplayGameControlValues();
16582
16583   DrawCompleteVideoDisplay();
16584   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16585   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16586   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16587
16588   ModifyPauseButtons();
16589
16590   BackToFront();
16591 }
16592
16593 static void GameUndo(int steps)
16594 {
16595   if (!CheckEngineSnapshotList())
16596     return;
16597
16598   int tape_property_bits = tape.property_bits;
16599
16600   LoadEngineSnapshot_Undo(steps);
16601
16602   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16603
16604   GameUndoRedoExt();
16605 }
16606
16607 static void GameRedo(int steps)
16608 {
16609   if (!CheckEngineSnapshotList())
16610     return;
16611
16612   int tape_property_bits = tape.property_bits;
16613
16614   LoadEngineSnapshot_Redo(steps);
16615
16616   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16617
16618   GameUndoRedoExt();
16619 }
16620
16621 static void HandleGameButtonsExt(int id, int button)
16622 {
16623   static boolean game_undo_executed = FALSE;
16624   int steps = BUTTON_STEPSIZE(button);
16625   boolean handle_game_buttons =
16626     (game_status == GAME_MODE_PLAYING ||
16627      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16628
16629   if (!handle_game_buttons)
16630     return;
16631
16632   switch (id)
16633   {
16634     case GAME_CTRL_ID_STOP:
16635     case GAME_CTRL_ID_PANEL_STOP:
16636     case GAME_CTRL_ID_TOUCH_STOP:
16637       TapeStopGame();
16638
16639       break;
16640
16641     case GAME_CTRL_ID_PAUSE:
16642     case GAME_CTRL_ID_PAUSE2:
16643     case GAME_CTRL_ID_PANEL_PAUSE:
16644     case GAME_CTRL_ID_TOUCH_PAUSE:
16645       if (network.enabled && game_status == GAME_MODE_PLAYING)
16646       {
16647         if (tape.pausing)
16648           SendToServer_ContinuePlaying();
16649         else
16650           SendToServer_PausePlaying();
16651       }
16652       else
16653         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16654
16655       game_undo_executed = FALSE;
16656
16657       break;
16658
16659     case GAME_CTRL_ID_PLAY:
16660     case GAME_CTRL_ID_PANEL_PLAY:
16661       if (game_status == GAME_MODE_MAIN)
16662       {
16663         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16664       }
16665       else if (tape.pausing)
16666       {
16667         if (network.enabled)
16668           SendToServer_ContinuePlaying();
16669         else
16670           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16671       }
16672       break;
16673
16674     case GAME_CTRL_ID_UNDO:
16675       // Important: When using "save snapshot when collecting an item" mode,
16676       // load last (current) snapshot for first "undo" after pressing "pause"
16677       // (else the last-but-one snapshot would be loaded, because the snapshot
16678       // pointer already points to the last snapshot when pressing "pause",
16679       // which is fine for "every step/move" mode, but not for "every collect")
16680       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16681           !game_undo_executed)
16682         steps--;
16683
16684       game_undo_executed = TRUE;
16685
16686       GameUndo(steps);
16687       break;
16688
16689     case GAME_CTRL_ID_REDO:
16690       GameRedo(steps);
16691       break;
16692
16693     case GAME_CTRL_ID_SAVE:
16694       TapeQuickSave();
16695       break;
16696
16697     case GAME_CTRL_ID_LOAD:
16698       TapeQuickLoad();
16699       break;
16700
16701     case SOUND_CTRL_ID_MUSIC:
16702     case SOUND_CTRL_ID_PANEL_MUSIC:
16703       if (setup.sound_music)
16704       { 
16705         setup.sound_music = FALSE;
16706
16707         FadeMusic();
16708       }
16709       else if (audio.music_available)
16710       { 
16711         setup.sound = setup.sound_music = TRUE;
16712
16713         SetAudioMode(setup.sound);
16714
16715         if (game_status == GAME_MODE_PLAYING)
16716           PlayLevelMusic();
16717       }
16718
16719       RedrawSoundButtonGadget(id);
16720
16721       break;
16722
16723     case SOUND_CTRL_ID_LOOPS:
16724     case SOUND_CTRL_ID_PANEL_LOOPS:
16725       if (setup.sound_loops)
16726         setup.sound_loops = FALSE;
16727       else if (audio.loops_available)
16728       {
16729         setup.sound = setup.sound_loops = TRUE;
16730
16731         SetAudioMode(setup.sound);
16732       }
16733
16734       RedrawSoundButtonGadget(id);
16735
16736       break;
16737
16738     case SOUND_CTRL_ID_SIMPLE:
16739     case SOUND_CTRL_ID_PANEL_SIMPLE:
16740       if (setup.sound_simple)
16741         setup.sound_simple = FALSE;
16742       else if (audio.sound_available)
16743       {
16744         setup.sound = setup.sound_simple = TRUE;
16745
16746         SetAudioMode(setup.sound);
16747       }
16748
16749       RedrawSoundButtonGadget(id);
16750
16751       break;
16752
16753     default:
16754       break;
16755   }
16756 }
16757
16758 static void HandleGameButtons(struct GadgetInfo *gi)
16759 {
16760   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16761 }
16762
16763 void HandleSoundButtonKeys(Key key)
16764 {
16765   if (key == setup.shortcut.sound_simple)
16766     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16767   else if (key == setup.shortcut.sound_loops)
16768     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16769   else if (key == setup.shortcut.sound_music)
16770     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16771 }