added explicit level number parameter to high-score handling functions
[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 //                  http://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()             (local_player->LevelSolved_PanelOff)
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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!network.enabled || player->connected_network)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 #if DEBUG_INIT_PLAYER
3277 static void DebugPrintPlayerStatus(char *message)
3278 {
3279   int i;
3280
3281   if (!options.debug)
3282     return;
3283
3284   printf("%s:\n", message);
3285
3286   for (i = 0; i < MAX_PLAYERS; i++)
3287   {
3288     struct PlayerInfo *player = &stored_player[i];
3289
3290     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3291            i + 1,
3292            player->present,
3293            player->connected,
3294            player->connected_locally,
3295            player->connected_network,
3296            player->active);
3297
3298     if (local_player == player)
3299       printf(" (local player)");
3300
3301     printf("\n");
3302   }
3303 }
3304 #endif
3305
3306 void InitGame()
3307 {
3308   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3309   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3310   int fade_mask = REDRAW_FIELD;
3311
3312   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3313   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3314   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3315   int initial_move_dir = MV_DOWN;
3316   int i, j, x, y;
3317
3318   // required here to update video display before fading (FIX THIS)
3319   DrawMaskedBorder(REDRAW_DOOR_2);
3320
3321   if (!game.restart_level)
3322     CloseDoor(DOOR_CLOSE_1);
3323
3324   SetGameStatus(GAME_MODE_PLAYING);
3325
3326   if (level_editor_test_game)
3327     FadeSkipNextFadeIn();
3328   else
3329     FadeSetEnterScreen();
3330
3331   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3332     fade_mask = REDRAW_ALL;
3333
3334   FadeLevelSoundsAndMusic();
3335
3336   ExpireSoundLoops(TRUE);
3337
3338   if (!level_editor_test_game)
3339     FadeOut(fade_mask);
3340
3341   /* needed if different viewport properties defined for playing */
3342   ChangeViewportPropertiesIfNeeded();
3343
3344   ClearField();
3345
3346   DrawCompleteVideoDisplay();
3347
3348   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3349
3350   InitGameEngine();
3351   InitGameControlValues();
3352
3353   /* don't play tapes over network */
3354   network_playing = (network.enabled && !tape.playing);
3355
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357   {
3358     struct PlayerInfo *player = &stored_player[i];
3359
3360     player->index_nr = i;
3361     player->index_bit = (1 << i);
3362     player->element_nr = EL_PLAYER_1 + i;
3363
3364     player->present = FALSE;
3365     player->active = FALSE;
3366     player->mapped = FALSE;
3367
3368     player->killed = FALSE;
3369     player->reanimated = FALSE;
3370
3371     player->action = 0;
3372     player->effective_action = 0;
3373     player->programmed_action = 0;
3374
3375     player->mouse_action.lx = 0;
3376     player->mouse_action.ly = 0;
3377     player->mouse_action.button = 0;
3378     player->mouse_action.button_hint = 0;
3379
3380     player->effective_mouse_action.lx = 0;
3381     player->effective_mouse_action.ly = 0;
3382     player->effective_mouse_action.button = 0;
3383     player->effective_mouse_action.button_hint = 0;
3384
3385     player->score = 0;
3386     player->score_final = 0;
3387
3388     player->health = MAX_HEALTH;
3389     player->health_final = MAX_HEALTH;
3390
3391     player->gems_still_needed = level.gems_needed;
3392     player->sokobanfields_still_needed = 0;
3393     player->lights_still_needed = 0;
3394     player->friends_still_needed = 0;
3395
3396     for (j = 0; j < MAX_NUM_KEYS; j++)
3397       player->key[j] = FALSE;
3398
3399     player->num_white_keys = 0;
3400
3401     player->dynabomb_count = 0;
3402     player->dynabomb_size = 1;
3403     player->dynabombs_left = 0;
3404     player->dynabomb_xl = FALSE;
3405
3406     player->MovDir = initial_move_dir;
3407     player->MovPos = 0;
3408     player->GfxPos = 0;
3409     player->GfxDir = initial_move_dir;
3410     player->GfxAction = ACTION_DEFAULT;
3411     player->Frame = 0;
3412     player->StepFrame = 0;
3413
3414     player->initial_element = player->element_nr;
3415     player->artwork_element =
3416       (level.use_artwork_element[i] ? level.artwork_element[i] :
3417        player->element_nr);
3418     player->use_murphy = FALSE;
3419
3420     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3421     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3422
3423     player->gravity = level.initial_player_gravity[i];
3424
3425     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3426
3427     player->actual_frame_counter = 0;
3428
3429     player->step_counter = 0;
3430
3431     player->last_move_dir = initial_move_dir;
3432
3433     player->is_active = FALSE;
3434
3435     player->is_waiting = FALSE;
3436     player->is_moving = FALSE;
3437     player->is_auto_moving = FALSE;
3438     player->is_digging = FALSE;
3439     player->is_snapping = FALSE;
3440     player->is_collecting = FALSE;
3441     player->is_pushing = FALSE;
3442     player->is_switching = FALSE;
3443     player->is_dropping = FALSE;
3444     player->is_dropping_pressed = FALSE;
3445
3446     player->is_bored = FALSE;
3447     player->is_sleeping = FALSE;
3448
3449     player->was_waiting = TRUE;
3450     player->was_moving = FALSE;
3451     player->was_snapping = FALSE;
3452     player->was_dropping = FALSE;
3453
3454     player->force_dropping = FALSE;
3455
3456     player->frame_counter_bored = -1;
3457     player->frame_counter_sleeping = -1;
3458
3459     player->anim_delay_counter = 0;
3460     player->post_delay_counter = 0;
3461
3462     player->dir_waiting = initial_move_dir;
3463     player->action_waiting = ACTION_DEFAULT;
3464     player->last_action_waiting = ACTION_DEFAULT;
3465     player->special_action_bored = ACTION_DEFAULT;
3466     player->special_action_sleeping = ACTION_DEFAULT;
3467
3468     player->switch_x = -1;
3469     player->switch_y = -1;
3470
3471     player->drop_x = -1;
3472     player->drop_y = -1;
3473
3474     player->show_envelope = 0;
3475
3476     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3477
3478     player->push_delay       = -1;      /* initialized when pushing starts */
3479     player->push_delay_value = game.initial_push_delay_value;
3480
3481     player->drop_delay = 0;
3482     player->drop_pressed_delay = 0;
3483
3484     player->last_jx = -1;
3485     player->last_jy = -1;
3486     player->jx = -1;
3487     player->jy = -1;
3488
3489     player->shield_normal_time_left = 0;
3490     player->shield_deadly_time_left = 0;
3491
3492     player->inventory_infinite_element = EL_UNDEFINED;
3493     player->inventory_size = 0;
3494
3495     if (level.use_initial_inventory[i])
3496     {
3497       for (j = 0; j < level.initial_inventory_size[i]; j++)
3498       {
3499         int element = level.initial_inventory_content[i][j];
3500         int collect_count = element_info[element].collect_count_initial;
3501         int k;
3502
3503         if (!IS_CUSTOM_ELEMENT(element))
3504           collect_count = 1;
3505
3506         if (collect_count == 0)
3507           player->inventory_infinite_element = element;
3508         else
3509           for (k = 0; k < collect_count; k++)
3510             if (player->inventory_size < MAX_INVENTORY_SIZE)
3511               player->inventory_element[player->inventory_size++] = element;
3512       }
3513     }
3514
3515     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3516     SnapField(player, 0, 0);
3517
3518     player->LevelSolved = FALSE;
3519     player->GameOver = FALSE;
3520
3521     player->LevelSolved_GameWon = FALSE;
3522     player->LevelSolved_GameEnd = FALSE;
3523     player->LevelSolved_PanelOff = FALSE;
3524     player->LevelSolved_SaveTape = FALSE;
3525     player->LevelSolved_SaveScore = FALSE;
3526
3527     player->LevelSolved_CountingTime = 0;
3528     player->LevelSolved_CountingScore = 0;
3529     player->LevelSolved_CountingHealth = 0;
3530
3531     map_player_action[i] = i;
3532   }
3533
3534   network_player_action_received = FALSE;
3535
3536   /* initial null action */
3537   if (network_playing)
3538     SendToServer_MovePlayer(MV_NONE);
3539
3540   ZX = ZY = -1;
3541   ExitX = ExitY = -1;
3542
3543   FrameCounter = 0;
3544   TimeFrames = 0;
3545   TimePlayed = 0;
3546   TimeLeft = level.time;
3547   TapeTime = 0;
3548
3549   ScreenMovDir = MV_NONE;
3550   ScreenMovPos = 0;
3551   ScreenGfxPos = 0;
3552
3553   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3554
3555   AllPlayersGone = FALSE;
3556
3557   game.no_time_limit = (level.time == 0);
3558
3559   game.yamyam_content_nr = 0;
3560   game.robot_wheel_active = FALSE;
3561   game.magic_wall_active = FALSE;
3562   game.magic_wall_time_left = 0;
3563   game.light_time_left = 0;
3564   game.timegate_time_left = 0;
3565   game.switchgate_pos = 0;
3566   game.wind_direction = level.wind_direction_initial;
3567
3568   game.lenses_time_left = 0;
3569   game.magnify_time_left = 0;
3570
3571   game.ball_state = level.ball_state_initial;
3572   game.ball_content_nr = 0;
3573
3574   game.envelope_active = FALSE;
3575
3576   for (i = 0; i < NUM_BELTS; i++)
3577   {
3578     game.belt_dir[i] = MV_NONE;
3579     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3580   }
3581
3582   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3583     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3584
3585 #if DEBUG_INIT_PLAYER
3586   DebugPrintPlayerStatus("Player status at level initialization");
3587 #endif
3588
3589   SCAN_PLAYFIELD(x, y)
3590   {
3591     Feld[x][y] = level.field[x][y];
3592     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3593     ChangeDelay[x][y] = 0;
3594     ChangePage[x][y] = -1;
3595     CustomValue[x][y] = 0;              /* initialized in InitField() */
3596     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3597     AmoebaNr[x][y] = 0;
3598     WasJustMoving[x][y] = 0;
3599     WasJustFalling[x][y] = 0;
3600     CheckCollision[x][y] = 0;
3601     CheckImpact[x][y] = 0;
3602     Stop[x][y] = FALSE;
3603     Pushed[x][y] = FALSE;
3604
3605     ChangeCount[x][y] = 0;
3606     ChangeEvent[x][y] = -1;
3607
3608     ExplodePhase[x][y] = 0;
3609     ExplodeDelay[x][y] = 0;
3610     ExplodeField[x][y] = EX_TYPE_NONE;
3611
3612     RunnerVisit[x][y] = 0;
3613     PlayerVisit[x][y] = 0;
3614
3615     GfxFrame[x][y] = 0;
3616     GfxRandom[x][y] = INIT_GFX_RANDOM();
3617     GfxElement[x][y] = EL_UNDEFINED;
3618     GfxAction[x][y] = ACTION_DEFAULT;
3619     GfxDir[x][y] = MV_NONE;
3620     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3621   }
3622
3623   SCAN_PLAYFIELD(x, y)
3624   {
3625     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3626       emulate_bd = FALSE;
3627     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3628       emulate_sb = FALSE;
3629     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3630       emulate_sp = FALSE;
3631
3632     InitField(x, y, TRUE);
3633
3634     ResetGfxAnimation(x, y);
3635   }
3636
3637   InitBeltMovement();
3638
3639   for (i = 0; i < MAX_PLAYERS; i++)
3640   {
3641     struct PlayerInfo *player = &stored_player[i];
3642
3643     /* set number of special actions for bored and sleeping animation */
3644     player->num_special_action_bored =
3645       get_num_special_action(player->artwork_element,
3646                              ACTION_BORING_1, ACTION_BORING_LAST);
3647     player->num_special_action_sleeping =
3648       get_num_special_action(player->artwork_element,
3649                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3650   }
3651
3652   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3653                     emulate_sb ? EMU_SOKOBAN :
3654                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3655
3656   /* initialize type of slippery elements */
3657   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3658   {
3659     if (!IS_CUSTOM_ELEMENT(i))
3660     {
3661       /* default: elements slip down either to the left or right randomly */
3662       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3663
3664       /* SP style elements prefer to slip down on the left side */
3665       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3666         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3667
3668       /* BD style elements prefer to slip down on the left side */
3669       if (game.emulation == EMU_BOULDERDASH)
3670         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3671     }
3672   }
3673
3674   /* initialize explosion and ignition delay */
3675   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3676   {
3677     if (!IS_CUSTOM_ELEMENT(i))
3678     {
3679       int num_phase = 8;
3680       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3681                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3682                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3683       int last_phase = (num_phase + 1) * delay;
3684       int half_phase = (num_phase / 2) * delay;
3685
3686       element_info[i].explosion_delay = last_phase - 1;
3687       element_info[i].ignition_delay = half_phase;
3688
3689       if (i == EL_BLACK_ORB)
3690         element_info[i].ignition_delay = 1;
3691     }
3692   }
3693
3694   /* correct non-moving belts to start moving left */
3695   for (i = 0; i < NUM_BELTS; i++)
3696     if (game.belt_dir[i] == MV_NONE)
3697       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3698
3699 #if USE_NEW_PLAYER_ASSIGNMENTS
3700   for (i = 0; i < MAX_PLAYERS; i++)
3701   {
3702     stored_player[i].connected = FALSE;
3703
3704     /* in network game mode, the local player might not be the first player */
3705     if (stored_player[i].connected_locally)
3706       local_player = &stored_player[i];
3707   }
3708
3709   if (!network.enabled)
3710     local_player->connected = TRUE;
3711
3712   if (tape.playing)
3713   {
3714     for (i = 0; i < MAX_PLAYERS; i++)
3715       stored_player[i].connected = tape.player_participates[i];
3716   }
3717   else if (network.enabled)
3718   {
3719     /* add team mode players connected over the network (needed for correct
3720        assignment of player figures from level to locally playing players) */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (stored_player[i].connected_network)
3724         stored_player[i].connected = TRUE;
3725   }
3726   else if (game.team_mode)
3727   {
3728     /* try to guess locally connected team mode players (needed for correct
3729        assignment of player figures from level to locally playing players) */
3730
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (setup.input[i].use_joystick ||
3733           setup.input[i].key.left != KSYM_UNDEFINED)
3734         stored_player[i].connected = TRUE;
3735   }
3736
3737 #if DEBUG_INIT_PLAYER
3738   DebugPrintPlayerStatus("Player status after level initialization");
3739 #endif
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743     printf("Reassigning players ...\n");
3744 #endif
3745
3746   /* check if any connected player was not found in playfield */
3747   for (i = 0; i < MAX_PLAYERS; i++)
3748   {
3749     struct PlayerInfo *player = &stored_player[i];
3750
3751     if (player->connected && !player->present)
3752     {
3753       struct PlayerInfo *field_player = NULL;
3754
3755 #if DEBUG_INIT_PLAYER
3756       if (options.debug)
3757         printf("- looking for field player for player %d ...\n", i + 1);
3758 #endif
3759
3760       /* assign first free player found that is present in the playfield */
3761
3762       /* first try: look for unmapped playfield player that is not connected */
3763       for (j = 0; j < MAX_PLAYERS; j++)
3764         if (field_player == NULL &&
3765             stored_player[j].present &&
3766             !stored_player[j].mapped &&
3767             !stored_player[j].connected)
3768           field_player = &stored_player[j];
3769
3770       /* second try: look for *any* unmapped playfield player */
3771       for (j = 0; j < MAX_PLAYERS; j++)
3772         if (field_player == NULL &&
3773             stored_player[j].present &&
3774             !stored_player[j].mapped)
3775           field_player = &stored_player[j];
3776
3777       if (field_player != NULL)
3778       {
3779         int jx = field_player->jx, jy = field_player->jy;
3780
3781 #if DEBUG_INIT_PLAYER
3782         if (options.debug)
3783           printf("- found player %d\n", field_player->index_nr + 1);
3784 #endif
3785
3786         player->present = FALSE;
3787         player->active = FALSE;
3788
3789         field_player->present = TRUE;
3790         field_player->active = TRUE;
3791
3792         /*
3793         player->initial_element = field_player->initial_element;
3794         player->artwork_element = field_player->artwork_element;
3795
3796         player->block_last_field       = field_player->block_last_field;
3797         player->block_delay_adjustment = field_player->block_delay_adjustment;
3798         */
3799
3800         StorePlayer[jx][jy] = field_player->element_nr;
3801
3802         field_player->jx = field_player->last_jx = jx;
3803         field_player->jy = field_player->last_jy = jy;
3804
3805         if (local_player == player)
3806           local_player = field_player;
3807
3808         map_player_action[field_player->index_nr] = i;
3809
3810         field_player->mapped = TRUE;
3811
3812 #if DEBUG_INIT_PLAYER
3813         if (options.debug)
3814           printf("- map_player_action[%d] == %d\n",
3815                  field_player->index_nr + 1, i + 1);
3816 #endif
3817       }
3818     }
3819
3820     if (player->connected && player->present)
3821       player->mapped = TRUE;
3822   }
3823
3824 #if DEBUG_INIT_PLAYER
3825   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3826 #endif
3827
3828 #else
3829
3830   /* check if any connected player was not found in playfield */
3831   for (i = 0; i < MAX_PLAYERS; i++)
3832   {
3833     struct PlayerInfo *player = &stored_player[i];
3834
3835     if (player->connected && !player->present)
3836     {
3837       for (j = 0; j < MAX_PLAYERS; j++)
3838       {
3839         struct PlayerInfo *field_player = &stored_player[j];
3840         int jx = field_player->jx, jy = field_player->jy;
3841
3842         /* assign first free player found that is present in the playfield */
3843         if (field_player->present && !field_player->connected)
3844         {
3845           player->present = TRUE;
3846           player->active = TRUE;
3847
3848           field_player->present = FALSE;
3849           field_player->active = FALSE;
3850
3851           player->initial_element = field_player->initial_element;
3852           player->artwork_element = field_player->artwork_element;
3853
3854           player->block_last_field       = field_player->block_last_field;
3855           player->block_delay_adjustment = field_player->block_delay_adjustment;
3856
3857           StorePlayer[jx][jy] = player->element_nr;
3858
3859           player->jx = player->last_jx = jx;
3860           player->jy = player->last_jy = jy;
3861
3862           break;
3863         }
3864       }
3865     }
3866   }
3867 #endif
3868
3869 #if 0
3870   printf("::: local_player->present == %d\n", local_player->present);
3871 #endif
3872
3873   /* set focus to local player for network games, else to all players */
3874   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3875   game.centered_player_nr_next = game.centered_player_nr;
3876   game.set_centered_player = FALSE;
3877
3878   if (network_playing && tape.recording)
3879   {
3880     /* store client dependent player focus when recording network games */
3881     tape.centered_player_nr_next = game.centered_player_nr_next;
3882     tape.set_centered_player = TRUE;
3883   }
3884
3885   if (tape.playing)
3886   {
3887     /* when playing a tape, eliminate all players who do not participate */
3888
3889 #if USE_NEW_PLAYER_ASSIGNMENTS
3890
3891     if (!game.team_mode)
3892     {
3893       for (i = 0; i < MAX_PLAYERS; i++)
3894       {
3895         if (stored_player[i].active &&
3896             !tape.player_participates[map_player_action[i]])
3897         {
3898           struct PlayerInfo *player = &stored_player[i];
3899           int jx = player->jx, jy = player->jy;
3900
3901 #if DEBUG_INIT_PLAYER
3902           if (options.debug)
3903             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3904 #endif
3905
3906           player->active = FALSE;
3907           StorePlayer[jx][jy] = 0;
3908           Feld[jx][jy] = EL_EMPTY;
3909         }
3910       }
3911     }
3912
3913 #else
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916     {
3917       if (stored_player[i].active &&
3918           !tape.player_participates[i])
3919       {
3920         struct PlayerInfo *player = &stored_player[i];
3921         int jx = player->jx, jy = player->jy;
3922
3923         player->active = FALSE;
3924         StorePlayer[jx][jy] = 0;
3925         Feld[jx][jy] = EL_EMPTY;
3926       }
3927     }
3928 #endif
3929   }
3930   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3931   {
3932     /* when in single player mode, eliminate all but the local player */
3933
3934     for (i = 0; i < MAX_PLAYERS; i++)
3935     {
3936       struct PlayerInfo *player = &stored_player[i];
3937
3938       if (player->active && player != local_player)
3939       {
3940         int jx = player->jx, jy = player->jy;
3941
3942         player->active = FALSE;
3943         player->present = FALSE;
3944
3945         StorePlayer[jx][jy] = 0;
3946         Feld[jx][jy] = EL_EMPTY;
3947       }
3948     }
3949   }
3950
3951   /* when recording the game, store which players take part in the game */
3952   if (tape.recording)
3953   {
3954 #if USE_NEW_PLAYER_ASSIGNMENTS
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956       if (stored_player[i].connected)
3957         tape.player_participates[i] = TRUE;
3958 #else
3959     for (i = 0; i < MAX_PLAYERS; i++)
3960       if (stored_player[i].active)
3961         tape.player_participates[i] = TRUE;
3962 #endif
3963   }
3964
3965 #if DEBUG_INIT_PLAYER
3966   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3967 #endif
3968
3969   if (BorderElement == EL_EMPTY)
3970   {
3971     SBX_Left = 0;
3972     SBX_Right = lev_fieldx - SCR_FIELDX;
3973     SBY_Upper = 0;
3974     SBY_Lower = lev_fieldy - SCR_FIELDY;
3975   }
3976   else
3977   {
3978     SBX_Left = -1;
3979     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3980     SBY_Upper = -1;
3981     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3982   }
3983
3984   if (full_lev_fieldx <= SCR_FIELDX)
3985     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3986   if (full_lev_fieldy <= SCR_FIELDY)
3987     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3988
3989   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3990     SBX_Left--;
3991   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3992     SBY_Upper--;
3993
3994   /* if local player not found, look for custom element that might create
3995      the player (make some assumptions about the right custom element) */
3996   if (!local_player->present)
3997   {
3998     int start_x = 0, start_y = 0;
3999     int found_rating = 0;
4000     int found_element = EL_UNDEFINED;
4001     int player_nr = local_player->index_nr;
4002
4003     SCAN_PLAYFIELD(x, y)
4004     {
4005       int element = Feld[x][y];
4006       int content;
4007       int xx, yy;
4008       boolean is_player;
4009
4010       if (level.use_start_element[player_nr] &&
4011           level.start_element[player_nr] == element &&
4012           found_rating < 4)
4013       {
4014         start_x = x;
4015         start_y = y;
4016
4017         found_rating = 4;
4018         found_element = element;
4019       }
4020
4021       if (!IS_CUSTOM_ELEMENT(element))
4022         continue;
4023
4024       if (CAN_CHANGE(element))
4025       {
4026         for (i = 0; i < element_info[element].num_change_pages; i++)
4027         {
4028           /* check for player created from custom element as single target */
4029           content = element_info[element].change_page[i].target_element;
4030           is_player = ELEM_IS_PLAYER(content);
4031
4032           if (is_player && (found_rating < 3 ||
4033                             (found_rating == 3 && element < found_element)))
4034           {
4035             start_x = x;
4036             start_y = y;
4037
4038             found_rating = 3;
4039             found_element = element;
4040           }
4041         }
4042       }
4043
4044       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4045       {
4046         /* check for player created from custom element as explosion content */
4047         content = element_info[element].content.e[xx][yy];
4048         is_player = ELEM_IS_PLAYER(content);
4049
4050         if (is_player && (found_rating < 2 ||
4051                           (found_rating == 2 && element < found_element)))
4052         {
4053           start_x = x + xx - 1;
4054           start_y = y + yy - 1;
4055
4056           found_rating = 2;
4057           found_element = element;
4058         }
4059
4060         if (!CAN_CHANGE(element))
4061           continue;
4062
4063         for (i = 0; i < element_info[element].num_change_pages; i++)
4064         {
4065           /* check for player created from custom element as extended target */
4066           content =
4067             element_info[element].change_page[i].target_content.e[xx][yy];
4068
4069           is_player = ELEM_IS_PLAYER(content);
4070
4071           if (is_player && (found_rating < 1 ||
4072                             (found_rating == 1 && element < found_element)))
4073           {
4074             start_x = x + xx - 1;
4075             start_y = y + yy - 1;
4076
4077             found_rating = 1;
4078             found_element = element;
4079           }
4080         }
4081       }
4082     }
4083
4084     scroll_x = SCROLL_POSITION_X(start_x);
4085     scroll_y = SCROLL_POSITION_Y(start_y);
4086   }
4087   else
4088   {
4089     scroll_x = SCROLL_POSITION_X(local_player->jx);
4090     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4091   }
4092
4093   /* !!! FIX THIS (START) !!! */
4094   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4095   {
4096     InitGameEngine_EM();
4097   }
4098   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4099   {
4100     InitGameEngine_SP();
4101   }
4102   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4103   {
4104     InitGameEngine_MM();
4105   }
4106   else
4107   {
4108     DrawLevel(REDRAW_FIELD);
4109     DrawAllPlayers();
4110
4111     /* after drawing the level, correct some elements */
4112     if (game.timegate_time_left == 0)
4113       CloseAllOpenTimegates();
4114   }
4115
4116   /* blit playfield from scroll buffer to normal back buffer for fading in */
4117   BlitScreenToBitmap(backbuffer);
4118   /* !!! FIX THIS (END) !!! */
4119
4120   DrawMaskedBorder(fade_mask);
4121
4122   FadeIn(fade_mask);
4123
4124 #if 1
4125   // full screen redraw is required at this point in the following cases:
4126   // - special editor door undrawn when game was started from level editor
4127   // - drawing area (playfield) was changed and has to be removed completely
4128   redraw_mask = REDRAW_ALL;
4129   BackToFront();
4130 #endif
4131
4132   if (!game.restart_level)
4133   {
4134     /* copy default game door content to main double buffer */
4135
4136     /* !!! CHECK AGAIN !!! */
4137     SetPanelBackground();
4138     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4139     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4140   }
4141
4142   SetPanelBackground();
4143   SetDrawBackgroundMask(REDRAW_DOOR_1);
4144
4145   UpdateAndDisplayGameControlValues();
4146
4147   if (!game.restart_level)
4148   {
4149     UnmapGameButtons();
4150     UnmapTapeButtons();
4151
4152     FreeGameButtons();
4153     CreateGameButtons();
4154
4155     MapGameButtons();
4156     MapTapeButtons();
4157
4158     /* copy actual game door content to door double buffer for OpenDoor() */
4159     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4160
4161     OpenDoor(DOOR_OPEN_ALL);
4162
4163     KeyboardAutoRepeatOffUnlessAutoplay();
4164
4165 #if DEBUG_INIT_PLAYER
4166     DebugPrintPlayerStatus("Player status (final)");
4167 #endif
4168   }
4169
4170   UnmapAllGadgets();
4171
4172   MapGameButtons();
4173   MapTapeButtons();
4174
4175   if (!game.restart_level && !tape.playing)
4176   {
4177     LevelStats_incPlayed(level_nr);
4178
4179     SaveLevelSetup_SeriesInfo();
4180   }
4181
4182   game.restart_level = FALSE;
4183   game.restart_game_message = NULL;
4184
4185   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4186     InitGameActions_MM();
4187
4188   SaveEngineSnapshotToListInitial();
4189
4190   if (!game.restart_level)
4191   {
4192     PlaySound(SND_GAME_STARTING);
4193
4194     if (setup.sound_music)
4195       PlayLevelMusic();
4196   }
4197 }
4198
4199 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4200                         int actual_player_x, int actual_player_y)
4201 {
4202   /* this is used for non-R'n'D game engines to update certain engine values */
4203
4204   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4205   {
4206     actual_player_x = correctLevelPosX_EM(actual_player_x);
4207     actual_player_y = correctLevelPosY_EM(actual_player_y);
4208   }
4209
4210   /* needed to determine if sounds are played within the visible screen area */
4211   scroll_x = actual_scroll_x;
4212   scroll_y = actual_scroll_y;
4213
4214   /* needed to get player position for "follow finger" playing input method */
4215   local_player->jx = actual_player_x;
4216   local_player->jy = actual_player_y;
4217 }
4218
4219 void InitMovDir(int x, int y)
4220 {
4221   int i, element = Feld[x][y];
4222   static int xy[4][2] =
4223   {
4224     {  0, +1 },
4225     { +1,  0 },
4226     {  0, -1 },
4227     { -1,  0 }
4228   };
4229   static int direction[3][4] =
4230   {
4231     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4232     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4233     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4234   };
4235
4236   switch (element)
4237   {
4238     case EL_BUG_RIGHT:
4239     case EL_BUG_UP:
4240     case EL_BUG_LEFT:
4241     case EL_BUG_DOWN:
4242       Feld[x][y] = EL_BUG;
4243       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4244       break;
4245
4246     case EL_SPACESHIP_RIGHT:
4247     case EL_SPACESHIP_UP:
4248     case EL_SPACESHIP_LEFT:
4249     case EL_SPACESHIP_DOWN:
4250       Feld[x][y] = EL_SPACESHIP;
4251       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4252       break;
4253
4254     case EL_BD_BUTTERFLY_RIGHT:
4255     case EL_BD_BUTTERFLY_UP:
4256     case EL_BD_BUTTERFLY_LEFT:
4257     case EL_BD_BUTTERFLY_DOWN:
4258       Feld[x][y] = EL_BD_BUTTERFLY;
4259       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4260       break;
4261
4262     case EL_BD_FIREFLY_RIGHT:
4263     case EL_BD_FIREFLY_UP:
4264     case EL_BD_FIREFLY_LEFT:
4265     case EL_BD_FIREFLY_DOWN:
4266       Feld[x][y] = EL_BD_FIREFLY;
4267       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4268       break;
4269
4270     case EL_PACMAN_RIGHT:
4271     case EL_PACMAN_UP:
4272     case EL_PACMAN_LEFT:
4273     case EL_PACMAN_DOWN:
4274       Feld[x][y] = EL_PACMAN;
4275       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4276       break;
4277
4278     case EL_YAMYAM_LEFT:
4279     case EL_YAMYAM_RIGHT:
4280     case EL_YAMYAM_UP:
4281     case EL_YAMYAM_DOWN:
4282       Feld[x][y] = EL_YAMYAM;
4283       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4284       break;
4285
4286     case EL_SP_SNIKSNAK:
4287       MovDir[x][y] = MV_UP;
4288       break;
4289
4290     case EL_SP_ELECTRON:
4291       MovDir[x][y] = MV_LEFT;
4292       break;
4293
4294     case EL_MOLE_LEFT:
4295     case EL_MOLE_RIGHT:
4296     case EL_MOLE_UP:
4297     case EL_MOLE_DOWN:
4298       Feld[x][y] = EL_MOLE;
4299       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4300       break;
4301
4302     default:
4303       if (IS_CUSTOM_ELEMENT(element))
4304       {
4305         struct ElementInfo *ei = &element_info[element];
4306         int move_direction_initial = ei->move_direction_initial;
4307         int move_pattern = ei->move_pattern;
4308
4309         if (move_direction_initial == MV_START_PREVIOUS)
4310         {
4311           if (MovDir[x][y] != MV_NONE)
4312             return;
4313
4314           move_direction_initial = MV_START_AUTOMATIC;
4315         }
4316
4317         if (move_direction_initial == MV_START_RANDOM)
4318           MovDir[x][y] = 1 << RND(4);
4319         else if (move_direction_initial & MV_ANY_DIRECTION)
4320           MovDir[x][y] = move_direction_initial;
4321         else if (move_pattern == MV_ALL_DIRECTIONS ||
4322                  move_pattern == MV_TURNING_LEFT ||
4323                  move_pattern == MV_TURNING_RIGHT ||
4324                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4325                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4326                  move_pattern == MV_TURNING_RANDOM)
4327           MovDir[x][y] = 1 << RND(4);
4328         else if (move_pattern == MV_HORIZONTAL)
4329           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4330         else if (move_pattern == MV_VERTICAL)
4331           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4332         else if (move_pattern & MV_ANY_DIRECTION)
4333           MovDir[x][y] = element_info[element].move_pattern;
4334         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4335                  move_pattern == MV_ALONG_RIGHT_SIDE)
4336         {
4337           /* use random direction as default start direction */
4338           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4339             MovDir[x][y] = 1 << RND(4);
4340
4341           for (i = 0; i < NUM_DIRECTIONS; i++)
4342           {
4343             int x1 = x + xy[i][0];
4344             int y1 = y + xy[i][1];
4345
4346             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4347             {
4348               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4349                 MovDir[x][y] = direction[0][i];
4350               else
4351                 MovDir[x][y] = direction[1][i];
4352
4353               break;
4354             }
4355           }
4356         }                
4357       }
4358       else
4359       {
4360         MovDir[x][y] = 1 << RND(4);
4361
4362         if (element != EL_BUG &&
4363             element != EL_SPACESHIP &&
4364             element != EL_BD_BUTTERFLY &&
4365             element != EL_BD_FIREFLY)
4366           break;
4367
4368         for (i = 0; i < NUM_DIRECTIONS; i++)
4369         {
4370           int x1 = x + xy[i][0];
4371           int y1 = y + xy[i][1];
4372
4373           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4374           {
4375             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4376             {
4377               MovDir[x][y] = direction[0][i];
4378               break;
4379             }
4380             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4381                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4382             {
4383               MovDir[x][y] = direction[1][i];
4384               break;
4385             }
4386           }
4387         }
4388       }
4389       break;
4390   }
4391
4392   GfxDir[x][y] = MovDir[x][y];
4393 }
4394
4395 void InitAmoebaNr(int x, int y)
4396 {
4397   int i;
4398   int group_nr = AmoebeNachbarNr(x, y);
4399
4400   if (group_nr == 0)
4401   {
4402     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4403     {
4404       if (AmoebaCnt[i] == 0)
4405       {
4406         group_nr = i;
4407         break;
4408       }
4409     }
4410   }
4411
4412   AmoebaNr[x][y] = group_nr;
4413   AmoebaCnt[group_nr]++;
4414   AmoebaCnt2[group_nr]++;
4415 }
4416
4417 static void PlayerWins(struct PlayerInfo *player)
4418 {
4419   player->LevelSolved = TRUE;
4420   player->GameOver = TRUE;
4421
4422   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4423                          level.native_em_level->lev->score :
4424                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4425                          game_mm.score :
4426                          player->score);
4427   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4428                           MM_HEALTH(game_mm.laser_overload_value) :
4429                           player->health);
4430
4431   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4432                                       TimeLeft);
4433   player->LevelSolved_CountingScore = player->score_final;
4434   player->LevelSolved_CountingHealth = player->health_final;
4435 }
4436
4437 void GameWon()
4438 {
4439   static int time_count_steps;
4440   static int time, time_final;
4441   static int score, score_final;
4442   static int health, health_final;
4443   static int game_over_delay_1 = 0;
4444   static int game_over_delay_2 = 0;
4445   static int game_over_delay_3 = 0;
4446   int game_over_delay_value_1 = 50;
4447   int game_over_delay_value_2 = 25;
4448   int game_over_delay_value_3 = 50;
4449
4450   if (!local_player->LevelSolved_GameWon)
4451   {
4452     int i;
4453
4454     /* do not start end game actions before the player stops moving (to exit) */
4455     if (local_player->MovPos)
4456       return;
4457
4458     local_player->LevelSolved_GameWon = TRUE;
4459     local_player->LevelSolved_SaveTape = tape.recording;
4460     local_player->LevelSolved_SaveScore = !tape.playing;
4461
4462     if (!tape.playing)
4463     {
4464       LevelStats_incSolved(level_nr);
4465
4466       SaveLevelSetup_SeriesInfo();
4467     }
4468
4469     if (tape.auto_play)         /* tape might already be stopped here */
4470       tape.auto_play_level_solved = TRUE;
4471
4472     TapeStop();
4473
4474     game_over_delay_1 = 0;
4475     game_over_delay_2 = 0;
4476     game_over_delay_3 = game_over_delay_value_3;
4477
4478     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4479     score = score_final = local_player->score_final;
4480     health = health_final = local_player->health_final;
4481
4482     if (level.score[SC_TIME_BONUS] > 0)
4483     {
4484       if (TimeLeft > 0)
4485       {
4486         time_final = 0;
4487         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4488       }
4489       else if (game.no_time_limit && TimePlayed < 999)
4490       {
4491         time_final = 999;
4492         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4493       }
4494
4495       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4496
4497       game_over_delay_1 = game_over_delay_value_1;
4498
4499       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4500       {
4501         health_final = 0;
4502         score_final += health * level.score[SC_TIME_BONUS];
4503
4504         game_over_delay_2 = game_over_delay_value_2;
4505       }
4506
4507       local_player->score_final = score_final;
4508       local_player->health_final = health_final;
4509     }
4510
4511     if (level_editor_test_game)
4512     {
4513       time = time_final;
4514       score = score_final;
4515
4516       local_player->LevelSolved_CountingTime = time;
4517       local_player->LevelSolved_CountingScore = score;
4518
4519       game_panel_controls[GAME_PANEL_TIME].value = time;
4520       game_panel_controls[GAME_PANEL_SCORE].value = score;
4521
4522       DisplayGameControlValues();
4523     }
4524
4525     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4526     {
4527       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4528       {
4529         /* close exit door after last player */
4530         if ((AllPlayersGone &&
4531              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4532               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4533               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4534             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4535             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4536         {
4537           int element = Feld[ExitX][ExitY];
4538
4539           Feld[ExitX][ExitY] =
4540             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4541              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4542              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4543              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4544              EL_EM_STEEL_EXIT_CLOSING);
4545
4546           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4547         }
4548
4549         /* player disappears */
4550         DrawLevelField(ExitX, ExitY);
4551       }
4552
4553       for (i = 0; i < MAX_PLAYERS; i++)
4554       {
4555         struct PlayerInfo *player = &stored_player[i];
4556
4557         if (player->present)
4558         {
4559           RemovePlayer(player);
4560
4561           /* player disappears */
4562           DrawLevelField(player->jx, player->jy);
4563         }
4564       }
4565     }
4566
4567     PlaySound(SND_GAME_WINNING);
4568   }
4569
4570   if (game_over_delay_1 > 0)
4571   {
4572     game_over_delay_1--;
4573
4574     return;
4575   }
4576
4577   if (time != time_final)
4578   {
4579     int time_to_go = ABS(time_final - time);
4580     int time_count_dir = (time < time_final ? +1 : -1);
4581
4582     if (time_to_go < time_count_steps)
4583       time_count_steps = 1;
4584
4585     time  += time_count_steps * time_count_dir;
4586     score += time_count_steps * level.score[SC_TIME_BONUS];
4587
4588     local_player->LevelSolved_CountingTime = time;
4589     local_player->LevelSolved_CountingScore = score;
4590
4591     game_panel_controls[GAME_PANEL_TIME].value = time;
4592     game_panel_controls[GAME_PANEL_SCORE].value = score;
4593
4594     DisplayGameControlValues();
4595
4596     if (time == time_final)
4597       StopSound(SND_GAME_LEVELTIME_BONUS);
4598     else if (setup.sound_loops)
4599       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4600     else
4601       PlaySound(SND_GAME_LEVELTIME_BONUS);
4602
4603     return;
4604   }
4605
4606   if (game_over_delay_2 > 0)
4607   {
4608     game_over_delay_2--;
4609
4610     return;
4611   }
4612
4613   if (health != health_final)
4614   {
4615     int health_count_dir = (health < health_final ? +1 : -1);
4616
4617     health += health_count_dir;
4618     score  += level.score[SC_TIME_BONUS];
4619
4620     local_player->LevelSolved_CountingHealth = health;
4621     local_player->LevelSolved_CountingScore = score;
4622
4623     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4624     game_panel_controls[GAME_PANEL_SCORE].value = score;
4625
4626     DisplayGameControlValues();
4627
4628     if (health == health_final)
4629       StopSound(SND_GAME_LEVELTIME_BONUS);
4630     else if (setup.sound_loops)
4631       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4632     else
4633       PlaySound(SND_GAME_LEVELTIME_BONUS);
4634
4635     return;
4636   }
4637
4638   local_player->LevelSolved_PanelOff = TRUE;
4639
4640   if (game_over_delay_3 > 0)
4641   {
4642     game_over_delay_3--;
4643
4644     return;
4645   }
4646
4647   GameEnd();
4648 }
4649
4650 void GameEnd()
4651 {
4652   int hi_pos;
4653   boolean raise_level = FALSE;
4654
4655   local_player->LevelSolved_GameEnd = TRUE;
4656
4657   if (local_player->LevelSolved_SaveTape)
4658   {
4659     /* make sure that request dialog to save tape does not open door again */
4660     if (!global.use_envelope_request)
4661       CloseDoor(DOOR_CLOSE_1);
4662
4663     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4664   }
4665
4666   /* if no tape is to be saved, close both doors simultaneously */
4667   CloseDoor(DOOR_CLOSE_ALL);
4668
4669   if (level_editor_test_game)
4670   {
4671     SetGameStatus(GAME_MODE_MAIN);
4672
4673     DrawMainMenu();
4674
4675     return;
4676   }
4677
4678   if (!local_player->LevelSolved_SaveScore)
4679   {
4680     SetGameStatus(GAME_MODE_MAIN);
4681
4682     DrawMainMenu();
4683
4684     return;
4685   }
4686
4687   if (level_nr == leveldir_current->handicap_level)
4688   {
4689     leveldir_current->handicap_level++;
4690
4691     SaveLevelSetup_SeriesInfo();
4692   }
4693
4694   if (setup.increment_levels &&
4695       level_nr < leveldir_current->last_level)
4696     raise_level = TRUE;                 /* advance to next level */
4697
4698   if ((hi_pos = NewHiScore(level_nr)) >= 0)
4699   {
4700     SetGameStatus(GAME_MODE_SCORES);
4701
4702     DrawHallOfFame(level_nr, hi_pos);
4703
4704     if (raise_level)
4705     {
4706       level_nr++;
4707       TapeErase();
4708     }
4709   }
4710   else
4711   {
4712     SetGameStatus(GAME_MODE_MAIN);
4713
4714     if (raise_level)
4715     {
4716       level_nr++;
4717       TapeErase();
4718     }
4719
4720     DrawMainMenu();
4721   }
4722 }
4723
4724 int NewHiScore(int level_nr)
4725 {
4726   int k, l;
4727   int position = -1;
4728   boolean one_score_entry_per_name = !program.many_scores_per_name;
4729
4730   LoadScore(level_nr);
4731
4732   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4733       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4734     return -1;
4735
4736   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4737   {
4738     if (local_player->score_final > highscore[k].Score)
4739     {
4740       /* player has made it to the hall of fame */
4741
4742       if (k < MAX_SCORE_ENTRIES - 1)
4743       {
4744         int m = MAX_SCORE_ENTRIES - 1;
4745
4746         if (one_score_entry_per_name)
4747         {
4748           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4749             if (strEqual(setup.player_name, highscore[l].Name))
4750               m = l;
4751
4752           if (m == k)   /* player's new highscore overwrites his old one */
4753             goto put_into_list;
4754         }
4755
4756         for (l = m; l > k; l--)
4757         {
4758           strcpy(highscore[l].Name, highscore[l - 1].Name);
4759           highscore[l].Score = highscore[l - 1].Score;
4760         }
4761       }
4762
4763       put_into_list:
4764
4765       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4766       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4767       highscore[k].Score = local_player->score_final; 
4768       position = k;
4769
4770       break;
4771     }
4772     else if (one_score_entry_per_name &&
4773              !strncmp(setup.player_name, highscore[k].Name,
4774                       MAX_PLAYER_NAME_LEN))
4775       break;    /* player already there with a higher score */
4776   }
4777
4778   if (position >= 0) 
4779     SaveScore(level_nr);
4780
4781   return position;
4782 }
4783
4784 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4785 {
4786   int element = Feld[x][y];
4787   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4788   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4789   int horiz_move = (dx != 0);
4790   int sign = (horiz_move ? dx : dy);
4791   int step = sign * element_info[element].move_stepsize;
4792
4793   /* special values for move stepsize for spring and things on conveyor belt */
4794   if (horiz_move)
4795   {
4796     if (CAN_FALL(element) &&
4797         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4798       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4799     else if (element == EL_SPRING)
4800       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4801   }
4802
4803   return step;
4804 }
4805
4806 inline static int getElementMoveStepsize(int x, int y)
4807 {
4808   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4809 }
4810
4811 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4812 {
4813   if (player->GfxAction != action || player->GfxDir != dir)
4814   {
4815     player->GfxAction = action;
4816     player->GfxDir = dir;
4817     player->Frame = 0;
4818     player->StepFrame = 0;
4819   }
4820 }
4821
4822 static void ResetGfxFrame(int x, int y)
4823 {
4824   // profiling showed that "autotest" spends 10~20% of its time in this function
4825   if (DrawingDeactivatedField())
4826     return;
4827
4828   int element = Feld[x][y];
4829   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4830
4831   if (graphic_info[graphic].anim_global_sync)
4832     GfxFrame[x][y] = FrameCounter;
4833   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4834     GfxFrame[x][y] = CustomValue[x][y];
4835   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4836     GfxFrame[x][y] = element_info[element].collect_score;
4837   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4838     GfxFrame[x][y] = ChangeDelay[x][y];
4839 }
4840
4841 static void ResetGfxAnimation(int x, int y)
4842 {
4843   GfxAction[x][y] = ACTION_DEFAULT;
4844   GfxDir[x][y] = MovDir[x][y];
4845   GfxFrame[x][y] = 0;
4846
4847   ResetGfxFrame(x, y);
4848 }
4849
4850 static void ResetRandomAnimationValue(int x, int y)
4851 {
4852   GfxRandom[x][y] = INIT_GFX_RANDOM();
4853 }
4854
4855 void InitMovingField(int x, int y, int direction)
4856 {
4857   int element = Feld[x][y];
4858   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4859   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4860   int newx = x + dx;
4861   int newy = y + dy;
4862   boolean is_moving_before, is_moving_after;
4863
4864   /* check if element was/is moving or being moved before/after mode change */
4865   is_moving_before = (WasJustMoving[x][y] != 0);
4866   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4867
4868   /* reset animation only for moving elements which change direction of moving
4869      or which just started or stopped moving
4870      (else CEs with property "can move" / "not moving" are reset each frame) */
4871   if (is_moving_before != is_moving_after ||
4872       direction != MovDir[x][y])
4873     ResetGfxAnimation(x, y);
4874
4875   MovDir[x][y] = direction;
4876   GfxDir[x][y] = direction;
4877
4878   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4879                      direction == MV_DOWN && CAN_FALL(element) ?
4880                      ACTION_FALLING : ACTION_MOVING);
4881
4882   /* this is needed for CEs with property "can move" / "not moving" */
4883
4884   if (is_moving_after)
4885   {
4886     if (Feld[newx][newy] == EL_EMPTY)
4887       Feld[newx][newy] = EL_BLOCKED;
4888
4889     MovDir[newx][newy] = MovDir[x][y];
4890
4891     CustomValue[newx][newy] = CustomValue[x][y];
4892
4893     GfxFrame[newx][newy] = GfxFrame[x][y];
4894     GfxRandom[newx][newy] = GfxRandom[x][y];
4895     GfxAction[newx][newy] = GfxAction[x][y];
4896     GfxDir[newx][newy] = GfxDir[x][y];
4897   }
4898 }
4899
4900 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4901 {
4902   int direction = MovDir[x][y];
4903   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4904   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4905
4906   *goes_to_x = newx;
4907   *goes_to_y = newy;
4908 }
4909
4910 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4911 {
4912   int oldx = x, oldy = y;
4913   int direction = MovDir[x][y];
4914
4915   if (direction == MV_LEFT)
4916     oldx++;
4917   else if (direction == MV_RIGHT)
4918     oldx--;
4919   else if (direction == MV_UP)
4920     oldy++;
4921   else if (direction == MV_DOWN)
4922     oldy--;
4923
4924   *comes_from_x = oldx;
4925   *comes_from_y = oldy;
4926 }
4927
4928 int MovingOrBlocked2Element(int x, int y)
4929 {
4930   int element = Feld[x][y];
4931
4932   if (element == EL_BLOCKED)
4933   {
4934     int oldx, oldy;
4935
4936     Blocked2Moving(x, y, &oldx, &oldy);
4937     return Feld[oldx][oldy];
4938   }
4939   else
4940     return element;
4941 }
4942
4943 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4944 {
4945   /* like MovingOrBlocked2Element(), but if element is moving
4946      and (x,y) is the field the moving element is just leaving,
4947      return EL_BLOCKED instead of the element value */
4948   int element = Feld[x][y];
4949
4950   if (IS_MOVING(x, y))
4951   {
4952     if (element == EL_BLOCKED)
4953     {
4954       int oldx, oldy;
4955
4956       Blocked2Moving(x, y, &oldx, &oldy);
4957       return Feld[oldx][oldy];
4958     }
4959     else
4960       return EL_BLOCKED;
4961   }
4962   else
4963     return element;
4964 }
4965
4966 static void RemoveField(int x, int y)
4967 {
4968   Feld[x][y] = EL_EMPTY;
4969
4970   MovPos[x][y] = 0;
4971   MovDir[x][y] = 0;
4972   MovDelay[x][y] = 0;
4973
4974   CustomValue[x][y] = 0;
4975
4976   AmoebaNr[x][y] = 0;
4977   ChangeDelay[x][y] = 0;
4978   ChangePage[x][y] = -1;
4979   Pushed[x][y] = FALSE;
4980
4981   GfxElement[x][y] = EL_UNDEFINED;
4982   GfxAction[x][y] = ACTION_DEFAULT;
4983   GfxDir[x][y] = MV_NONE;
4984 }
4985
4986 void RemoveMovingField(int x, int y)
4987 {
4988   int oldx = x, oldy = y, newx = x, newy = y;
4989   int element = Feld[x][y];
4990   int next_element = EL_UNDEFINED;
4991
4992   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4993     return;
4994
4995   if (IS_MOVING(x, y))
4996   {
4997     Moving2Blocked(x, y, &newx, &newy);
4998
4999     if (Feld[newx][newy] != EL_BLOCKED)
5000     {
5001       /* element is moving, but target field is not free (blocked), but
5002          already occupied by something different (example: acid pool);
5003          in this case, only remove the moving field, but not the target */
5004
5005       RemoveField(oldx, oldy);
5006
5007       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5008
5009       TEST_DrawLevelField(oldx, oldy);
5010
5011       return;
5012     }
5013   }
5014   else if (element == EL_BLOCKED)
5015   {
5016     Blocked2Moving(x, y, &oldx, &oldy);
5017     if (!IS_MOVING(oldx, oldy))
5018       return;
5019   }
5020
5021   if (element == EL_BLOCKED &&
5022       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5023        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5024        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5025        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5026        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5027        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5028     next_element = get_next_element(Feld[oldx][oldy]);
5029
5030   RemoveField(oldx, oldy);
5031   RemoveField(newx, newy);
5032
5033   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5034
5035   if (next_element != EL_UNDEFINED)
5036     Feld[oldx][oldy] = next_element;
5037
5038   TEST_DrawLevelField(oldx, oldy);
5039   TEST_DrawLevelField(newx, newy);
5040 }
5041
5042 void DrawDynamite(int x, int y)
5043 {
5044   int sx = SCREENX(x), sy = SCREENY(y);
5045   int graphic = el2img(Feld[x][y]);
5046   int frame;
5047
5048   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5049     return;
5050
5051   if (IS_WALKABLE_INSIDE(Back[x][y]))
5052     return;
5053
5054   if (Back[x][y])
5055     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5056   else if (Store[x][y])
5057     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5058
5059   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5060
5061   if (Back[x][y] || Store[x][y])
5062     DrawGraphicThruMask(sx, sy, graphic, frame);
5063   else
5064     DrawGraphic(sx, sy, graphic, frame);
5065 }
5066
5067 void CheckDynamite(int x, int y)
5068 {
5069   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5070   {
5071     MovDelay[x][y]--;
5072
5073     if (MovDelay[x][y] != 0)
5074     {
5075       DrawDynamite(x, y);
5076       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5077
5078       return;
5079     }
5080   }
5081
5082   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5083
5084   Bang(x, y);
5085 }
5086
5087 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5088 {
5089   boolean num_checked_players = 0;
5090   int i;
5091
5092   for (i = 0; i < MAX_PLAYERS; i++)
5093   {
5094     if (stored_player[i].active)
5095     {
5096       int sx = stored_player[i].jx;
5097       int sy = stored_player[i].jy;
5098
5099       if (num_checked_players == 0)
5100       {
5101         *sx1 = *sx2 = sx;
5102         *sy1 = *sy2 = sy;
5103       }
5104       else
5105       {
5106         *sx1 = MIN(*sx1, sx);
5107         *sy1 = MIN(*sy1, sy);
5108         *sx2 = MAX(*sx2, sx);
5109         *sy2 = MAX(*sy2, sy);
5110       }
5111
5112       num_checked_players++;
5113     }
5114   }
5115 }
5116
5117 static boolean checkIfAllPlayersFitToScreen_RND()
5118 {
5119   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5120
5121   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5122
5123   return (sx2 - sx1 < SCR_FIELDX &&
5124           sy2 - sy1 < SCR_FIELDY);
5125 }
5126
5127 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5128 {
5129   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5130
5131   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5132
5133   *sx = (sx1 + sx2) / 2;
5134   *sy = (sy1 + sy2) / 2;
5135 }
5136
5137 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5138                         boolean center_screen, boolean quick_relocation)
5139 {
5140   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5141   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5142   boolean no_delay = (tape.warp_forward);
5143   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5144   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5145   int new_scroll_x, new_scroll_y;
5146
5147   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5148   {
5149     /* case 1: quick relocation inside visible screen (without scrolling) */
5150
5151     RedrawPlayfield();
5152
5153     return;
5154   }
5155
5156   if (!level.shifted_relocation || center_screen)
5157   {
5158     /* relocation _with_ centering of screen */
5159
5160     new_scroll_x = SCROLL_POSITION_X(x);
5161     new_scroll_y = SCROLL_POSITION_Y(y);
5162   }
5163   else
5164   {
5165     /* relocation _without_ centering of screen */
5166
5167     int center_scroll_x = SCROLL_POSITION_X(old_x);
5168     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5169     int offset_x = x + (scroll_x - center_scroll_x);
5170     int offset_y = y + (scroll_y - center_scroll_y);
5171
5172     /* for new screen position, apply previous offset to center position */
5173     new_scroll_x = SCROLL_POSITION_X(offset_x);
5174     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5175   }
5176
5177   if (quick_relocation)
5178   {
5179     /* case 2: quick relocation (redraw without visible scrolling) */
5180
5181     scroll_x = new_scroll_x;
5182     scroll_y = new_scroll_y;
5183
5184     RedrawPlayfield();
5185
5186     return;
5187   }
5188
5189   /* case 3: visible relocation (with scrolling to new position) */
5190
5191   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5192
5193   SetVideoFrameDelay(wait_delay_value);
5194
5195   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5196   {
5197     int dx = 0, dy = 0;
5198     int fx = FX, fy = FY;
5199
5200     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5201     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5202
5203     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5204       break;
5205
5206     scroll_x -= dx;
5207     scroll_y -= dy;
5208
5209     fx += dx * TILEX / 2;
5210     fy += dy * TILEY / 2;
5211
5212     ScrollLevel(dx, dy);
5213     DrawAllPlayers();
5214
5215     /* scroll in two steps of half tile size to make things smoother */
5216     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5217
5218     /* scroll second step to align at full tile size */
5219     BlitScreenToBitmap(window);
5220   }
5221
5222   DrawAllPlayers();
5223   BackToFront();
5224
5225   SetVideoFrameDelay(frame_delay_value_old);
5226 }
5227
5228 void RelocatePlayer(int jx, int jy, int el_player_raw)
5229 {
5230   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5231   int player_nr = GET_PLAYER_NR(el_player);
5232   struct PlayerInfo *player = &stored_player[player_nr];
5233   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5234   boolean no_delay = (tape.warp_forward);
5235   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5236   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5237   int old_jx = player->jx;
5238   int old_jy = player->jy;
5239   int old_element = Feld[old_jx][old_jy];
5240   int element = Feld[jx][jy];
5241   boolean player_relocated = (old_jx != jx || old_jy != jy);
5242
5243   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5244   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5245   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5246   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5247   int leave_side_horiz = move_dir_horiz;
5248   int leave_side_vert  = move_dir_vert;
5249   int enter_side = enter_side_horiz | enter_side_vert;
5250   int leave_side = leave_side_horiz | leave_side_vert;
5251
5252   if (player->GameOver)         /* do not reanimate dead player */
5253     return;
5254
5255   if (!player_relocated)        /* no need to relocate the player */
5256     return;
5257
5258   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5259   {
5260     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5261     DrawLevelField(jx, jy);
5262   }
5263
5264   if (player->present)
5265   {
5266     while (player->MovPos)
5267     {
5268       ScrollPlayer(player, SCROLL_GO_ON);
5269       ScrollScreen(NULL, SCROLL_GO_ON);
5270
5271       AdvanceFrameAndPlayerCounters(player->index_nr);
5272
5273       DrawPlayer(player);
5274
5275       BackToFront_WithFrameDelay(wait_delay_value);
5276     }
5277
5278     DrawPlayer(player);         /* needed here only to cleanup last field */
5279     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5280
5281     player->is_moving = FALSE;
5282   }
5283
5284   if (IS_CUSTOM_ELEMENT(old_element))
5285     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5286                                CE_LEFT_BY_PLAYER,
5287                                player->index_bit, leave_side);
5288
5289   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5290                                       CE_PLAYER_LEAVES_X,
5291                                       player->index_bit, leave_side);
5292
5293   Feld[jx][jy] = el_player;
5294   InitPlayerField(jx, jy, el_player, TRUE);
5295
5296   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5297      possible that the relocation target field did not contain a player element,
5298      but a walkable element, to which the new player was relocated -- in this
5299      case, restore that (already initialized!) element on the player field */
5300   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5301   {
5302     Feld[jx][jy] = element;     /* restore previously existing element */
5303   }
5304
5305   /* only visually relocate centered player */
5306   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5307                      FALSE, level.instant_relocation);
5308
5309   TestIfPlayerTouchesBadThing(jx, jy);
5310   TestIfPlayerTouchesCustomElement(jx, jy);
5311
5312   if (IS_CUSTOM_ELEMENT(element))
5313     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5314                                player->index_bit, enter_side);
5315
5316   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5317                                       player->index_bit, enter_side);
5318
5319   if (player->is_switching)
5320   {
5321     /* ensure that relocation while still switching an element does not cause
5322        a new element to be treated as also switched directly after relocation
5323        (this is important for teleporter switches that teleport the player to
5324        a place where another teleporter switch is in the same direction, which
5325        would then incorrectly be treated as immediately switched before the
5326        direction key that caused the switch was released) */
5327
5328     player->switch_x += jx - old_jx;
5329     player->switch_y += jy - old_jy;
5330   }
5331 }
5332
5333 void Explode(int ex, int ey, int phase, int mode)
5334 {
5335   int x, y;
5336   int last_phase;
5337   int border_element;
5338
5339   /* !!! eliminate this variable !!! */
5340   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5341
5342   if (game.explosions_delayed)
5343   {
5344     ExplodeField[ex][ey] = mode;
5345     return;
5346   }
5347
5348   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5349   {
5350     int center_element = Feld[ex][ey];
5351     int artwork_element, explosion_element;     /* set these values later */
5352
5353     /* remove things displayed in background while burning dynamite */
5354     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5355       Back[ex][ey] = 0;
5356
5357     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5358     {
5359       /* put moving element to center field (and let it explode there) */
5360       center_element = MovingOrBlocked2Element(ex, ey);
5361       RemoveMovingField(ex, ey);
5362       Feld[ex][ey] = center_element;
5363     }
5364
5365     /* now "center_element" is finally determined -- set related values now */
5366     artwork_element = center_element;           /* for custom player artwork */
5367     explosion_element = center_element;         /* for custom player artwork */
5368
5369     if (IS_PLAYER(ex, ey))
5370     {
5371       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5372
5373       artwork_element = stored_player[player_nr].artwork_element;
5374
5375       if (level.use_explosion_element[player_nr])
5376       {
5377         explosion_element = level.explosion_element[player_nr];
5378         artwork_element = explosion_element;
5379       }
5380     }
5381
5382     if (mode == EX_TYPE_NORMAL ||
5383         mode == EX_TYPE_CENTER ||
5384         mode == EX_TYPE_CROSS)
5385       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5386
5387     last_phase = element_info[explosion_element].explosion_delay + 1;
5388
5389     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5390     {
5391       int xx = x - ex + 1;
5392       int yy = y - ey + 1;
5393       int element;
5394
5395       if (!IN_LEV_FIELD(x, y) ||
5396           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5397           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5398         continue;
5399
5400       element = Feld[x][y];
5401
5402       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5403       {
5404         element = MovingOrBlocked2Element(x, y);
5405
5406         if (!IS_EXPLOSION_PROOF(element))
5407           RemoveMovingField(x, y);
5408       }
5409
5410       /* indestructible elements can only explode in center (but not flames) */
5411       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5412                                            mode == EX_TYPE_BORDER)) ||
5413           element == EL_FLAMES)
5414         continue;
5415
5416       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5417          behaviour, for example when touching a yamyam that explodes to rocks
5418          with active deadly shield, a rock is created under the player !!! */
5419       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5420 #if 0
5421       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5422           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5423            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5424 #else
5425       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5426 #endif
5427       {
5428         if (IS_ACTIVE_BOMB(element))
5429         {
5430           /* re-activate things under the bomb like gate or penguin */
5431           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5432           Back[x][y] = 0;
5433         }
5434
5435         continue;
5436       }
5437
5438       /* save walkable background elements while explosion on same tile */
5439       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5440           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5441         Back[x][y] = element;
5442
5443       /* ignite explodable elements reached by other explosion */
5444       if (element == EL_EXPLOSION)
5445         element = Store2[x][y];
5446
5447       if (AmoebaNr[x][y] &&
5448           (element == EL_AMOEBA_FULL ||
5449            element == EL_BD_AMOEBA ||
5450            element == EL_AMOEBA_GROWING))
5451       {
5452         AmoebaCnt[AmoebaNr[x][y]]--;
5453         AmoebaCnt2[AmoebaNr[x][y]]--;
5454       }
5455
5456       RemoveField(x, y);
5457
5458       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5459       {
5460         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5461
5462         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5463
5464         if (PLAYERINFO(ex, ey)->use_murphy)
5465           Store[x][y] = EL_EMPTY;
5466       }
5467
5468       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5469          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5470       else if (ELEM_IS_PLAYER(center_element))
5471         Store[x][y] = EL_EMPTY;
5472       else if (center_element == EL_YAMYAM)
5473         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5474       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5475         Store[x][y] = element_info[center_element].content.e[xx][yy];
5476 #if 1
5477       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5478          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5479          otherwise) -- FIX THIS !!! */
5480       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5481         Store[x][y] = element_info[element].content.e[1][1];
5482 #else
5483       else if (!CAN_EXPLODE(element))
5484         Store[x][y] = element_info[element].content.e[1][1];
5485 #endif
5486       else
5487         Store[x][y] = EL_EMPTY;
5488
5489       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5490           center_element == EL_AMOEBA_TO_DIAMOND)
5491         Store2[x][y] = element;
5492
5493       Feld[x][y] = EL_EXPLOSION;
5494       GfxElement[x][y] = artwork_element;
5495
5496       ExplodePhase[x][y] = 1;
5497       ExplodeDelay[x][y] = last_phase;
5498
5499       Stop[x][y] = TRUE;
5500     }
5501
5502     if (center_element == EL_YAMYAM)
5503       game.yamyam_content_nr =
5504         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5505
5506     return;
5507   }
5508
5509   if (Stop[ex][ey])
5510     return;
5511
5512   x = ex;
5513   y = ey;
5514
5515   if (phase == 1)
5516     GfxFrame[x][y] = 0;         /* restart explosion animation */
5517
5518   last_phase = ExplodeDelay[x][y];
5519
5520   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5521
5522   /* this can happen if the player leaves an explosion just in time */
5523   if (GfxElement[x][y] == EL_UNDEFINED)
5524     GfxElement[x][y] = EL_EMPTY;
5525
5526   border_element = Store2[x][y];
5527   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5528     border_element = StorePlayer[x][y];
5529
5530   if (phase == element_info[border_element].ignition_delay ||
5531       phase == last_phase)
5532   {
5533     boolean border_explosion = FALSE;
5534
5535     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5536         !PLAYER_EXPLOSION_PROTECTED(x, y))
5537     {
5538       KillPlayerUnlessExplosionProtected(x, y);
5539       border_explosion = TRUE;
5540     }
5541     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5542     {
5543       Feld[x][y] = Store2[x][y];
5544       Store2[x][y] = 0;
5545       Bang(x, y);
5546       border_explosion = TRUE;
5547     }
5548     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5549     {
5550       AmoebeUmwandeln(x, y);
5551       Store2[x][y] = 0;
5552       border_explosion = TRUE;
5553     }
5554
5555     /* if an element just explodes due to another explosion (chain-reaction),
5556        do not immediately end the new explosion when it was the last frame of
5557        the explosion (as it would be done in the following "if"-statement!) */
5558     if (border_explosion && phase == last_phase)
5559       return;
5560   }
5561
5562   if (phase == last_phase)
5563   {
5564     int element;
5565
5566     element = Feld[x][y] = Store[x][y];
5567     Store[x][y] = Store2[x][y] = 0;
5568     GfxElement[x][y] = EL_UNDEFINED;
5569
5570     /* player can escape from explosions and might therefore be still alive */
5571     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5572         element <= EL_PLAYER_IS_EXPLODING_4)
5573     {
5574       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5575       int explosion_element = EL_PLAYER_1 + player_nr;
5576       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5577       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5578
5579       if (level.use_explosion_element[player_nr])
5580         explosion_element = level.explosion_element[player_nr];
5581
5582       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5583                     element_info[explosion_element].content.e[xx][yy]);
5584     }
5585
5586     /* restore probably existing indestructible background element */
5587     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5588       element = Feld[x][y] = Back[x][y];
5589     Back[x][y] = 0;
5590
5591     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5592     GfxDir[x][y] = MV_NONE;
5593     ChangeDelay[x][y] = 0;
5594     ChangePage[x][y] = -1;
5595
5596     CustomValue[x][y] = 0;
5597
5598     InitField_WithBug2(x, y, FALSE);
5599
5600     TEST_DrawLevelField(x, y);
5601
5602     TestIfElementTouchesCustomElement(x, y);
5603
5604     if (GFX_CRUMBLED(element))
5605       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5606
5607     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5608       StorePlayer[x][y] = 0;
5609
5610     if (ELEM_IS_PLAYER(element))
5611       RelocatePlayer(x, y, element);
5612   }
5613   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5614   {
5615     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5616     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5617
5618     if (phase == delay)
5619       TEST_DrawLevelFieldCrumbled(x, y);
5620
5621     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5622     {
5623       DrawLevelElement(x, y, Back[x][y]);
5624       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5625     }
5626     else if (IS_WALKABLE_UNDER(Back[x][y]))
5627     {
5628       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5629       DrawLevelElementThruMask(x, y, Back[x][y]);
5630     }
5631     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5632       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5633   }
5634 }
5635
5636 void DynaExplode(int ex, int ey)
5637 {
5638   int i, j;
5639   int dynabomb_element = Feld[ex][ey];
5640   int dynabomb_size = 1;
5641   boolean dynabomb_xl = FALSE;
5642   struct PlayerInfo *player;
5643   static int xy[4][2] =
5644   {
5645     { 0, -1 },
5646     { -1, 0 },
5647     { +1, 0 },
5648     { 0, +1 }
5649   };
5650
5651   if (IS_ACTIVE_BOMB(dynabomb_element))
5652   {
5653     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5654     dynabomb_size = player->dynabomb_size;
5655     dynabomb_xl = player->dynabomb_xl;
5656     player->dynabombs_left++;
5657   }
5658
5659   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5660
5661   for (i = 0; i < NUM_DIRECTIONS; i++)
5662   {
5663     for (j = 1; j <= dynabomb_size; j++)
5664     {
5665       int x = ex + j * xy[i][0];
5666       int y = ey + j * xy[i][1];
5667       int element;
5668
5669       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5670         break;
5671
5672       element = Feld[x][y];
5673
5674       /* do not restart explosions of fields with active bombs */
5675       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5676         continue;
5677
5678       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5679
5680       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5681           !IS_DIGGABLE(element) && !dynabomb_xl)
5682         break;
5683     }
5684   }
5685 }
5686
5687 void Bang(int x, int y)
5688 {
5689   int element = MovingOrBlocked2Element(x, y);
5690   int explosion_type = EX_TYPE_NORMAL;
5691
5692   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5693   {
5694     struct PlayerInfo *player = PLAYERINFO(x, y);
5695
5696     element = Feld[x][y] = player->initial_element;
5697
5698     if (level.use_explosion_element[player->index_nr])
5699     {
5700       int explosion_element = level.explosion_element[player->index_nr];
5701
5702       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5703         explosion_type = EX_TYPE_CROSS;
5704       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5705         explosion_type = EX_TYPE_CENTER;
5706     }
5707   }
5708
5709   switch (element)
5710   {
5711     case EL_BUG:
5712     case EL_SPACESHIP:
5713     case EL_BD_BUTTERFLY:
5714     case EL_BD_FIREFLY:
5715     case EL_YAMYAM:
5716     case EL_DARK_YAMYAM:
5717     case EL_ROBOT:
5718     case EL_PACMAN:
5719     case EL_MOLE:
5720       RaiseScoreElement(element);
5721       break;
5722
5723     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5724     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5725     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5726     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5727     case EL_DYNABOMB_INCREASE_NUMBER:
5728     case EL_DYNABOMB_INCREASE_SIZE:
5729     case EL_DYNABOMB_INCREASE_POWER:
5730       explosion_type = EX_TYPE_DYNA;
5731       break;
5732
5733     case EL_DC_LANDMINE:
5734       explosion_type = EX_TYPE_CENTER;
5735       break;
5736
5737     case EL_PENGUIN:
5738     case EL_LAMP:
5739     case EL_LAMP_ACTIVE:
5740     case EL_AMOEBA_TO_DIAMOND:
5741       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5742         explosion_type = EX_TYPE_CENTER;
5743       break;
5744
5745     default:
5746       if (element_info[element].explosion_type == EXPLODES_CROSS)
5747         explosion_type = EX_TYPE_CROSS;
5748       else if (element_info[element].explosion_type == EXPLODES_1X1)
5749         explosion_type = EX_TYPE_CENTER;
5750       break;
5751   }
5752
5753   if (explosion_type == EX_TYPE_DYNA)
5754     DynaExplode(x, y);
5755   else
5756     Explode(x, y, EX_PHASE_START, explosion_type);
5757
5758   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5759 }
5760
5761 void SplashAcid(int x, int y)
5762 {
5763   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5764       (!IN_LEV_FIELD(x - 1, y - 2) ||
5765        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5766     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5767
5768   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5769       (!IN_LEV_FIELD(x + 1, y - 2) ||
5770        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5771     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5772
5773   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5774 }
5775
5776 static void InitBeltMovement()
5777 {
5778   static int belt_base_element[4] =
5779   {
5780     EL_CONVEYOR_BELT_1_LEFT,
5781     EL_CONVEYOR_BELT_2_LEFT,
5782     EL_CONVEYOR_BELT_3_LEFT,
5783     EL_CONVEYOR_BELT_4_LEFT
5784   };
5785   static int belt_base_active_element[4] =
5786   {
5787     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5788     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5789     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5790     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5791   };
5792
5793   int x, y, i, j;
5794
5795   /* set frame order for belt animation graphic according to belt direction */
5796   for (i = 0; i < NUM_BELTS; i++)
5797   {
5798     int belt_nr = i;
5799
5800     for (j = 0; j < NUM_BELT_PARTS; j++)
5801     {
5802       int element = belt_base_active_element[belt_nr] + j;
5803       int graphic_1 = el2img(element);
5804       int graphic_2 = el2panelimg(element);
5805
5806       if (game.belt_dir[i] == MV_LEFT)
5807       {
5808         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5809         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5810       }
5811       else
5812       {
5813         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5814         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5815       }
5816     }
5817   }
5818
5819   SCAN_PLAYFIELD(x, y)
5820   {
5821     int element = Feld[x][y];
5822
5823     for (i = 0; i < NUM_BELTS; i++)
5824     {
5825       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5826       {
5827         int e_belt_nr = getBeltNrFromBeltElement(element);
5828         int belt_nr = i;
5829
5830         if (e_belt_nr == belt_nr)
5831         {
5832           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5833
5834           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5835         }
5836       }
5837     }
5838   }
5839 }
5840
5841 static void ToggleBeltSwitch(int x, int y)
5842 {
5843   static int belt_base_element[4] =
5844   {
5845     EL_CONVEYOR_BELT_1_LEFT,
5846     EL_CONVEYOR_BELT_2_LEFT,
5847     EL_CONVEYOR_BELT_3_LEFT,
5848     EL_CONVEYOR_BELT_4_LEFT
5849   };
5850   static int belt_base_active_element[4] =
5851   {
5852     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5853     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5854     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5855     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5856   };
5857   static int belt_base_switch_element[4] =
5858   {
5859     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5860     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5861     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5862     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5863   };
5864   static int belt_move_dir[4] =
5865   {
5866     MV_LEFT,
5867     MV_NONE,
5868     MV_RIGHT,
5869     MV_NONE,
5870   };
5871
5872   int element = Feld[x][y];
5873   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5874   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5875   int belt_dir = belt_move_dir[belt_dir_nr];
5876   int xx, yy, i;
5877
5878   if (!IS_BELT_SWITCH(element))
5879     return;
5880
5881   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5882   game.belt_dir[belt_nr] = belt_dir;
5883
5884   if (belt_dir_nr == 3)
5885     belt_dir_nr = 1;
5886
5887   /* set frame order for belt animation graphic according to belt direction */
5888   for (i = 0; i < NUM_BELT_PARTS; i++)
5889   {
5890     int element = belt_base_active_element[belt_nr] + i;
5891     int graphic_1 = el2img(element);
5892     int graphic_2 = el2panelimg(element);
5893
5894     if (belt_dir == MV_LEFT)
5895     {
5896       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5897       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5898     }
5899     else
5900     {
5901       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5902       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5903     }
5904   }
5905
5906   SCAN_PLAYFIELD(xx, yy)
5907   {
5908     int element = Feld[xx][yy];
5909
5910     if (IS_BELT_SWITCH(element))
5911     {
5912       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5913
5914       if (e_belt_nr == belt_nr)
5915       {
5916         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5917         TEST_DrawLevelField(xx, yy);
5918       }
5919     }
5920     else if (IS_BELT(element) && belt_dir != MV_NONE)
5921     {
5922       int e_belt_nr = getBeltNrFromBeltElement(element);
5923
5924       if (e_belt_nr == belt_nr)
5925       {
5926         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5927
5928         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5929         TEST_DrawLevelField(xx, yy);
5930       }
5931     }
5932     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5933     {
5934       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5935
5936       if (e_belt_nr == belt_nr)
5937       {
5938         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5939
5940         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5941         TEST_DrawLevelField(xx, yy);
5942       }
5943     }
5944   }
5945 }
5946
5947 static void ToggleSwitchgateSwitch(int x, int y)
5948 {
5949   int xx, yy;
5950
5951   game.switchgate_pos = !game.switchgate_pos;
5952
5953   SCAN_PLAYFIELD(xx, yy)
5954   {
5955     int element = Feld[xx][yy];
5956
5957     if (element == EL_SWITCHGATE_SWITCH_UP)
5958     {
5959       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5960       TEST_DrawLevelField(xx, yy);
5961     }
5962     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5963     {
5964       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5965       TEST_DrawLevelField(xx, yy);
5966     }
5967     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5968     {
5969       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5970       TEST_DrawLevelField(xx, yy);
5971     }
5972     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5973     {
5974       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5975       TEST_DrawLevelField(xx, yy);
5976     }
5977     else if (element == EL_SWITCHGATE_OPEN ||
5978              element == EL_SWITCHGATE_OPENING)
5979     {
5980       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5981
5982       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5983     }
5984     else if (element == EL_SWITCHGATE_CLOSED ||
5985              element == EL_SWITCHGATE_CLOSING)
5986     {
5987       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5988
5989       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5990     }
5991   }
5992 }
5993
5994 static int getInvisibleActiveFromInvisibleElement(int element)
5995 {
5996   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5997           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5998           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5999           element);
6000 }
6001
6002 static int getInvisibleFromInvisibleActiveElement(int element)
6003 {
6004   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6005           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6006           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6007           element);
6008 }
6009
6010 static void RedrawAllLightSwitchesAndInvisibleElements()
6011 {
6012   int x, y;
6013
6014   SCAN_PLAYFIELD(x, y)
6015   {
6016     int element = Feld[x][y];
6017
6018     if (element == EL_LIGHT_SWITCH &&
6019         game.light_time_left > 0)
6020     {
6021       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6022       TEST_DrawLevelField(x, y);
6023     }
6024     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6025              game.light_time_left == 0)
6026     {
6027       Feld[x][y] = EL_LIGHT_SWITCH;
6028       TEST_DrawLevelField(x, y);
6029     }
6030     else if (element == EL_EMC_DRIPPER &&
6031              game.light_time_left > 0)
6032     {
6033       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6034       TEST_DrawLevelField(x, y);
6035     }
6036     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6037              game.light_time_left == 0)
6038     {
6039       Feld[x][y] = EL_EMC_DRIPPER;
6040       TEST_DrawLevelField(x, y);
6041     }
6042     else if (element == EL_INVISIBLE_STEELWALL ||
6043              element == EL_INVISIBLE_WALL ||
6044              element == EL_INVISIBLE_SAND)
6045     {
6046       if (game.light_time_left > 0)
6047         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6048
6049       TEST_DrawLevelField(x, y);
6050
6051       /* uncrumble neighbour fields, if needed */
6052       if (element == EL_INVISIBLE_SAND)
6053         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6054     }
6055     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6056              element == EL_INVISIBLE_WALL_ACTIVE ||
6057              element == EL_INVISIBLE_SAND_ACTIVE)
6058     {
6059       if (game.light_time_left == 0)
6060         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6061
6062       TEST_DrawLevelField(x, y);
6063
6064       /* re-crumble neighbour fields, if needed */
6065       if (element == EL_INVISIBLE_SAND)
6066         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6067     }
6068   }
6069 }
6070
6071 static void RedrawAllInvisibleElementsForLenses()
6072 {
6073   int x, y;
6074
6075   SCAN_PLAYFIELD(x, y)
6076   {
6077     int element = Feld[x][y];
6078
6079     if (element == EL_EMC_DRIPPER &&
6080         game.lenses_time_left > 0)
6081     {
6082       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6083       TEST_DrawLevelField(x, y);
6084     }
6085     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6086              game.lenses_time_left == 0)
6087     {
6088       Feld[x][y] = EL_EMC_DRIPPER;
6089       TEST_DrawLevelField(x, y);
6090     }
6091     else if (element == EL_INVISIBLE_STEELWALL ||
6092              element == EL_INVISIBLE_WALL ||
6093              element == EL_INVISIBLE_SAND)
6094     {
6095       if (game.lenses_time_left > 0)
6096         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6097
6098       TEST_DrawLevelField(x, y);
6099
6100       /* uncrumble neighbour fields, if needed */
6101       if (element == EL_INVISIBLE_SAND)
6102         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6103     }
6104     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6105              element == EL_INVISIBLE_WALL_ACTIVE ||
6106              element == EL_INVISIBLE_SAND_ACTIVE)
6107     {
6108       if (game.lenses_time_left == 0)
6109         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6110
6111       TEST_DrawLevelField(x, y);
6112
6113       /* re-crumble neighbour fields, if needed */
6114       if (element == EL_INVISIBLE_SAND)
6115         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6116     }
6117   }
6118 }
6119
6120 static void RedrawAllInvisibleElementsForMagnifier()
6121 {
6122   int x, y;
6123
6124   SCAN_PLAYFIELD(x, y)
6125   {
6126     int element = Feld[x][y];
6127
6128     if (element == EL_EMC_FAKE_GRASS &&
6129         game.magnify_time_left > 0)
6130     {
6131       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6132       TEST_DrawLevelField(x, y);
6133     }
6134     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6135              game.magnify_time_left == 0)
6136     {
6137       Feld[x][y] = EL_EMC_FAKE_GRASS;
6138       TEST_DrawLevelField(x, y);
6139     }
6140     else if (IS_GATE_GRAY(element) &&
6141              game.magnify_time_left > 0)
6142     {
6143       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6144                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6145                     IS_EM_GATE_GRAY(element) ?
6146                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6147                     IS_EMC_GATE_GRAY(element) ?
6148                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6149                     IS_DC_GATE_GRAY(element) ?
6150                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6151                     element);
6152       TEST_DrawLevelField(x, y);
6153     }
6154     else if (IS_GATE_GRAY_ACTIVE(element) &&
6155              game.magnify_time_left == 0)
6156     {
6157       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6158                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6159                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6160                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6161                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6162                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6163                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6164                     EL_DC_GATE_WHITE_GRAY :
6165                     element);
6166       TEST_DrawLevelField(x, y);
6167     }
6168   }
6169 }
6170
6171 static void ToggleLightSwitch(int x, int y)
6172 {
6173   int element = Feld[x][y];
6174
6175   game.light_time_left =
6176     (element == EL_LIGHT_SWITCH ?
6177      level.time_light * FRAMES_PER_SECOND : 0);
6178
6179   RedrawAllLightSwitchesAndInvisibleElements();
6180 }
6181
6182 static void ActivateTimegateSwitch(int x, int y)
6183 {
6184   int xx, yy;
6185
6186   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6187
6188   SCAN_PLAYFIELD(xx, yy)
6189   {
6190     int element = Feld[xx][yy];
6191
6192     if (element == EL_TIMEGATE_CLOSED ||
6193         element == EL_TIMEGATE_CLOSING)
6194     {
6195       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6196       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6197     }
6198
6199     /*
6200     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6201     {
6202       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6203       TEST_DrawLevelField(xx, yy);
6204     }
6205     */
6206
6207   }
6208
6209   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6210                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6211 }
6212
6213 void Impact(int x, int y)
6214 {
6215   boolean last_line = (y == lev_fieldy - 1);
6216   boolean object_hit = FALSE;
6217   boolean impact = (last_line || object_hit);
6218   int element = Feld[x][y];
6219   int smashed = EL_STEELWALL;
6220
6221   if (!last_line)       /* check if element below was hit */
6222   {
6223     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6224       return;
6225
6226     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6227                                          MovDir[x][y + 1] != MV_DOWN ||
6228                                          MovPos[x][y + 1] <= TILEY / 2));
6229
6230     /* do not smash moving elements that left the smashed field in time */
6231     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6232         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6233       object_hit = FALSE;
6234
6235 #if USE_QUICKSAND_IMPACT_BUGFIX
6236     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6237     {
6238       RemoveMovingField(x, y + 1);
6239       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6240       Feld[x][y + 2] = EL_ROCK;
6241       TEST_DrawLevelField(x, y + 2);
6242
6243       object_hit = TRUE;
6244     }
6245
6246     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6247     {
6248       RemoveMovingField(x, y + 1);
6249       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6250       Feld[x][y + 2] = EL_ROCK;
6251       TEST_DrawLevelField(x, y + 2);
6252
6253       object_hit = TRUE;
6254     }
6255 #endif
6256
6257     if (object_hit)
6258       smashed = MovingOrBlocked2Element(x, y + 1);
6259
6260     impact = (last_line || object_hit);
6261   }
6262
6263   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6264   {
6265     SplashAcid(x, y + 1);
6266     return;
6267   }
6268
6269   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6270   /* only reset graphic animation if graphic really changes after impact */
6271   if (impact &&
6272       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6273   {
6274     ResetGfxAnimation(x, y);
6275     TEST_DrawLevelField(x, y);
6276   }
6277
6278   if (impact && CAN_EXPLODE_IMPACT(element))
6279   {
6280     Bang(x, y);
6281     return;
6282   }
6283   else if (impact && element == EL_PEARL &&
6284            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6285   {
6286     ResetGfxAnimation(x, y);
6287
6288     Feld[x][y] = EL_PEARL_BREAKING;
6289     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6290     return;
6291   }
6292   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6293   {
6294     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6295
6296     return;
6297   }
6298
6299   if (impact && element == EL_AMOEBA_DROP)
6300   {
6301     if (object_hit && IS_PLAYER(x, y + 1))
6302       KillPlayerUnlessEnemyProtected(x, y + 1);
6303     else if (object_hit && smashed == EL_PENGUIN)
6304       Bang(x, y + 1);
6305     else
6306     {
6307       Feld[x][y] = EL_AMOEBA_GROWING;
6308       Store[x][y] = EL_AMOEBA_WET;
6309
6310       ResetRandomAnimationValue(x, y);
6311     }
6312     return;
6313   }
6314
6315   if (object_hit)               /* check which object was hit */
6316   {
6317     if ((CAN_PASS_MAGIC_WALL(element) && 
6318          (smashed == EL_MAGIC_WALL ||
6319           smashed == EL_BD_MAGIC_WALL)) ||
6320         (CAN_PASS_DC_MAGIC_WALL(element) &&
6321          smashed == EL_DC_MAGIC_WALL))
6322     {
6323       int xx, yy;
6324       int activated_magic_wall =
6325         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6326          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6327          EL_DC_MAGIC_WALL_ACTIVE);
6328
6329       /* activate magic wall / mill */
6330       SCAN_PLAYFIELD(xx, yy)
6331       {
6332         if (Feld[xx][yy] == smashed)
6333           Feld[xx][yy] = activated_magic_wall;
6334       }
6335
6336       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6337       game.magic_wall_active = TRUE;
6338
6339       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6340                             SND_MAGIC_WALL_ACTIVATING :
6341                             smashed == EL_BD_MAGIC_WALL ?
6342                             SND_BD_MAGIC_WALL_ACTIVATING :
6343                             SND_DC_MAGIC_WALL_ACTIVATING));
6344     }
6345
6346     if (IS_PLAYER(x, y + 1))
6347     {
6348       if (CAN_SMASH_PLAYER(element))
6349       {
6350         KillPlayerUnlessEnemyProtected(x, y + 1);
6351         return;
6352       }
6353     }
6354     else if (smashed == EL_PENGUIN)
6355     {
6356       if (CAN_SMASH_PLAYER(element))
6357       {
6358         Bang(x, y + 1);
6359         return;
6360       }
6361     }
6362     else if (element == EL_BD_DIAMOND)
6363     {
6364       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6365       {
6366         Bang(x, y + 1);
6367         return;
6368       }
6369     }
6370     else if (((element == EL_SP_INFOTRON ||
6371                element == EL_SP_ZONK) &&
6372               (smashed == EL_SP_SNIKSNAK ||
6373                smashed == EL_SP_ELECTRON ||
6374                smashed == EL_SP_DISK_ORANGE)) ||
6375              (element == EL_SP_INFOTRON &&
6376               smashed == EL_SP_DISK_YELLOW))
6377     {
6378       Bang(x, y + 1);
6379       return;
6380     }
6381     else if (CAN_SMASH_EVERYTHING(element))
6382     {
6383       if (IS_CLASSIC_ENEMY(smashed) ||
6384           CAN_EXPLODE_SMASHED(smashed))
6385       {
6386         Bang(x, y + 1);
6387         return;
6388       }
6389       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6390       {
6391         if (smashed == EL_LAMP ||
6392             smashed == EL_LAMP_ACTIVE)
6393         {
6394           Bang(x, y + 1);
6395           return;
6396         }
6397         else if (smashed == EL_NUT)
6398         {
6399           Feld[x][y + 1] = EL_NUT_BREAKING;
6400           PlayLevelSound(x, y, SND_NUT_BREAKING);
6401           RaiseScoreElement(EL_NUT);
6402           return;
6403         }
6404         else if (smashed == EL_PEARL)
6405         {
6406           ResetGfxAnimation(x, y);
6407
6408           Feld[x][y + 1] = EL_PEARL_BREAKING;
6409           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6410           return;
6411         }
6412         else if (smashed == EL_DIAMOND)
6413         {
6414           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6415           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6416           return;
6417         }
6418         else if (IS_BELT_SWITCH(smashed))
6419         {
6420           ToggleBeltSwitch(x, y + 1);
6421         }
6422         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6423                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6424                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6425                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6426         {
6427           ToggleSwitchgateSwitch(x, y + 1);
6428         }
6429         else if (smashed == EL_LIGHT_SWITCH ||
6430                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6431         {
6432           ToggleLightSwitch(x, y + 1);
6433         }
6434         else
6435         {
6436           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6437
6438           CheckElementChangeBySide(x, y + 1, smashed, element,
6439                                    CE_SWITCHED, CH_SIDE_TOP);
6440           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6441                                             CH_SIDE_TOP);
6442         }
6443       }
6444       else
6445       {
6446         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6447       }
6448     }
6449   }
6450
6451   /* play sound of magic wall / mill */
6452   if (!last_line &&
6453       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6454        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6455        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6456   {
6457     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6458       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6459     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6460       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6461     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6462       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6463
6464     return;
6465   }
6466
6467   /* play sound of object that hits the ground */
6468   if (last_line || object_hit)
6469     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6470 }
6471
6472 inline static void TurnRoundExt(int x, int y)
6473 {
6474   static struct
6475   {
6476     int dx, dy;
6477   } move_xy[] =
6478   {
6479     {  0,  0 },
6480     { -1,  0 },
6481     { +1,  0 },
6482     {  0,  0 },
6483     {  0, -1 },
6484     {  0,  0 }, { 0, 0 }, { 0, 0 },
6485     {  0, +1 }
6486   };
6487   static struct
6488   {
6489     int left, right, back;
6490   } turn[] =
6491   {
6492     { 0,        0,              0        },
6493     { MV_DOWN,  MV_UP,          MV_RIGHT },
6494     { MV_UP,    MV_DOWN,        MV_LEFT  },
6495     { 0,        0,              0        },
6496     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6497     { 0,        0,              0        },
6498     { 0,        0,              0        },
6499     { 0,        0,              0        },
6500     { MV_RIGHT, MV_LEFT,        MV_UP    }
6501   };
6502
6503   int element = Feld[x][y];
6504   int move_pattern = element_info[element].move_pattern;
6505
6506   int old_move_dir = MovDir[x][y];
6507   int left_dir  = turn[old_move_dir].left;
6508   int right_dir = turn[old_move_dir].right;
6509   int back_dir  = turn[old_move_dir].back;
6510
6511   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6512   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6513   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6514   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6515
6516   int left_x  = x + left_dx,  left_y  = y + left_dy;
6517   int right_x = x + right_dx, right_y = y + right_dy;
6518   int move_x  = x + move_dx,  move_y  = y + move_dy;
6519
6520   int xx, yy;
6521
6522   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6523   {
6524     TestIfBadThingTouchesOtherBadThing(x, y);
6525
6526     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6527       MovDir[x][y] = right_dir;
6528     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6529       MovDir[x][y] = left_dir;
6530
6531     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6532       MovDelay[x][y] = 9;
6533     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6534       MovDelay[x][y] = 1;
6535   }
6536   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6537   {
6538     TestIfBadThingTouchesOtherBadThing(x, y);
6539
6540     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6541       MovDir[x][y] = left_dir;
6542     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6543       MovDir[x][y] = right_dir;
6544
6545     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6546       MovDelay[x][y] = 9;
6547     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6548       MovDelay[x][y] = 1;
6549   }
6550   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6551   {
6552     TestIfBadThingTouchesOtherBadThing(x, y);
6553
6554     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6555       MovDir[x][y] = left_dir;
6556     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6557       MovDir[x][y] = right_dir;
6558
6559     if (MovDir[x][y] != old_move_dir)
6560       MovDelay[x][y] = 9;
6561   }
6562   else if (element == EL_YAMYAM)
6563   {
6564     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6565     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6566
6567     if (can_turn_left && can_turn_right)
6568       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6569     else if (can_turn_left)
6570       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6571     else if (can_turn_right)
6572       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6573     else
6574       MovDir[x][y] = back_dir;
6575
6576     MovDelay[x][y] = 16 + 16 * RND(3);
6577   }
6578   else if (element == EL_DARK_YAMYAM)
6579   {
6580     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6581                                                          left_x, left_y);
6582     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6583                                                          right_x, right_y);
6584
6585     if (can_turn_left && can_turn_right)
6586       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6587     else if (can_turn_left)
6588       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6589     else if (can_turn_right)
6590       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6591     else
6592       MovDir[x][y] = back_dir;
6593
6594     MovDelay[x][y] = 16 + 16 * RND(3);
6595   }
6596   else if (element == EL_PACMAN)
6597   {
6598     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6599     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6600
6601     if (can_turn_left && can_turn_right)
6602       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6603     else if (can_turn_left)
6604       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6605     else if (can_turn_right)
6606       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6607     else
6608       MovDir[x][y] = back_dir;
6609
6610     MovDelay[x][y] = 6 + RND(40);
6611   }
6612   else if (element == EL_PIG)
6613   {
6614     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6615     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6616     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6617     boolean should_turn_left, should_turn_right, should_move_on;
6618     int rnd_value = 24;
6619     int rnd = RND(rnd_value);
6620
6621     should_turn_left = (can_turn_left &&
6622                         (!can_move_on ||
6623                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6624                                                    y + back_dy + left_dy)));
6625     should_turn_right = (can_turn_right &&
6626                          (!can_move_on ||
6627                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6628                                                     y + back_dy + right_dy)));
6629     should_move_on = (can_move_on &&
6630                       (!can_turn_left ||
6631                        !can_turn_right ||
6632                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6633                                                  y + move_dy + left_dy) ||
6634                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6635                                                  y + move_dy + right_dy)));
6636
6637     if (should_turn_left || should_turn_right || should_move_on)
6638     {
6639       if (should_turn_left && should_turn_right && should_move_on)
6640         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6641                         rnd < 2 * rnd_value / 3 ? right_dir :
6642                         old_move_dir);
6643       else if (should_turn_left && should_turn_right)
6644         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6645       else if (should_turn_left && should_move_on)
6646         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6647       else if (should_turn_right && should_move_on)
6648         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6649       else if (should_turn_left)
6650         MovDir[x][y] = left_dir;
6651       else if (should_turn_right)
6652         MovDir[x][y] = right_dir;
6653       else if (should_move_on)
6654         MovDir[x][y] = old_move_dir;
6655     }
6656     else if (can_move_on && rnd > rnd_value / 8)
6657       MovDir[x][y] = old_move_dir;
6658     else if (can_turn_left && can_turn_right)
6659       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6660     else if (can_turn_left && rnd > rnd_value / 8)
6661       MovDir[x][y] = left_dir;
6662     else if (can_turn_right && rnd > rnd_value/8)
6663       MovDir[x][y] = right_dir;
6664     else
6665       MovDir[x][y] = back_dir;
6666
6667     xx = x + move_xy[MovDir[x][y]].dx;
6668     yy = y + move_xy[MovDir[x][y]].dy;
6669
6670     if (!IN_LEV_FIELD(xx, yy) ||
6671         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6672       MovDir[x][y] = old_move_dir;
6673
6674     MovDelay[x][y] = 0;
6675   }
6676   else if (element == EL_DRAGON)
6677   {
6678     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6679     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6680     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6681     int rnd_value = 24;
6682     int rnd = RND(rnd_value);
6683
6684     if (can_move_on && rnd > rnd_value / 8)
6685       MovDir[x][y] = old_move_dir;
6686     else if (can_turn_left && can_turn_right)
6687       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6688     else if (can_turn_left && rnd > rnd_value / 8)
6689       MovDir[x][y] = left_dir;
6690     else if (can_turn_right && rnd > rnd_value / 8)
6691       MovDir[x][y] = right_dir;
6692     else
6693       MovDir[x][y] = back_dir;
6694
6695     xx = x + move_xy[MovDir[x][y]].dx;
6696     yy = y + move_xy[MovDir[x][y]].dy;
6697
6698     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6699       MovDir[x][y] = old_move_dir;
6700
6701     MovDelay[x][y] = 0;
6702   }
6703   else if (element == EL_MOLE)
6704   {
6705     boolean can_move_on =
6706       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6707                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6708                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6709     if (!can_move_on)
6710     {
6711       boolean can_turn_left =
6712         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6713                               IS_AMOEBOID(Feld[left_x][left_y])));
6714
6715       boolean can_turn_right =
6716         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6717                               IS_AMOEBOID(Feld[right_x][right_y])));
6718
6719       if (can_turn_left && can_turn_right)
6720         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6721       else if (can_turn_left)
6722         MovDir[x][y] = left_dir;
6723       else
6724         MovDir[x][y] = right_dir;
6725     }
6726
6727     if (MovDir[x][y] != old_move_dir)
6728       MovDelay[x][y] = 9;
6729   }
6730   else if (element == EL_BALLOON)
6731   {
6732     MovDir[x][y] = game.wind_direction;
6733     MovDelay[x][y] = 0;
6734   }
6735   else if (element == EL_SPRING)
6736   {
6737     if (MovDir[x][y] & MV_HORIZONTAL)
6738     {
6739       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6740           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6741       {
6742         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6743         ResetGfxAnimation(move_x, move_y);
6744         TEST_DrawLevelField(move_x, move_y);
6745
6746         MovDir[x][y] = back_dir;
6747       }
6748       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6749                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6750         MovDir[x][y] = MV_NONE;
6751     }
6752
6753     MovDelay[x][y] = 0;
6754   }
6755   else if (element == EL_ROBOT ||
6756            element == EL_SATELLITE ||
6757            element == EL_PENGUIN ||
6758            element == EL_EMC_ANDROID)
6759   {
6760     int attr_x = -1, attr_y = -1;
6761
6762     if (AllPlayersGone)
6763     {
6764       attr_x = ExitX;
6765       attr_y = ExitY;
6766     }
6767     else
6768     {
6769       int i;
6770
6771       for (i = 0; i < MAX_PLAYERS; i++)
6772       {
6773         struct PlayerInfo *player = &stored_player[i];
6774         int jx = player->jx, jy = player->jy;
6775
6776         if (!player->active)
6777           continue;
6778
6779         if (attr_x == -1 ||
6780             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6781         {
6782           attr_x = jx;
6783           attr_y = jy;
6784         }
6785       }
6786     }
6787
6788     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6789         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6790          game.engine_version < VERSION_IDENT(3,1,0,0)))
6791     {
6792       attr_x = ZX;
6793       attr_y = ZY;
6794     }
6795
6796     if (element == EL_PENGUIN)
6797     {
6798       int i;
6799       static int xy[4][2] =
6800       {
6801         { 0, -1 },
6802         { -1, 0 },
6803         { +1, 0 },
6804         { 0, +1 }
6805       };
6806
6807       for (i = 0; i < NUM_DIRECTIONS; i++)
6808       {
6809         int ex = x + xy[i][0];
6810         int ey = y + xy[i][1];
6811
6812         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6813                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6814                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6815                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6816         {
6817           attr_x = ex;
6818           attr_y = ey;
6819           break;
6820         }
6821       }
6822     }
6823
6824     MovDir[x][y] = MV_NONE;
6825     if (attr_x < x)
6826       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6827     else if (attr_x > x)
6828       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6829     if (attr_y < y)
6830       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6831     else if (attr_y > y)
6832       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6833
6834     if (element == EL_ROBOT)
6835     {
6836       int newx, newy;
6837
6838       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6839         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6840       Moving2Blocked(x, y, &newx, &newy);
6841
6842       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6843         MovDelay[x][y] = 8 + 8 * !RND(3);
6844       else
6845         MovDelay[x][y] = 16;
6846     }
6847     else if (element == EL_PENGUIN)
6848     {
6849       int newx, newy;
6850
6851       MovDelay[x][y] = 1;
6852
6853       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6854       {
6855         boolean first_horiz = RND(2);
6856         int new_move_dir = MovDir[x][y];
6857
6858         MovDir[x][y] =
6859           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6860         Moving2Blocked(x, y, &newx, &newy);
6861
6862         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6863           return;
6864
6865         MovDir[x][y] =
6866           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6867         Moving2Blocked(x, y, &newx, &newy);
6868
6869         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6870           return;
6871
6872         MovDir[x][y] = old_move_dir;
6873         return;
6874       }
6875     }
6876     else if (element == EL_SATELLITE)
6877     {
6878       int newx, newy;
6879
6880       MovDelay[x][y] = 1;
6881
6882       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6883       {
6884         boolean first_horiz = RND(2);
6885         int new_move_dir = MovDir[x][y];
6886
6887         MovDir[x][y] =
6888           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6889         Moving2Blocked(x, y, &newx, &newy);
6890
6891         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6892           return;
6893
6894         MovDir[x][y] =
6895           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6896         Moving2Blocked(x, y, &newx, &newy);
6897
6898         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6899           return;
6900
6901         MovDir[x][y] = old_move_dir;
6902         return;
6903       }
6904     }
6905     else if (element == EL_EMC_ANDROID)
6906     {
6907       static int check_pos[16] =
6908       {
6909         -1,             /*  0 => (invalid)          */
6910         7,              /*  1 => MV_LEFT            */
6911         3,              /*  2 => MV_RIGHT           */
6912         -1,             /*  3 => (invalid)          */
6913         1,              /*  4 =>            MV_UP   */
6914         0,              /*  5 => MV_LEFT  | MV_UP   */
6915         2,              /*  6 => MV_RIGHT | MV_UP   */
6916         -1,             /*  7 => (invalid)          */
6917         5,              /*  8 =>            MV_DOWN */
6918         6,              /*  9 => MV_LEFT  | MV_DOWN */
6919         4,              /* 10 => MV_RIGHT | MV_DOWN */
6920         -1,             /* 11 => (invalid)          */
6921         -1,             /* 12 => (invalid)          */
6922         -1,             /* 13 => (invalid)          */
6923         -1,             /* 14 => (invalid)          */
6924         -1,             /* 15 => (invalid)          */
6925       };
6926       static struct
6927       {
6928         int dx, dy;
6929         int dir;
6930       } check_xy[8] =
6931       {
6932         { -1, -1,       MV_LEFT  | MV_UP   },
6933         {  0, -1,                  MV_UP   },
6934         { +1, -1,       MV_RIGHT | MV_UP   },
6935         { +1,  0,       MV_RIGHT           },
6936         { +1, +1,       MV_RIGHT | MV_DOWN },
6937         {  0, +1,                  MV_DOWN },
6938         { -1, +1,       MV_LEFT  | MV_DOWN },
6939         { -1,  0,       MV_LEFT            },
6940       };
6941       int start_pos, check_order;
6942       boolean can_clone = FALSE;
6943       int i;
6944
6945       /* check if there is any free field around current position */
6946       for (i = 0; i < 8; i++)
6947       {
6948         int newx = x + check_xy[i].dx;
6949         int newy = y + check_xy[i].dy;
6950
6951         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6952         {
6953           can_clone = TRUE;
6954
6955           break;
6956         }
6957       }
6958
6959       if (can_clone)            /* randomly find an element to clone */
6960       {
6961         can_clone = FALSE;
6962
6963         start_pos = check_pos[RND(8)];
6964         check_order = (RND(2) ? -1 : +1);
6965
6966         for (i = 0; i < 8; i++)
6967         {
6968           int pos_raw = start_pos + i * check_order;
6969           int pos = (pos_raw + 8) % 8;
6970           int newx = x + check_xy[pos].dx;
6971           int newy = y + check_xy[pos].dy;
6972
6973           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6974           {
6975             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6976             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6977
6978             Store[x][y] = Feld[newx][newy];
6979
6980             can_clone = TRUE;
6981
6982             break;
6983           }
6984         }
6985       }
6986
6987       if (can_clone)            /* randomly find a direction to move */
6988       {
6989         can_clone = FALSE;
6990
6991         start_pos = check_pos[RND(8)];
6992         check_order = (RND(2) ? -1 : +1);
6993
6994         for (i = 0; i < 8; i++)
6995         {
6996           int pos_raw = start_pos + i * check_order;
6997           int pos = (pos_raw + 8) % 8;
6998           int newx = x + check_xy[pos].dx;
6999           int newy = y + check_xy[pos].dy;
7000           int new_move_dir = check_xy[pos].dir;
7001
7002           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7003           {
7004             MovDir[x][y] = new_move_dir;
7005             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7006
7007             can_clone = TRUE;
7008
7009             break;
7010           }
7011         }
7012       }
7013
7014       if (can_clone)            /* cloning and moving successful */
7015         return;
7016
7017       /* cannot clone -- try to move towards player */
7018
7019       start_pos = check_pos[MovDir[x][y] & 0x0f];
7020       check_order = (RND(2) ? -1 : +1);
7021
7022       for (i = 0; i < 3; i++)
7023       {
7024         /* first check start_pos, then previous/next or (next/previous) pos */
7025         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7026         int pos = (pos_raw + 8) % 8;
7027         int newx = x + check_xy[pos].dx;
7028         int newy = y + check_xy[pos].dy;
7029         int new_move_dir = check_xy[pos].dir;
7030
7031         if (IS_PLAYER(newx, newy))
7032           break;
7033
7034         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7035         {
7036           MovDir[x][y] = new_move_dir;
7037           MovDelay[x][y] = level.android_move_time * 8 + 1;
7038
7039           break;
7040         }
7041       }
7042     }
7043   }
7044   else if (move_pattern == MV_TURNING_LEFT ||
7045            move_pattern == MV_TURNING_RIGHT ||
7046            move_pattern == MV_TURNING_LEFT_RIGHT ||
7047            move_pattern == MV_TURNING_RIGHT_LEFT ||
7048            move_pattern == MV_TURNING_RANDOM ||
7049            move_pattern == MV_ALL_DIRECTIONS)
7050   {
7051     boolean can_turn_left =
7052       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7053     boolean can_turn_right =
7054       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7055
7056     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7057       return;
7058
7059     if (move_pattern == MV_TURNING_LEFT)
7060       MovDir[x][y] = left_dir;
7061     else if (move_pattern == MV_TURNING_RIGHT)
7062       MovDir[x][y] = right_dir;
7063     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7064       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7065     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7066       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7067     else if (move_pattern == MV_TURNING_RANDOM)
7068       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7069                       can_turn_right && !can_turn_left ? right_dir :
7070                       RND(2) ? left_dir : right_dir);
7071     else if (can_turn_left && can_turn_right)
7072       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7073     else if (can_turn_left)
7074       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7075     else if (can_turn_right)
7076       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7077     else
7078       MovDir[x][y] = back_dir;
7079
7080     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7081   }
7082   else if (move_pattern == MV_HORIZONTAL ||
7083            move_pattern == MV_VERTICAL)
7084   {
7085     if (move_pattern & old_move_dir)
7086       MovDir[x][y] = back_dir;
7087     else if (move_pattern == MV_HORIZONTAL)
7088       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7089     else if (move_pattern == MV_VERTICAL)
7090       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7091
7092     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7093   }
7094   else if (move_pattern & MV_ANY_DIRECTION)
7095   {
7096     MovDir[x][y] = move_pattern;
7097     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7098   }
7099   else if (move_pattern & MV_WIND_DIRECTION)
7100   {
7101     MovDir[x][y] = game.wind_direction;
7102     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7103   }
7104   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7105   {
7106     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7107       MovDir[x][y] = left_dir;
7108     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7109       MovDir[x][y] = right_dir;
7110
7111     if (MovDir[x][y] != old_move_dir)
7112       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7113   }
7114   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7115   {
7116     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7117       MovDir[x][y] = right_dir;
7118     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7119       MovDir[x][y] = left_dir;
7120
7121     if (MovDir[x][y] != old_move_dir)
7122       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7123   }
7124   else if (move_pattern == MV_TOWARDS_PLAYER ||
7125            move_pattern == MV_AWAY_FROM_PLAYER)
7126   {
7127     int attr_x = -1, attr_y = -1;
7128     int newx, newy;
7129     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7130
7131     if (AllPlayersGone)
7132     {
7133       attr_x = ExitX;
7134       attr_y = ExitY;
7135     }
7136     else
7137     {
7138       int i;
7139
7140       for (i = 0; i < MAX_PLAYERS; i++)
7141       {
7142         struct PlayerInfo *player = &stored_player[i];
7143         int jx = player->jx, jy = player->jy;
7144
7145         if (!player->active)
7146           continue;
7147
7148         if (attr_x == -1 ||
7149             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7150         {
7151           attr_x = jx;
7152           attr_y = jy;
7153         }
7154       }
7155     }
7156
7157     MovDir[x][y] = MV_NONE;
7158     if (attr_x < x)
7159       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7160     else if (attr_x > x)
7161       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7162     if (attr_y < y)
7163       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7164     else if (attr_y > y)
7165       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7166
7167     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7168
7169     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7170     {
7171       boolean first_horiz = RND(2);
7172       int new_move_dir = MovDir[x][y];
7173
7174       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7175       {
7176         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7177         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178
7179         return;
7180       }
7181
7182       MovDir[x][y] =
7183         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7184       Moving2Blocked(x, y, &newx, &newy);
7185
7186       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7187         return;
7188
7189       MovDir[x][y] =
7190         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7191       Moving2Blocked(x, y, &newx, &newy);
7192
7193       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7194         return;
7195
7196       MovDir[x][y] = old_move_dir;
7197     }
7198   }
7199   else if (move_pattern == MV_WHEN_PUSHED ||
7200            move_pattern == MV_WHEN_DROPPED)
7201   {
7202     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7203       MovDir[x][y] = MV_NONE;
7204
7205     MovDelay[x][y] = 0;
7206   }
7207   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7208   {
7209     static int test_xy[7][2] =
7210     {
7211       { 0, -1 },
7212       { -1, 0 },
7213       { +1, 0 },
7214       { 0, +1 },
7215       { 0, -1 },
7216       { -1, 0 },
7217       { +1, 0 },
7218     };
7219     static int test_dir[7] =
7220     {
7221       MV_UP,
7222       MV_LEFT,
7223       MV_RIGHT,
7224       MV_DOWN,
7225       MV_UP,
7226       MV_LEFT,
7227       MV_RIGHT,
7228     };
7229     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7230     int move_preference = -1000000;     /* start with very low preference */
7231     int new_move_dir = MV_NONE;
7232     int start_test = RND(4);
7233     int i;
7234
7235     for (i = 0; i < NUM_DIRECTIONS; i++)
7236     {
7237       int move_dir = test_dir[start_test + i];
7238       int move_dir_preference;
7239
7240       xx = x + test_xy[start_test + i][0];
7241       yy = y + test_xy[start_test + i][1];
7242
7243       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7244           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7245       {
7246         new_move_dir = move_dir;
7247
7248         break;
7249       }
7250
7251       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7252         continue;
7253
7254       move_dir_preference = -1 * RunnerVisit[xx][yy];
7255       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7256         move_dir_preference = PlayerVisit[xx][yy];
7257
7258       if (move_dir_preference > move_preference)
7259       {
7260         /* prefer field that has not been visited for the longest time */
7261         move_preference = move_dir_preference;
7262         new_move_dir = move_dir;
7263       }
7264       else if (move_dir_preference == move_preference &&
7265                move_dir == old_move_dir)
7266       {
7267         /* prefer last direction when all directions are preferred equally */
7268         move_preference = move_dir_preference;
7269         new_move_dir = move_dir;
7270       }
7271     }
7272
7273     MovDir[x][y] = new_move_dir;
7274     if (old_move_dir != new_move_dir)
7275       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7276   }
7277 }
7278
7279 static void TurnRound(int x, int y)
7280 {
7281   int direction = MovDir[x][y];
7282
7283   TurnRoundExt(x, y);
7284
7285   GfxDir[x][y] = MovDir[x][y];
7286
7287   if (direction != MovDir[x][y])
7288     GfxFrame[x][y] = 0;
7289
7290   if (MovDelay[x][y])
7291     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7292
7293   ResetGfxFrame(x, y);
7294 }
7295
7296 static boolean JustBeingPushed(int x, int y)
7297 {
7298   int i;
7299
7300   for (i = 0; i < MAX_PLAYERS; i++)
7301   {
7302     struct PlayerInfo *player = &stored_player[i];
7303
7304     if (player->active && player->is_pushing && player->MovPos)
7305     {
7306       int next_jx = player->jx + (player->jx - player->last_jx);
7307       int next_jy = player->jy + (player->jy - player->last_jy);
7308
7309       if (x == next_jx && y == next_jy)
7310         return TRUE;
7311     }
7312   }
7313
7314   return FALSE;
7315 }
7316
7317 void StartMoving(int x, int y)
7318 {
7319   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7320   int element = Feld[x][y];
7321
7322   if (Stop[x][y])
7323     return;
7324
7325   if (MovDelay[x][y] == 0)
7326     GfxAction[x][y] = ACTION_DEFAULT;
7327
7328   if (CAN_FALL(element) && y < lev_fieldy - 1)
7329   {
7330     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7331         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7332       if (JustBeingPushed(x, y))
7333         return;
7334
7335     if (element == EL_QUICKSAND_FULL)
7336     {
7337       if (IS_FREE(x, y + 1))
7338       {
7339         InitMovingField(x, y, MV_DOWN);
7340         started_moving = TRUE;
7341
7342         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7343 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7344         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7345           Store[x][y] = EL_ROCK;
7346 #else
7347         Store[x][y] = EL_ROCK;
7348 #endif
7349
7350         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7351       }
7352       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7353       {
7354         if (!MovDelay[x][y])
7355         {
7356           MovDelay[x][y] = TILEY + 1;
7357
7358           ResetGfxAnimation(x, y);
7359           ResetGfxAnimation(x, y + 1);
7360         }
7361
7362         if (MovDelay[x][y])
7363         {
7364           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7365           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7366
7367           MovDelay[x][y]--;
7368           if (MovDelay[x][y])
7369             return;
7370         }
7371
7372         Feld[x][y] = EL_QUICKSAND_EMPTY;
7373         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7374         Store[x][y + 1] = Store[x][y];
7375         Store[x][y] = 0;
7376
7377         PlayLevelSoundAction(x, y, ACTION_FILLING);
7378       }
7379       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7380       {
7381         if (!MovDelay[x][y])
7382         {
7383           MovDelay[x][y] = TILEY + 1;
7384
7385           ResetGfxAnimation(x, y);
7386           ResetGfxAnimation(x, y + 1);
7387         }
7388
7389         if (MovDelay[x][y])
7390         {
7391           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7392           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7393
7394           MovDelay[x][y]--;
7395           if (MovDelay[x][y])
7396             return;
7397         }
7398
7399         Feld[x][y] = EL_QUICKSAND_EMPTY;
7400         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7401         Store[x][y + 1] = Store[x][y];
7402         Store[x][y] = 0;
7403
7404         PlayLevelSoundAction(x, y, ACTION_FILLING);
7405       }
7406     }
7407     else if (element == EL_QUICKSAND_FAST_FULL)
7408     {
7409       if (IS_FREE(x, y + 1))
7410       {
7411         InitMovingField(x, y, MV_DOWN);
7412         started_moving = TRUE;
7413
7414         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7415 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7416         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7417           Store[x][y] = EL_ROCK;
7418 #else
7419         Store[x][y] = EL_ROCK;
7420 #endif
7421
7422         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7423       }
7424       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7425       {
7426         if (!MovDelay[x][y])
7427         {
7428           MovDelay[x][y] = TILEY + 1;
7429
7430           ResetGfxAnimation(x, y);
7431           ResetGfxAnimation(x, y + 1);
7432         }
7433
7434         if (MovDelay[x][y])
7435         {
7436           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7437           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7438
7439           MovDelay[x][y]--;
7440           if (MovDelay[x][y])
7441             return;
7442         }
7443
7444         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7445         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7446         Store[x][y + 1] = Store[x][y];
7447         Store[x][y] = 0;
7448
7449         PlayLevelSoundAction(x, y, ACTION_FILLING);
7450       }
7451       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7452       {
7453         if (!MovDelay[x][y])
7454         {
7455           MovDelay[x][y] = TILEY + 1;
7456
7457           ResetGfxAnimation(x, y);
7458           ResetGfxAnimation(x, y + 1);
7459         }
7460
7461         if (MovDelay[x][y])
7462         {
7463           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7464           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7465
7466           MovDelay[x][y]--;
7467           if (MovDelay[x][y])
7468             return;
7469         }
7470
7471         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7472         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7473         Store[x][y + 1] = Store[x][y];
7474         Store[x][y] = 0;
7475
7476         PlayLevelSoundAction(x, y, ACTION_FILLING);
7477       }
7478     }
7479     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7480              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7481     {
7482       InitMovingField(x, y, MV_DOWN);
7483       started_moving = TRUE;
7484
7485       Feld[x][y] = EL_QUICKSAND_FILLING;
7486       Store[x][y] = element;
7487
7488       PlayLevelSoundAction(x, y, ACTION_FILLING);
7489     }
7490     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7491              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7492     {
7493       InitMovingField(x, y, MV_DOWN);
7494       started_moving = TRUE;
7495
7496       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7497       Store[x][y] = element;
7498
7499       PlayLevelSoundAction(x, y, ACTION_FILLING);
7500     }
7501     else if (element == EL_MAGIC_WALL_FULL)
7502     {
7503       if (IS_FREE(x, y + 1))
7504       {
7505         InitMovingField(x, y, MV_DOWN);
7506         started_moving = TRUE;
7507
7508         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7509         Store[x][y] = EL_CHANGED(Store[x][y]);
7510       }
7511       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7512       {
7513         if (!MovDelay[x][y])
7514           MovDelay[x][y] = TILEY / 4 + 1;
7515
7516         if (MovDelay[x][y])
7517         {
7518           MovDelay[x][y]--;
7519           if (MovDelay[x][y])
7520             return;
7521         }
7522
7523         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7524         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7525         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7526         Store[x][y] = 0;
7527       }
7528     }
7529     else if (element == EL_BD_MAGIC_WALL_FULL)
7530     {
7531       if (IS_FREE(x, y + 1))
7532       {
7533         InitMovingField(x, y, MV_DOWN);
7534         started_moving = TRUE;
7535
7536         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7537         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7538       }
7539       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7540       {
7541         if (!MovDelay[x][y])
7542           MovDelay[x][y] = TILEY / 4 + 1;
7543
7544         if (MovDelay[x][y])
7545         {
7546           MovDelay[x][y]--;
7547           if (MovDelay[x][y])
7548             return;
7549         }
7550
7551         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7552         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7553         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7554         Store[x][y] = 0;
7555       }
7556     }
7557     else if (element == EL_DC_MAGIC_WALL_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7565         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7566       }
7567       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7568       {
7569         if (!MovDelay[x][y])
7570           MovDelay[x][y] = TILEY / 4 + 1;
7571
7572         if (MovDelay[x][y])
7573         {
7574           MovDelay[x][y]--;
7575           if (MovDelay[x][y])
7576             return;
7577         }
7578
7579         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7580         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7581         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7582         Store[x][y] = 0;
7583       }
7584     }
7585     else if ((CAN_PASS_MAGIC_WALL(element) &&
7586               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7587                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7588              (CAN_PASS_DC_MAGIC_WALL(element) &&
7589               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7590
7591     {
7592       InitMovingField(x, y, MV_DOWN);
7593       started_moving = TRUE;
7594
7595       Feld[x][y] =
7596         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7597          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7598          EL_DC_MAGIC_WALL_FILLING);
7599       Store[x][y] = element;
7600     }
7601     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7602     {
7603       SplashAcid(x, y + 1);
7604
7605       InitMovingField(x, y, MV_DOWN);
7606       started_moving = TRUE;
7607
7608       Store[x][y] = EL_ACID;
7609     }
7610     else if (
7611              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7612               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7613              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7614               CAN_FALL(element) && WasJustFalling[x][y] &&
7615               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7616
7617              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7618               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7619               (Feld[x][y + 1] == EL_BLOCKED)))
7620     {
7621       /* this is needed for a special case not covered by calling "Impact()"
7622          from "ContinueMoving()": if an element moves to a tile directly below
7623          another element which was just falling on that tile (which was empty
7624          in the previous frame), the falling element above would just stop
7625          instead of smashing the element below (in previous version, the above
7626          element was just checked for "moving" instead of "falling", resulting
7627          in incorrect smashes caused by horizontal movement of the above
7628          element; also, the case of the player being the element to smash was
7629          simply not covered here... :-/ ) */
7630
7631       CheckCollision[x][y] = 0;
7632       CheckImpact[x][y] = 0;
7633
7634       Impact(x, y);
7635     }
7636     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7637     {
7638       if (MovDir[x][y] == MV_NONE)
7639       {
7640         InitMovingField(x, y, MV_DOWN);
7641         started_moving = TRUE;
7642       }
7643     }
7644     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7645     {
7646       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7647         MovDir[x][y] = MV_DOWN;
7648
7649       InitMovingField(x, y, MV_DOWN);
7650       started_moving = TRUE;
7651     }
7652     else if (element == EL_AMOEBA_DROP)
7653     {
7654       Feld[x][y] = EL_AMOEBA_GROWING;
7655       Store[x][y] = EL_AMOEBA_WET;
7656     }
7657     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7658               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7659              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7660              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7661     {
7662       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7663                                 (IS_FREE(x - 1, y + 1) ||
7664                                  Feld[x - 1][y + 1] == EL_ACID));
7665       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7666                                 (IS_FREE(x + 1, y + 1) ||
7667                                  Feld[x + 1][y + 1] == EL_ACID));
7668       boolean can_fall_any  = (can_fall_left || can_fall_right);
7669       boolean can_fall_both = (can_fall_left && can_fall_right);
7670       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7671
7672       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7673       {
7674         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7675           can_fall_right = FALSE;
7676         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7677           can_fall_left = FALSE;
7678         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7679           can_fall_right = FALSE;
7680         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7681           can_fall_left = FALSE;
7682
7683         can_fall_any  = (can_fall_left || can_fall_right);
7684         can_fall_both = FALSE;
7685       }
7686
7687       if (can_fall_both)
7688       {
7689         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7690           can_fall_right = FALSE;       /* slip down on left side */
7691         else
7692           can_fall_left = !(can_fall_right = RND(2));
7693
7694         can_fall_both = FALSE;
7695       }
7696
7697       if (can_fall_any)
7698       {
7699         /* if not determined otherwise, prefer left side for slipping down */
7700         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7701         started_moving = TRUE;
7702       }
7703     }
7704     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7705     {
7706       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7707       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7708       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7709       int belt_dir = game.belt_dir[belt_nr];
7710
7711       if ((belt_dir == MV_LEFT  && left_is_free) ||
7712           (belt_dir == MV_RIGHT && right_is_free))
7713       {
7714         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7715
7716         InitMovingField(x, y, belt_dir);
7717         started_moving = TRUE;
7718
7719         Pushed[x][y] = TRUE;
7720         Pushed[nextx][y] = TRUE;
7721
7722         GfxAction[x][y] = ACTION_DEFAULT;
7723       }
7724       else
7725       {
7726         MovDir[x][y] = 0;       /* if element was moving, stop it */
7727       }
7728     }
7729   }
7730
7731   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7732   if (CAN_MOVE(element) && !started_moving)
7733   {
7734     int move_pattern = element_info[element].move_pattern;
7735     int newx, newy;
7736
7737     Moving2Blocked(x, y, &newx, &newy);
7738
7739     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7740       return;
7741
7742     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7743         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7744     {
7745       WasJustMoving[x][y] = 0;
7746       CheckCollision[x][y] = 0;
7747
7748       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7749
7750       if (Feld[x][y] != element)        /* element has changed */
7751         return;
7752     }
7753
7754     if (!MovDelay[x][y])        /* start new movement phase */
7755     {
7756       /* all objects that can change their move direction after each step
7757          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7758
7759       if (element != EL_YAMYAM &&
7760           element != EL_DARK_YAMYAM &&
7761           element != EL_PACMAN &&
7762           !(move_pattern & MV_ANY_DIRECTION) &&
7763           move_pattern != MV_TURNING_LEFT &&
7764           move_pattern != MV_TURNING_RIGHT &&
7765           move_pattern != MV_TURNING_LEFT_RIGHT &&
7766           move_pattern != MV_TURNING_RIGHT_LEFT &&
7767           move_pattern != MV_TURNING_RANDOM)
7768       {
7769         TurnRound(x, y);
7770
7771         if (MovDelay[x][y] && (element == EL_BUG ||
7772                                element == EL_SPACESHIP ||
7773                                element == EL_SP_SNIKSNAK ||
7774                                element == EL_SP_ELECTRON ||
7775                                element == EL_MOLE))
7776           TEST_DrawLevelField(x, y);
7777       }
7778     }
7779
7780     if (MovDelay[x][y])         /* wait some time before next movement */
7781     {
7782       MovDelay[x][y]--;
7783
7784       if (element == EL_ROBOT ||
7785           element == EL_YAMYAM ||
7786           element == EL_DARK_YAMYAM)
7787       {
7788         DrawLevelElementAnimationIfNeeded(x, y, element);
7789         PlayLevelSoundAction(x, y, ACTION_WAITING);
7790       }
7791       else if (element == EL_SP_ELECTRON)
7792         DrawLevelElementAnimationIfNeeded(x, y, element);
7793       else if (element == EL_DRAGON)
7794       {
7795         int i;
7796         int dir = MovDir[x][y];
7797         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7798         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7799         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7800                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7801                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7802                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7803         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7804
7805         GfxAction[x][y] = ACTION_ATTACKING;
7806
7807         if (IS_PLAYER(x, y))
7808           DrawPlayerField(x, y);
7809         else
7810           TEST_DrawLevelField(x, y);
7811
7812         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7813
7814         for (i = 1; i <= 3; i++)
7815         {
7816           int xx = x + i * dx;
7817           int yy = y + i * dy;
7818           int sx = SCREENX(xx);
7819           int sy = SCREENY(yy);
7820           int flame_graphic = graphic + (i - 1);
7821
7822           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7823             break;
7824
7825           if (MovDelay[x][y])
7826           {
7827             int flamed = MovingOrBlocked2Element(xx, yy);
7828
7829             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7830               Bang(xx, yy);
7831             else
7832               RemoveMovingField(xx, yy);
7833
7834             ChangeDelay[xx][yy] = 0;
7835
7836             Feld[xx][yy] = EL_FLAMES;
7837
7838             if (IN_SCR_FIELD(sx, sy))
7839             {
7840               TEST_DrawLevelFieldCrumbled(xx, yy);
7841               DrawGraphic(sx, sy, flame_graphic, frame);
7842             }
7843           }
7844           else
7845           {
7846             if (Feld[xx][yy] == EL_FLAMES)
7847               Feld[xx][yy] = EL_EMPTY;
7848             TEST_DrawLevelField(xx, yy);
7849           }
7850         }
7851       }
7852
7853       if (MovDelay[x][y])       /* element still has to wait some time */
7854       {
7855         PlayLevelSoundAction(x, y, ACTION_WAITING);
7856
7857         return;
7858       }
7859     }
7860
7861     /* now make next step */
7862
7863     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7864
7865     if (DONT_COLLIDE_WITH(element) &&
7866         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7867         !PLAYER_ENEMY_PROTECTED(newx, newy))
7868     {
7869       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7870
7871       return;
7872     }
7873
7874     else if (CAN_MOVE_INTO_ACID(element) &&
7875              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7876              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7877              (MovDir[x][y] == MV_DOWN ||
7878               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7879     {
7880       SplashAcid(newx, newy);
7881       Store[x][y] = EL_ACID;
7882     }
7883     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7884     {
7885       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7886           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7887           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7888           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7889       {
7890         RemoveField(x, y);
7891         TEST_DrawLevelField(x, y);
7892
7893         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7894         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7895           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7896
7897         local_player->friends_still_needed--;
7898         if (!local_player->friends_still_needed &&
7899             !local_player->GameOver && AllPlayersGone)
7900           PlayerWins(local_player);
7901
7902         return;
7903       }
7904       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7905       {
7906         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7907           TEST_DrawLevelField(newx, newy);
7908         else
7909           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7910       }
7911       else if (!IS_FREE(newx, newy))
7912       {
7913         GfxAction[x][y] = ACTION_WAITING;
7914
7915         if (IS_PLAYER(x, y))
7916           DrawPlayerField(x, y);
7917         else
7918           TEST_DrawLevelField(x, y);
7919
7920         return;
7921       }
7922     }
7923     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7924     {
7925       if (IS_FOOD_PIG(Feld[newx][newy]))
7926       {
7927         if (IS_MOVING(newx, newy))
7928           RemoveMovingField(newx, newy);
7929         else
7930         {
7931           Feld[newx][newy] = EL_EMPTY;
7932           TEST_DrawLevelField(newx, newy);
7933         }
7934
7935         PlayLevelSound(x, y, SND_PIG_DIGGING);
7936       }
7937       else if (!IS_FREE(newx, newy))
7938       {
7939         if (IS_PLAYER(x, y))
7940           DrawPlayerField(x, y);
7941         else
7942           TEST_DrawLevelField(x, y);
7943
7944         return;
7945       }
7946     }
7947     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7948     {
7949       if (Store[x][y] != EL_EMPTY)
7950       {
7951         boolean can_clone = FALSE;
7952         int xx, yy;
7953
7954         /* check if element to clone is still there */
7955         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7956         {
7957           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7958           {
7959             can_clone = TRUE;
7960
7961             break;
7962           }
7963         }
7964
7965         /* cannot clone or target field not free anymore -- do not clone */
7966         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7967           Store[x][y] = EL_EMPTY;
7968       }
7969
7970       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7971       {
7972         if (IS_MV_DIAGONAL(MovDir[x][y]))
7973         {
7974           int diagonal_move_dir = MovDir[x][y];
7975           int stored = Store[x][y];
7976           int change_delay = 8;
7977           int graphic;
7978
7979           /* android is moving diagonally */
7980
7981           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7982
7983           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7984           GfxElement[x][y] = EL_EMC_ANDROID;
7985           GfxAction[x][y] = ACTION_SHRINKING;
7986           GfxDir[x][y] = diagonal_move_dir;
7987           ChangeDelay[x][y] = change_delay;
7988
7989           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7990                                    GfxDir[x][y]);
7991
7992           DrawLevelGraphicAnimation(x, y, graphic);
7993           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7994
7995           if (Feld[newx][newy] == EL_ACID)
7996           {
7997             SplashAcid(newx, newy);
7998
7999             return;
8000           }
8001
8002           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8003
8004           Store[newx][newy] = EL_EMC_ANDROID;
8005           GfxElement[newx][newy] = EL_EMC_ANDROID;
8006           GfxAction[newx][newy] = ACTION_GROWING;
8007           GfxDir[newx][newy] = diagonal_move_dir;
8008           ChangeDelay[newx][newy] = change_delay;
8009
8010           graphic = el_act_dir2img(GfxElement[newx][newy],
8011                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8012
8013           DrawLevelGraphicAnimation(newx, newy, graphic);
8014           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8015
8016           return;
8017         }
8018         else
8019         {
8020           Feld[newx][newy] = EL_EMPTY;
8021           TEST_DrawLevelField(newx, newy);
8022
8023           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8024         }
8025       }
8026       else if (!IS_FREE(newx, newy))
8027       {
8028         return;
8029       }
8030     }
8031     else if (IS_CUSTOM_ELEMENT(element) &&
8032              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8033     {
8034       if (!DigFieldByCE(newx, newy, element))
8035         return;
8036
8037       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8038       {
8039         RunnerVisit[x][y] = FrameCounter;
8040         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8041       }
8042     }
8043     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8044     {
8045       if (!IS_FREE(newx, newy))
8046       {
8047         if (IS_PLAYER(x, y))
8048           DrawPlayerField(x, y);
8049         else
8050           TEST_DrawLevelField(x, y);
8051
8052         return;
8053       }
8054       else
8055       {
8056         boolean wanna_flame = !RND(10);
8057         int dx = newx - x, dy = newy - y;
8058         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8059         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8060         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8061                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8062         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8063                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8064
8065         if ((wanna_flame ||
8066              IS_CLASSIC_ENEMY(element1) ||
8067              IS_CLASSIC_ENEMY(element2)) &&
8068             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8069             element1 != EL_FLAMES && element2 != EL_FLAMES)
8070         {
8071           ResetGfxAnimation(x, y);
8072           GfxAction[x][y] = ACTION_ATTACKING;
8073
8074           if (IS_PLAYER(x, y))
8075             DrawPlayerField(x, y);
8076           else
8077             TEST_DrawLevelField(x, y);
8078
8079           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8080
8081           MovDelay[x][y] = 50;
8082
8083           Feld[newx][newy] = EL_FLAMES;
8084           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8085             Feld[newx1][newy1] = EL_FLAMES;
8086           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8087             Feld[newx2][newy2] = EL_FLAMES;
8088
8089           return;
8090         }
8091       }
8092     }
8093     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8094              Feld[newx][newy] == EL_DIAMOND)
8095     {
8096       if (IS_MOVING(newx, newy))
8097         RemoveMovingField(newx, newy);
8098       else
8099       {
8100         Feld[newx][newy] = EL_EMPTY;
8101         TEST_DrawLevelField(newx, newy);
8102       }
8103
8104       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8105     }
8106     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8107              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8108     {
8109       if (AmoebaNr[newx][newy])
8110       {
8111         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8112         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8113             Feld[newx][newy] == EL_BD_AMOEBA)
8114           AmoebaCnt[AmoebaNr[newx][newy]]--;
8115       }
8116
8117       if (IS_MOVING(newx, newy))
8118       {
8119         RemoveMovingField(newx, newy);
8120       }
8121       else
8122       {
8123         Feld[newx][newy] = EL_EMPTY;
8124         TEST_DrawLevelField(newx, newy);
8125       }
8126
8127       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8128     }
8129     else if ((element == EL_PACMAN || element == EL_MOLE)
8130              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8131     {
8132       if (AmoebaNr[newx][newy])
8133       {
8134         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8135         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8136             Feld[newx][newy] == EL_BD_AMOEBA)
8137           AmoebaCnt[AmoebaNr[newx][newy]]--;
8138       }
8139
8140       if (element == EL_MOLE)
8141       {
8142         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8143         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8144
8145         ResetGfxAnimation(x, y);
8146         GfxAction[x][y] = ACTION_DIGGING;
8147         TEST_DrawLevelField(x, y);
8148
8149         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8150
8151         return;                         /* wait for shrinking amoeba */
8152       }
8153       else      /* element == EL_PACMAN */
8154       {
8155         Feld[newx][newy] = EL_EMPTY;
8156         TEST_DrawLevelField(newx, newy);
8157         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8158       }
8159     }
8160     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8161              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8162               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8163     {
8164       /* wait for shrinking amoeba to completely disappear */
8165       return;
8166     }
8167     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8168     {
8169       /* object was running against a wall */
8170
8171       TurnRound(x, y);
8172
8173       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8174         DrawLevelElementAnimation(x, y, element);
8175
8176       if (DONT_TOUCH(element))
8177         TestIfBadThingTouchesPlayer(x, y);
8178
8179       return;
8180     }
8181
8182     InitMovingField(x, y, MovDir[x][y]);
8183
8184     PlayLevelSoundAction(x, y, ACTION_MOVING);
8185   }
8186
8187   if (MovDir[x][y])
8188     ContinueMoving(x, y);
8189 }
8190
8191 void ContinueMoving(int x, int y)
8192 {
8193   int element = Feld[x][y];
8194   struct ElementInfo *ei = &element_info[element];
8195   int direction = MovDir[x][y];
8196   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8197   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8198   int newx = x + dx, newy = y + dy;
8199   int stored = Store[x][y];
8200   int stored_new = Store[newx][newy];
8201   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8202   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8203   boolean last_line = (newy == lev_fieldy - 1);
8204
8205   MovPos[x][y] += getElementMoveStepsize(x, y);
8206
8207   if (pushed_by_player) /* special case: moving object pushed by player */
8208     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8209
8210   if (ABS(MovPos[x][y]) < TILEX)
8211   {
8212     TEST_DrawLevelField(x, y);
8213
8214     return;     /* element is still moving */
8215   }
8216
8217   /* element reached destination field */
8218
8219   Feld[x][y] = EL_EMPTY;
8220   Feld[newx][newy] = element;
8221   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8222
8223   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8224   {
8225     element = Feld[newx][newy] = EL_ACID;
8226   }
8227   else if (element == EL_MOLE)
8228   {
8229     Feld[x][y] = EL_SAND;
8230
8231     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8232   }
8233   else if (element == EL_QUICKSAND_FILLING)
8234   {
8235     element = Feld[newx][newy] = get_next_element(element);
8236     Store[newx][newy] = Store[x][y];
8237   }
8238   else if (element == EL_QUICKSAND_EMPTYING)
8239   {
8240     Feld[x][y] = get_next_element(element);
8241     element = Feld[newx][newy] = Store[x][y];
8242   }
8243   else if (element == EL_QUICKSAND_FAST_FILLING)
8244   {
8245     element = Feld[newx][newy] = get_next_element(element);
8246     Store[newx][newy] = Store[x][y];
8247   }
8248   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8249   {
8250     Feld[x][y] = get_next_element(element);
8251     element = Feld[newx][newy] = Store[x][y];
8252   }
8253   else if (element == EL_MAGIC_WALL_FILLING)
8254   {
8255     element = Feld[newx][newy] = get_next_element(element);
8256     if (!game.magic_wall_active)
8257       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8258     Store[newx][newy] = Store[x][y];
8259   }
8260   else if (element == EL_MAGIC_WALL_EMPTYING)
8261   {
8262     Feld[x][y] = get_next_element(element);
8263     if (!game.magic_wall_active)
8264       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8265     element = Feld[newx][newy] = Store[x][y];
8266
8267     InitField(newx, newy, FALSE);
8268   }
8269   else if (element == EL_BD_MAGIC_WALL_FILLING)
8270   {
8271     element = Feld[newx][newy] = get_next_element(element);
8272     if (!game.magic_wall_active)
8273       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8274     Store[newx][newy] = Store[x][y];
8275   }
8276   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8277   {
8278     Feld[x][y] = get_next_element(element);
8279     if (!game.magic_wall_active)
8280       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8281     element = Feld[newx][newy] = Store[x][y];
8282
8283     InitField(newx, newy, FALSE);
8284   }
8285   else if (element == EL_DC_MAGIC_WALL_FILLING)
8286   {
8287     element = Feld[newx][newy] = get_next_element(element);
8288     if (!game.magic_wall_active)
8289       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8290     Store[newx][newy] = Store[x][y];
8291   }
8292   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8293   {
8294     Feld[x][y] = get_next_element(element);
8295     if (!game.magic_wall_active)
8296       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8297     element = Feld[newx][newy] = Store[x][y];
8298
8299     InitField(newx, newy, FALSE);
8300   }
8301   else if (element == EL_AMOEBA_DROPPING)
8302   {
8303     Feld[x][y] = get_next_element(element);
8304     element = Feld[newx][newy] = Store[x][y];
8305   }
8306   else if (element == EL_SOKOBAN_OBJECT)
8307   {
8308     if (Back[x][y])
8309       Feld[x][y] = Back[x][y];
8310
8311     if (Back[newx][newy])
8312       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8313
8314     Back[x][y] = Back[newx][newy] = 0;
8315   }
8316
8317   Store[x][y] = EL_EMPTY;
8318   MovPos[x][y] = 0;
8319   MovDir[x][y] = 0;
8320   MovDelay[x][y] = 0;
8321
8322   MovDelay[newx][newy] = 0;
8323
8324   if (CAN_CHANGE_OR_HAS_ACTION(element))
8325   {
8326     /* copy element change control values to new field */
8327     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8328     ChangePage[newx][newy]  = ChangePage[x][y];
8329     ChangeCount[newx][newy] = ChangeCount[x][y];
8330     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8331   }
8332
8333   CustomValue[newx][newy] = CustomValue[x][y];
8334
8335   ChangeDelay[x][y] = 0;
8336   ChangePage[x][y] = -1;
8337   ChangeCount[x][y] = 0;
8338   ChangeEvent[x][y] = -1;
8339
8340   CustomValue[x][y] = 0;
8341
8342   /* copy animation control values to new field */
8343   GfxFrame[newx][newy]  = GfxFrame[x][y];
8344   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8345   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8346   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8347
8348   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8349
8350   /* some elements can leave other elements behind after moving */
8351   if (ei->move_leave_element != EL_EMPTY &&
8352       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8353       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8354   {
8355     int move_leave_element = ei->move_leave_element;
8356
8357     /* this makes it possible to leave the removed element again */
8358     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8359       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8360
8361     Feld[x][y] = move_leave_element;
8362
8363     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8364       MovDir[x][y] = direction;
8365
8366     InitField(x, y, FALSE);
8367
8368     if (GFX_CRUMBLED(Feld[x][y]))
8369       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8370
8371     if (ELEM_IS_PLAYER(move_leave_element))
8372       RelocatePlayer(x, y, move_leave_element);
8373   }
8374
8375   /* do this after checking for left-behind element */
8376   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8377
8378   if (!CAN_MOVE(element) ||
8379       (CAN_FALL(element) && direction == MV_DOWN &&
8380        (element == EL_SPRING ||
8381         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8382         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8383     GfxDir[x][y] = MovDir[newx][newy] = 0;
8384
8385   TEST_DrawLevelField(x, y);
8386   TEST_DrawLevelField(newx, newy);
8387
8388   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8389
8390   /* prevent pushed element from moving on in pushed direction */
8391   if (pushed_by_player && CAN_MOVE(element) &&
8392       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8393       !(element_info[element].move_pattern & direction))
8394     TurnRound(newx, newy);
8395
8396   /* prevent elements on conveyor belt from moving on in last direction */
8397   if (pushed_by_conveyor && CAN_FALL(element) &&
8398       direction & MV_HORIZONTAL)
8399     MovDir[newx][newy] = 0;
8400
8401   if (!pushed_by_player)
8402   {
8403     int nextx = newx + dx, nexty = newy + dy;
8404     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8405
8406     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8407
8408     if (CAN_FALL(element) && direction == MV_DOWN)
8409       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8410
8411     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8412       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8413
8414     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8415       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8416   }
8417
8418   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8419   {
8420     TestIfBadThingTouchesPlayer(newx, newy);
8421     TestIfBadThingTouchesFriend(newx, newy);
8422
8423     if (!IS_CUSTOM_ELEMENT(element))
8424       TestIfBadThingTouchesOtherBadThing(newx, newy);
8425   }
8426   else if (element == EL_PENGUIN)
8427     TestIfFriendTouchesBadThing(newx, newy);
8428
8429   if (DONT_GET_HIT_BY(element))
8430   {
8431     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8432   }
8433
8434   /* give the player one last chance (one more frame) to move away */
8435   if (CAN_FALL(element) && direction == MV_DOWN &&
8436       (last_line || (!IS_FREE(x, newy + 1) &&
8437                      (!IS_PLAYER(x, newy + 1) ||
8438                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8439     Impact(x, newy);
8440
8441   if (pushed_by_player && !game.use_change_when_pushing_bug)
8442   {
8443     int push_side = MV_DIR_OPPOSITE(direction);
8444     struct PlayerInfo *player = PLAYERINFO(x, y);
8445
8446     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8447                                player->index_bit, push_side);
8448     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8449                                         player->index_bit, push_side);
8450   }
8451
8452   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8453     MovDelay[newx][newy] = 1;
8454
8455   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8456
8457   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8458   TestIfElementHitsCustomElement(newx, newy, direction);
8459   TestIfPlayerTouchesCustomElement(newx, newy);
8460   TestIfElementTouchesCustomElement(newx, newy);
8461
8462   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8463       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8464     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8465                              MV_DIR_OPPOSITE(direction));
8466 }
8467
8468 int AmoebeNachbarNr(int ax, int ay)
8469 {
8470   int i;
8471   int element = Feld[ax][ay];
8472   int group_nr = 0;
8473   static int xy[4][2] =
8474   {
8475     { 0, -1 },
8476     { -1, 0 },
8477     { +1, 0 },
8478     { 0, +1 }
8479   };
8480
8481   for (i = 0; i < NUM_DIRECTIONS; i++)
8482   {
8483     int x = ax + xy[i][0];
8484     int y = ay + xy[i][1];
8485
8486     if (!IN_LEV_FIELD(x, y))
8487       continue;
8488
8489     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8490       group_nr = AmoebaNr[x][y];
8491   }
8492
8493   return group_nr;
8494 }
8495
8496 void AmoebenVereinigen(int ax, int ay)
8497 {
8498   int i, x, y, xx, yy;
8499   int new_group_nr = AmoebaNr[ax][ay];
8500   static int xy[4][2] =
8501   {
8502     { 0, -1 },
8503     { -1, 0 },
8504     { +1, 0 },
8505     { 0, +1 }
8506   };
8507
8508   if (new_group_nr == 0)
8509     return;
8510
8511   for (i = 0; i < NUM_DIRECTIONS; i++)
8512   {
8513     x = ax + xy[i][0];
8514     y = ay + xy[i][1];
8515
8516     if (!IN_LEV_FIELD(x, y))
8517       continue;
8518
8519     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8520          Feld[x][y] == EL_BD_AMOEBA ||
8521          Feld[x][y] == EL_AMOEBA_DEAD) &&
8522         AmoebaNr[x][y] != new_group_nr)
8523     {
8524       int old_group_nr = AmoebaNr[x][y];
8525
8526       if (old_group_nr == 0)
8527         return;
8528
8529       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8530       AmoebaCnt[old_group_nr] = 0;
8531       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8532       AmoebaCnt2[old_group_nr] = 0;
8533
8534       SCAN_PLAYFIELD(xx, yy)
8535       {
8536         if (AmoebaNr[xx][yy] == old_group_nr)
8537           AmoebaNr[xx][yy] = new_group_nr;
8538       }
8539     }
8540   }
8541 }
8542
8543 void AmoebeUmwandeln(int ax, int ay)
8544 {
8545   int i, x, y;
8546
8547   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8548   {
8549     int group_nr = AmoebaNr[ax][ay];
8550
8551 #ifdef DEBUG
8552     if (group_nr == 0)
8553     {
8554       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8555       printf("AmoebeUmwandeln(): This should never happen!\n");
8556       return;
8557     }
8558 #endif
8559
8560     SCAN_PLAYFIELD(x, y)
8561     {
8562       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8563       {
8564         AmoebaNr[x][y] = 0;
8565         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8566       }
8567     }
8568
8569     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8570                             SND_AMOEBA_TURNING_TO_GEM :
8571                             SND_AMOEBA_TURNING_TO_ROCK));
8572     Bang(ax, ay);
8573   }
8574   else
8575   {
8576     static int xy[4][2] =
8577     {
8578       { 0, -1 },
8579       { -1, 0 },
8580       { +1, 0 },
8581       { 0, +1 }
8582     };
8583
8584     for (i = 0; i < NUM_DIRECTIONS; i++)
8585     {
8586       x = ax + xy[i][0];
8587       y = ay + xy[i][1];
8588
8589       if (!IN_LEV_FIELD(x, y))
8590         continue;
8591
8592       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8593       {
8594         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8595                               SND_AMOEBA_TURNING_TO_GEM :
8596                               SND_AMOEBA_TURNING_TO_ROCK));
8597         Bang(x, y);
8598       }
8599     }
8600   }
8601 }
8602
8603 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8604 {
8605   int x, y;
8606   int group_nr = AmoebaNr[ax][ay];
8607   boolean done = FALSE;
8608
8609 #ifdef DEBUG
8610   if (group_nr == 0)
8611   {
8612     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8613     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8614     return;
8615   }
8616 #endif
8617
8618   SCAN_PLAYFIELD(x, y)
8619   {
8620     if (AmoebaNr[x][y] == group_nr &&
8621         (Feld[x][y] == EL_AMOEBA_DEAD ||
8622          Feld[x][y] == EL_BD_AMOEBA ||
8623          Feld[x][y] == EL_AMOEBA_GROWING))
8624     {
8625       AmoebaNr[x][y] = 0;
8626       Feld[x][y] = new_element;
8627       InitField(x, y, FALSE);
8628       TEST_DrawLevelField(x, y);
8629       done = TRUE;
8630     }
8631   }
8632
8633   if (done)
8634     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8635                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8636                             SND_BD_AMOEBA_TURNING_TO_GEM));
8637 }
8638
8639 void AmoebeWaechst(int x, int y)
8640 {
8641   static unsigned int sound_delay = 0;
8642   static unsigned int sound_delay_value = 0;
8643
8644   if (!MovDelay[x][y])          /* start new growing cycle */
8645   {
8646     MovDelay[x][y] = 7;
8647
8648     if (DelayReached(&sound_delay, sound_delay_value))
8649     {
8650       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8651       sound_delay_value = 30;
8652     }
8653   }
8654
8655   if (MovDelay[x][y])           /* wait some time before growing bigger */
8656   {
8657     MovDelay[x][y]--;
8658     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8659     {
8660       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8661                                            6 - MovDelay[x][y]);
8662
8663       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8664     }
8665
8666     if (!MovDelay[x][y])
8667     {
8668       Feld[x][y] = Store[x][y];
8669       Store[x][y] = 0;
8670       TEST_DrawLevelField(x, y);
8671     }
8672   }
8673 }
8674
8675 void AmoebaDisappearing(int x, int y)
8676 {
8677   static unsigned int sound_delay = 0;
8678   static unsigned int sound_delay_value = 0;
8679
8680   if (!MovDelay[x][y])          /* start new shrinking cycle */
8681   {
8682     MovDelay[x][y] = 7;
8683
8684     if (DelayReached(&sound_delay, sound_delay_value))
8685       sound_delay_value = 30;
8686   }
8687
8688   if (MovDelay[x][y])           /* wait some time before shrinking */
8689   {
8690     MovDelay[x][y]--;
8691     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8692     {
8693       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8694                                            6 - MovDelay[x][y]);
8695
8696       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8697     }
8698
8699     if (!MovDelay[x][y])
8700     {
8701       Feld[x][y] = EL_EMPTY;
8702       TEST_DrawLevelField(x, y);
8703
8704       /* don't let mole enter this field in this cycle;
8705          (give priority to objects falling to this field from above) */
8706       Stop[x][y] = TRUE;
8707     }
8708   }
8709 }
8710
8711 void AmoebeAbleger(int ax, int ay)
8712 {
8713   int i;
8714   int element = Feld[ax][ay];
8715   int graphic = el2img(element);
8716   int newax = ax, neway = ay;
8717   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8718   static int xy[4][2] =
8719   {
8720     { 0, -1 },
8721     { -1, 0 },
8722     { +1, 0 },
8723     { 0, +1 }
8724   };
8725
8726   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8727   {
8728     Feld[ax][ay] = EL_AMOEBA_DEAD;
8729     TEST_DrawLevelField(ax, ay);
8730     return;
8731   }
8732
8733   if (IS_ANIMATED(graphic))
8734     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8735
8736   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8737     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8738
8739   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8740   {
8741     MovDelay[ax][ay]--;
8742     if (MovDelay[ax][ay])
8743       return;
8744   }
8745
8746   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8747   {
8748     int start = RND(4);
8749     int x = ax + xy[start][0];
8750     int y = ay + xy[start][1];
8751
8752     if (!IN_LEV_FIELD(x, y))
8753       return;
8754
8755     if (IS_FREE(x, y) ||
8756         CAN_GROW_INTO(Feld[x][y]) ||
8757         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8758         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8759     {
8760       newax = x;
8761       neway = y;
8762     }
8763
8764     if (newax == ax && neway == ay)
8765       return;
8766   }
8767   else                          /* normal or "filled" (BD style) amoeba */
8768   {
8769     int start = RND(4);
8770     boolean waiting_for_player = FALSE;
8771
8772     for (i = 0; i < NUM_DIRECTIONS; i++)
8773     {
8774       int j = (start + i) % 4;
8775       int x = ax + xy[j][0];
8776       int y = ay + xy[j][1];
8777
8778       if (!IN_LEV_FIELD(x, y))
8779         continue;
8780
8781       if (IS_FREE(x, y) ||
8782           CAN_GROW_INTO(Feld[x][y]) ||
8783           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8784           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8785       {
8786         newax = x;
8787         neway = y;
8788         break;
8789       }
8790       else if (IS_PLAYER(x, y))
8791         waiting_for_player = TRUE;
8792     }
8793
8794     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8795     {
8796       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8797       {
8798         Feld[ax][ay] = EL_AMOEBA_DEAD;
8799         TEST_DrawLevelField(ax, ay);
8800         AmoebaCnt[AmoebaNr[ax][ay]]--;
8801
8802         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8803         {
8804           if (element == EL_AMOEBA_FULL)
8805             AmoebeUmwandeln(ax, ay);
8806           else if (element == EL_BD_AMOEBA)
8807             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8808         }
8809       }
8810       return;
8811     }
8812     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8813     {
8814       /* amoeba gets larger by growing in some direction */
8815
8816       int new_group_nr = AmoebaNr[ax][ay];
8817
8818 #ifdef DEBUG
8819   if (new_group_nr == 0)
8820   {
8821     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8822     printf("AmoebeAbleger(): This should never happen!\n");
8823     return;
8824   }
8825 #endif
8826
8827       AmoebaNr[newax][neway] = new_group_nr;
8828       AmoebaCnt[new_group_nr]++;
8829       AmoebaCnt2[new_group_nr]++;
8830
8831       /* if amoeba touches other amoeba(s) after growing, unify them */
8832       AmoebenVereinigen(newax, neway);
8833
8834       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8835       {
8836         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8837         return;
8838       }
8839     }
8840   }
8841
8842   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8843       (neway == lev_fieldy - 1 && newax != ax))
8844   {
8845     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8846     Store[newax][neway] = element;
8847   }
8848   else if (neway == ay || element == EL_EMC_DRIPPER)
8849   {
8850     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8851
8852     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8853   }
8854   else
8855   {
8856     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8857     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8858     Store[ax][ay] = EL_AMOEBA_DROP;
8859     ContinueMoving(ax, ay);
8860     return;
8861   }
8862
8863   TEST_DrawLevelField(newax, neway);
8864 }
8865
8866 void Life(int ax, int ay)
8867 {
8868   int x1, y1, x2, y2;
8869   int life_time = 40;
8870   int element = Feld[ax][ay];
8871   int graphic = el2img(element);
8872   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8873                          level.biomaze);
8874   boolean changed = FALSE;
8875
8876   if (IS_ANIMATED(graphic))
8877     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8878
8879   if (Stop[ax][ay])
8880     return;
8881
8882   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8883     MovDelay[ax][ay] = life_time;
8884
8885   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8886   {
8887     MovDelay[ax][ay]--;
8888     if (MovDelay[ax][ay])
8889       return;
8890   }
8891
8892   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8893   {
8894     int xx = ax+x1, yy = ay+y1;
8895     int nachbarn = 0;
8896
8897     if (!IN_LEV_FIELD(xx, yy))
8898       continue;
8899
8900     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8901     {
8902       int x = xx+x2, y = yy+y2;
8903
8904       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8905         continue;
8906
8907       if (((Feld[x][y] == element ||
8908             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8909            !Stop[x][y]) ||
8910           (IS_FREE(x, y) && Stop[x][y]))
8911         nachbarn++;
8912     }
8913
8914     if (xx == ax && yy == ay)           /* field in the middle */
8915     {
8916       if (nachbarn < life_parameter[0] ||
8917           nachbarn > life_parameter[1])
8918       {
8919         Feld[xx][yy] = EL_EMPTY;
8920         if (!Stop[xx][yy])
8921           TEST_DrawLevelField(xx, yy);
8922         Stop[xx][yy] = TRUE;
8923         changed = TRUE;
8924       }
8925     }
8926     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8927     {                                   /* free border field */
8928       if (nachbarn >= life_parameter[2] &&
8929           nachbarn <= life_parameter[3])
8930       {
8931         Feld[xx][yy] = element;
8932         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8933         if (!Stop[xx][yy])
8934           TEST_DrawLevelField(xx, yy);
8935         Stop[xx][yy] = TRUE;
8936         changed = TRUE;
8937       }
8938     }
8939   }
8940
8941   if (changed)
8942     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8943                    SND_GAME_OF_LIFE_GROWING);
8944 }
8945
8946 static void InitRobotWheel(int x, int y)
8947 {
8948   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8949 }
8950
8951 static void RunRobotWheel(int x, int y)
8952 {
8953   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8954 }
8955
8956 static void StopRobotWheel(int x, int y)
8957 {
8958   if (ZX == x && ZY == y)
8959   {
8960     ZX = ZY = -1;
8961
8962     game.robot_wheel_active = FALSE;
8963   }
8964 }
8965
8966 static void InitTimegateWheel(int x, int y)
8967 {
8968   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8969 }
8970
8971 static void RunTimegateWheel(int x, int y)
8972 {
8973   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8974 }
8975
8976 static void InitMagicBallDelay(int x, int y)
8977 {
8978   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8979 }
8980
8981 static void ActivateMagicBall(int bx, int by)
8982 {
8983   int x, y;
8984
8985   if (level.ball_random)
8986   {
8987     int pos_border = RND(8);    /* select one of the eight border elements */
8988     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8989     int xx = pos_content % 3;
8990     int yy = pos_content / 3;
8991
8992     x = bx - 1 + xx;
8993     y = by - 1 + yy;
8994
8995     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8996       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8997   }
8998   else
8999   {
9000     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9001     {
9002       int xx = x - bx + 1;
9003       int yy = y - by + 1;
9004
9005       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9006         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9007     }
9008   }
9009
9010   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9011 }
9012
9013 void CheckExit(int x, int y)
9014 {
9015   if (local_player->gems_still_needed > 0 ||
9016       local_player->sokobanfields_still_needed > 0 ||
9017       local_player->lights_still_needed > 0)
9018   {
9019     int element = Feld[x][y];
9020     int graphic = el2img(element);
9021
9022     if (IS_ANIMATED(graphic))
9023       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9024
9025     return;
9026   }
9027
9028   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9029     return;
9030
9031   Feld[x][y] = EL_EXIT_OPENING;
9032
9033   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9034 }
9035
9036 void CheckExitEM(int x, int y)
9037 {
9038   if (local_player->gems_still_needed > 0 ||
9039       local_player->sokobanfields_still_needed > 0 ||
9040       local_player->lights_still_needed > 0)
9041   {
9042     int element = Feld[x][y];
9043     int graphic = el2img(element);
9044
9045     if (IS_ANIMATED(graphic))
9046       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9047
9048     return;
9049   }
9050
9051   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9052     return;
9053
9054   Feld[x][y] = EL_EM_EXIT_OPENING;
9055
9056   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9057 }
9058
9059 void CheckExitSteel(int x, int y)
9060 {
9061   if (local_player->gems_still_needed > 0 ||
9062       local_player->sokobanfields_still_needed > 0 ||
9063       local_player->lights_still_needed > 0)
9064   {
9065     int element = Feld[x][y];
9066     int graphic = el2img(element);
9067
9068     if (IS_ANIMATED(graphic))
9069       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9070
9071     return;
9072   }
9073
9074   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9075     return;
9076
9077   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9078
9079   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9080 }
9081
9082 void CheckExitSteelEM(int x, int y)
9083 {
9084   if (local_player->gems_still_needed > 0 ||
9085       local_player->sokobanfields_still_needed > 0 ||
9086       local_player->lights_still_needed > 0)
9087   {
9088     int element = Feld[x][y];
9089     int graphic = el2img(element);
9090
9091     if (IS_ANIMATED(graphic))
9092       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9093
9094     return;
9095   }
9096
9097   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9098     return;
9099
9100   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9101
9102   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9103 }
9104
9105 void CheckExitSP(int x, int y)
9106 {
9107   if (local_player->gems_still_needed > 0)
9108   {
9109     int element = Feld[x][y];
9110     int graphic = el2img(element);
9111
9112     if (IS_ANIMATED(graphic))
9113       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9114
9115     return;
9116   }
9117
9118   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9119     return;
9120
9121   Feld[x][y] = EL_SP_EXIT_OPENING;
9122
9123   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9124 }
9125
9126 static void CloseAllOpenTimegates()
9127 {
9128   int x, y;
9129
9130   SCAN_PLAYFIELD(x, y)
9131   {
9132     int element = Feld[x][y];
9133
9134     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9135     {
9136       Feld[x][y] = EL_TIMEGATE_CLOSING;
9137
9138       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9139     }
9140   }
9141 }
9142
9143 void DrawTwinkleOnField(int x, int y)
9144 {
9145   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9146     return;
9147
9148   if (Feld[x][y] == EL_BD_DIAMOND)
9149     return;
9150
9151   if (MovDelay[x][y] == 0)      /* next animation frame */
9152     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9153
9154   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9155   {
9156     MovDelay[x][y]--;
9157
9158     DrawLevelElementAnimation(x, y, Feld[x][y]);
9159
9160     if (MovDelay[x][y] != 0)
9161     {
9162       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9163                                            10 - MovDelay[x][y]);
9164
9165       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9166     }
9167   }
9168 }
9169
9170 void MauerWaechst(int x, int y)
9171 {
9172   int delay = 6;
9173
9174   if (!MovDelay[x][y])          /* next animation frame */
9175     MovDelay[x][y] = 3 * delay;
9176
9177   if (MovDelay[x][y])           /* wait some time before next frame */
9178   {
9179     MovDelay[x][y]--;
9180
9181     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9182     {
9183       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9184       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9185
9186       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9187     }
9188
9189     if (!MovDelay[x][y])
9190     {
9191       if (MovDir[x][y] == MV_LEFT)
9192       {
9193         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9194           TEST_DrawLevelField(x - 1, y);
9195       }
9196       else if (MovDir[x][y] == MV_RIGHT)
9197       {
9198         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9199           TEST_DrawLevelField(x + 1, y);
9200       }
9201       else if (MovDir[x][y] == MV_UP)
9202       {
9203         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9204           TEST_DrawLevelField(x, y - 1);
9205       }
9206       else
9207       {
9208         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9209           TEST_DrawLevelField(x, y + 1);
9210       }
9211
9212       Feld[x][y] = Store[x][y];
9213       Store[x][y] = 0;
9214       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9215       TEST_DrawLevelField(x, y);
9216     }
9217   }
9218 }
9219
9220 void MauerAbleger(int ax, int ay)
9221 {
9222   int element = Feld[ax][ay];
9223   int graphic = el2img(element);
9224   boolean oben_frei = FALSE, unten_frei = FALSE;
9225   boolean links_frei = FALSE, rechts_frei = FALSE;
9226   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9227   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9228   boolean new_wall = FALSE;
9229
9230   if (IS_ANIMATED(graphic))
9231     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9232
9233   if (!MovDelay[ax][ay])        /* start building new wall */
9234     MovDelay[ax][ay] = 6;
9235
9236   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9237   {
9238     MovDelay[ax][ay]--;
9239     if (MovDelay[ax][ay])
9240       return;
9241   }
9242
9243   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9244     oben_frei = TRUE;
9245   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9246     unten_frei = TRUE;
9247   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9248     links_frei = TRUE;
9249   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9250     rechts_frei = TRUE;
9251
9252   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9253       element == EL_EXPANDABLE_WALL_ANY)
9254   {
9255     if (oben_frei)
9256     {
9257       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9258       Store[ax][ay-1] = element;
9259       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9260       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9261         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9262                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9263       new_wall = TRUE;
9264     }
9265     if (unten_frei)
9266     {
9267       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9268       Store[ax][ay+1] = element;
9269       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9270       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9271         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9272                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9273       new_wall = TRUE;
9274     }
9275   }
9276
9277   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9278       element == EL_EXPANDABLE_WALL_ANY ||
9279       element == EL_EXPANDABLE_WALL ||
9280       element == EL_BD_EXPANDABLE_WALL)
9281   {
9282     if (links_frei)
9283     {
9284       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9285       Store[ax-1][ay] = element;
9286       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9287       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9288         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9289                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9290       new_wall = TRUE;
9291     }
9292
9293     if (rechts_frei)
9294     {
9295       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9296       Store[ax+1][ay] = element;
9297       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9298       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9299         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9300                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9301       new_wall = TRUE;
9302     }
9303   }
9304
9305   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9306     TEST_DrawLevelField(ax, ay);
9307
9308   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9309     oben_massiv = TRUE;
9310   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9311     unten_massiv = TRUE;
9312   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9313     links_massiv = TRUE;
9314   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9315     rechts_massiv = TRUE;
9316
9317   if (((oben_massiv && unten_massiv) ||
9318        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9319        element == EL_EXPANDABLE_WALL) &&
9320       ((links_massiv && rechts_massiv) ||
9321        element == EL_EXPANDABLE_WALL_VERTICAL))
9322     Feld[ax][ay] = EL_WALL;
9323
9324   if (new_wall)
9325     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9326 }
9327
9328 void MauerAblegerStahl(int ax, int ay)
9329 {
9330   int element = Feld[ax][ay];
9331   int graphic = el2img(element);
9332   boolean oben_frei = FALSE, unten_frei = FALSE;
9333   boolean links_frei = FALSE, rechts_frei = FALSE;
9334   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9335   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9336   boolean new_wall = FALSE;
9337
9338   if (IS_ANIMATED(graphic))
9339     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9340
9341   if (!MovDelay[ax][ay])        /* start building new wall */
9342     MovDelay[ax][ay] = 6;
9343
9344   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9345   {
9346     MovDelay[ax][ay]--;
9347     if (MovDelay[ax][ay])
9348       return;
9349   }
9350
9351   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9352     oben_frei = TRUE;
9353   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9354     unten_frei = TRUE;
9355   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9356     links_frei = TRUE;
9357   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9358     rechts_frei = TRUE;
9359
9360   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9361       element == EL_EXPANDABLE_STEELWALL_ANY)
9362   {
9363     if (oben_frei)
9364     {
9365       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9366       Store[ax][ay-1] = element;
9367       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9368       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9369         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9370                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9371       new_wall = TRUE;
9372     }
9373     if (unten_frei)
9374     {
9375       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9376       Store[ax][ay+1] = element;
9377       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9378       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9379         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9380                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9381       new_wall = TRUE;
9382     }
9383   }
9384
9385   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9386       element == EL_EXPANDABLE_STEELWALL_ANY)
9387   {
9388     if (links_frei)
9389     {
9390       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9391       Store[ax-1][ay] = element;
9392       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9393       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9394         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9395                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9396       new_wall = TRUE;
9397     }
9398
9399     if (rechts_frei)
9400     {
9401       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9402       Store[ax+1][ay] = element;
9403       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9404       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9405         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9406                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9407       new_wall = TRUE;
9408     }
9409   }
9410
9411   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9412     oben_massiv = TRUE;
9413   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9414     unten_massiv = TRUE;
9415   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9416     links_massiv = TRUE;
9417   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9418     rechts_massiv = TRUE;
9419
9420   if (((oben_massiv && unten_massiv) ||
9421        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9422       ((links_massiv && rechts_massiv) ||
9423        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9424     Feld[ax][ay] = EL_STEELWALL;
9425
9426   if (new_wall)
9427     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9428 }
9429
9430 void CheckForDragon(int x, int y)
9431 {
9432   int i, j;
9433   boolean dragon_found = FALSE;
9434   static int xy[4][2] =
9435   {
9436     { 0, -1 },
9437     { -1, 0 },
9438     { +1, 0 },
9439     { 0, +1 }
9440   };
9441
9442   for (i = 0; i < NUM_DIRECTIONS; i++)
9443   {
9444     for (j = 0; j < 4; j++)
9445     {
9446       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9447
9448       if (IN_LEV_FIELD(xx, yy) &&
9449           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9450       {
9451         if (Feld[xx][yy] == EL_DRAGON)
9452           dragon_found = TRUE;
9453       }
9454       else
9455         break;
9456     }
9457   }
9458
9459   if (!dragon_found)
9460   {
9461     for (i = 0; i < NUM_DIRECTIONS; i++)
9462     {
9463       for (j = 0; j < 3; j++)
9464       {
9465         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9466   
9467         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9468         {
9469           Feld[xx][yy] = EL_EMPTY;
9470           TEST_DrawLevelField(xx, yy);
9471         }
9472         else
9473           break;
9474       }
9475     }
9476   }
9477 }
9478
9479 static void InitBuggyBase(int x, int y)
9480 {
9481   int element = Feld[x][y];
9482   int activating_delay = FRAMES_PER_SECOND / 4;
9483
9484   ChangeDelay[x][y] =
9485     (element == EL_SP_BUGGY_BASE ?
9486      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9487      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9488      activating_delay :
9489      element == EL_SP_BUGGY_BASE_ACTIVE ?
9490      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9491 }
9492
9493 static void WarnBuggyBase(int x, int y)
9494 {
9495   int i;
9496   static int xy[4][2] =
9497   {
9498     { 0, -1 },
9499     { -1, 0 },
9500     { +1, 0 },
9501     { 0, +1 }
9502   };
9503
9504   for (i = 0; i < NUM_DIRECTIONS; i++)
9505   {
9506     int xx = x + xy[i][0];
9507     int yy = y + xy[i][1];
9508
9509     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9510     {
9511       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9512
9513       break;
9514     }
9515   }
9516 }
9517
9518 static void InitTrap(int x, int y)
9519 {
9520   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9521 }
9522
9523 static void ActivateTrap(int x, int y)
9524 {
9525   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9526 }
9527
9528 static void ChangeActiveTrap(int x, int y)
9529 {
9530   int graphic = IMG_TRAP_ACTIVE;
9531
9532   /* if new animation frame was drawn, correct crumbled sand border */
9533   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9534     TEST_DrawLevelFieldCrumbled(x, y);
9535 }
9536
9537 static int getSpecialActionElement(int element, int number, int base_element)
9538 {
9539   return (element != EL_EMPTY ? element :
9540           number != -1 ? base_element + number - 1 :
9541           EL_EMPTY);
9542 }
9543
9544 static int getModifiedActionNumber(int value_old, int operator, int operand,
9545                                    int value_min, int value_max)
9546 {
9547   int value_new = (operator == CA_MODE_SET      ? operand :
9548                    operator == CA_MODE_ADD      ? value_old + operand :
9549                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9550                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9551                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9552                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9553                    value_old);
9554
9555   return (value_new < value_min ? value_min :
9556           value_new > value_max ? value_max :
9557           value_new);
9558 }
9559
9560 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9561 {
9562   struct ElementInfo *ei = &element_info[element];
9563   struct ElementChangeInfo *change = &ei->change_page[page];
9564   int target_element = change->target_element;
9565   int action_type = change->action_type;
9566   int action_mode = change->action_mode;
9567   int action_arg = change->action_arg;
9568   int action_element = change->action_element;
9569   int i;
9570
9571   if (!change->has_action)
9572     return;
9573
9574   /* ---------- determine action paramater values -------------------------- */
9575
9576   int level_time_value =
9577     (level.time > 0 ? TimeLeft :
9578      TimePlayed);
9579
9580   int action_arg_element_raw =
9581     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9582      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9583      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9584      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9585      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9586      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9587      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9588      EL_EMPTY);
9589   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9590
9591   int action_arg_direction =
9592     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9593      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9594      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9595      change->actual_trigger_side :
9596      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9597      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9598      MV_NONE);
9599
9600   int action_arg_number_min =
9601     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9602      CA_ARG_MIN);
9603
9604   int action_arg_number_max =
9605     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9606      action_type == CA_SET_LEVEL_GEMS ? 999 :
9607      action_type == CA_SET_LEVEL_TIME ? 9999 :
9608      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9609      action_type == CA_SET_CE_VALUE ? 9999 :
9610      action_type == CA_SET_CE_SCORE ? 9999 :
9611      CA_ARG_MAX);
9612
9613   int action_arg_number_reset =
9614     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9615      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9616      action_type == CA_SET_LEVEL_TIME ? level.time :
9617      action_type == CA_SET_LEVEL_SCORE ? 0 :
9618      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9619      action_type == CA_SET_CE_SCORE ? 0 :
9620      0);
9621
9622   int action_arg_number =
9623     (action_arg <= CA_ARG_MAX ? action_arg :
9624      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9625      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9626      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9627      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9628      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9629      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9630      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9631      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9632      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9633      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9634      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9635      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9636      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9637      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9638      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9639      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9640      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9641      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9642      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9643      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9644      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9645      -1);
9646
9647   int action_arg_number_old =
9648     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9649      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9650      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9651      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9652      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9653      0);
9654
9655   int action_arg_number_new =
9656     getModifiedActionNumber(action_arg_number_old,
9657                             action_mode, action_arg_number,
9658                             action_arg_number_min, action_arg_number_max);
9659
9660   int trigger_player_bits =
9661     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9662      change->actual_trigger_player_bits : change->trigger_player);
9663
9664   int action_arg_player_bits =
9665     (action_arg >= CA_ARG_PLAYER_1 &&
9666      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9667      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9668      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9669      PLAYER_BITS_ANY);
9670
9671   /* ---------- execute action  -------------------------------------------- */
9672
9673   switch (action_type)
9674   {
9675     case CA_NO_ACTION:
9676     {
9677       return;
9678     }
9679
9680     /* ---------- level actions  ------------------------------------------- */
9681
9682     case CA_RESTART_LEVEL:
9683     {
9684       game.restart_level = TRUE;
9685
9686       break;
9687     }
9688
9689     case CA_SHOW_ENVELOPE:
9690     {
9691       int element = getSpecialActionElement(action_arg_element,
9692                                             action_arg_number, EL_ENVELOPE_1);
9693
9694       if (IS_ENVELOPE(element))
9695         local_player->show_envelope = element;
9696
9697       break;
9698     }
9699
9700     case CA_SET_LEVEL_TIME:
9701     {
9702       if (level.time > 0)       /* only modify limited time value */
9703       {
9704         TimeLeft = action_arg_number_new;
9705
9706         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9707
9708         DisplayGameControlValues();
9709
9710         if (!TimeLeft && setup.time_limit)
9711           for (i = 0; i < MAX_PLAYERS; i++)
9712             KillPlayer(&stored_player[i]);
9713       }
9714
9715       break;
9716     }
9717
9718     case CA_SET_LEVEL_SCORE:
9719     {
9720       local_player->score = action_arg_number_new;
9721
9722       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9723
9724       DisplayGameControlValues();
9725
9726       break;
9727     }
9728
9729     case CA_SET_LEVEL_GEMS:
9730     {
9731       local_player->gems_still_needed = action_arg_number_new;
9732
9733       game.snapshot.collected_item = TRUE;
9734
9735       game_panel_controls[GAME_PANEL_GEMS].value =
9736         local_player->gems_still_needed;
9737
9738       DisplayGameControlValues();
9739
9740       break;
9741     }
9742
9743     case CA_SET_LEVEL_WIND:
9744     {
9745       game.wind_direction = action_arg_direction;
9746
9747       break;
9748     }
9749
9750     case CA_SET_LEVEL_RANDOM_SEED:
9751     {
9752       /* ensure that setting a new random seed while playing is predictable */
9753       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9754
9755       break;
9756     }
9757
9758     /* ---------- player actions  ------------------------------------------ */
9759
9760     case CA_MOVE_PLAYER:
9761     {
9762       /* automatically move to the next field in specified direction */
9763       for (i = 0; i < MAX_PLAYERS; i++)
9764         if (trigger_player_bits & (1 << i))
9765           stored_player[i].programmed_action = action_arg_direction;
9766
9767       break;
9768     }
9769
9770     case CA_EXIT_PLAYER:
9771     {
9772       for (i = 0; i < MAX_PLAYERS; i++)
9773         if (action_arg_player_bits & (1 << i))
9774           PlayerWins(&stored_player[i]);
9775
9776       break;
9777     }
9778
9779     case CA_KILL_PLAYER:
9780     {
9781       for (i = 0; i < MAX_PLAYERS; i++)
9782         if (action_arg_player_bits & (1 << i))
9783           KillPlayer(&stored_player[i]);
9784
9785       break;
9786     }
9787
9788     case CA_SET_PLAYER_KEYS:
9789     {
9790       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9791       int element = getSpecialActionElement(action_arg_element,
9792                                             action_arg_number, EL_KEY_1);
9793
9794       if (IS_KEY(element))
9795       {
9796         for (i = 0; i < MAX_PLAYERS; i++)
9797         {
9798           if (trigger_player_bits & (1 << i))
9799           {
9800             stored_player[i].key[KEY_NR(element)] = key_state;
9801
9802             DrawGameDoorValues();
9803           }
9804         }
9805       }
9806
9807       break;
9808     }
9809
9810     case CA_SET_PLAYER_SPEED:
9811     {
9812       for (i = 0; i < MAX_PLAYERS; i++)
9813       {
9814         if (trigger_player_bits & (1 << i))
9815         {
9816           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9817
9818           if (action_arg == CA_ARG_SPEED_FASTER &&
9819               stored_player[i].cannot_move)
9820           {
9821             action_arg_number = STEPSIZE_VERY_SLOW;
9822           }
9823           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9824                    action_arg == CA_ARG_SPEED_FASTER)
9825           {
9826             action_arg_number = 2;
9827             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9828                            CA_MODE_MULTIPLY);
9829           }
9830           else if (action_arg == CA_ARG_NUMBER_RESET)
9831           {
9832             action_arg_number = level.initial_player_stepsize[i];
9833           }
9834
9835           move_stepsize =
9836             getModifiedActionNumber(move_stepsize,
9837                                     action_mode,
9838                                     action_arg_number,
9839                                     action_arg_number_min,
9840                                     action_arg_number_max);
9841
9842           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9843         }
9844       }
9845
9846       break;
9847     }
9848
9849     case CA_SET_PLAYER_SHIELD:
9850     {
9851       for (i = 0; i < MAX_PLAYERS; i++)
9852       {
9853         if (trigger_player_bits & (1 << i))
9854         {
9855           if (action_arg == CA_ARG_SHIELD_OFF)
9856           {
9857             stored_player[i].shield_normal_time_left = 0;
9858             stored_player[i].shield_deadly_time_left = 0;
9859           }
9860           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9861           {
9862             stored_player[i].shield_normal_time_left = 999999;
9863           }
9864           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9865           {
9866             stored_player[i].shield_normal_time_left = 999999;
9867             stored_player[i].shield_deadly_time_left = 999999;
9868           }
9869         }
9870       }
9871
9872       break;
9873     }
9874
9875     case CA_SET_PLAYER_GRAVITY:
9876     {
9877       for (i = 0; i < MAX_PLAYERS; i++)
9878       {
9879         if (trigger_player_bits & (1 << i))
9880         {
9881           stored_player[i].gravity =
9882             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9883              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9884              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9885              stored_player[i].gravity);
9886         }
9887       }
9888
9889       break;
9890     }
9891
9892     case CA_SET_PLAYER_ARTWORK:
9893     {
9894       for (i = 0; i < MAX_PLAYERS; i++)
9895       {
9896         if (trigger_player_bits & (1 << i))
9897         {
9898           int artwork_element = action_arg_element;
9899
9900           if (action_arg == CA_ARG_ELEMENT_RESET)
9901             artwork_element =
9902               (level.use_artwork_element[i] ? level.artwork_element[i] :
9903                stored_player[i].element_nr);
9904
9905           if (stored_player[i].artwork_element != artwork_element)
9906             stored_player[i].Frame = 0;
9907
9908           stored_player[i].artwork_element = artwork_element;
9909
9910           SetPlayerWaiting(&stored_player[i], FALSE);
9911
9912           /* set number of special actions for bored and sleeping animation */
9913           stored_player[i].num_special_action_bored =
9914             get_num_special_action(artwork_element,
9915                                    ACTION_BORING_1, ACTION_BORING_LAST);
9916           stored_player[i].num_special_action_sleeping =
9917             get_num_special_action(artwork_element,
9918                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9919         }
9920       }
9921
9922       break;
9923     }
9924
9925     case CA_SET_PLAYER_INVENTORY:
9926     {
9927       for (i = 0; i < MAX_PLAYERS; i++)
9928       {
9929         struct PlayerInfo *player = &stored_player[i];
9930         int j, k;
9931
9932         if (trigger_player_bits & (1 << i))
9933         {
9934           int inventory_element = action_arg_element;
9935
9936           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9937               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9938               action_arg == CA_ARG_ELEMENT_ACTION)
9939           {
9940             int element = inventory_element;
9941             int collect_count = element_info[element].collect_count_initial;
9942
9943             if (!IS_CUSTOM_ELEMENT(element))
9944               collect_count = 1;
9945
9946             if (collect_count == 0)
9947               player->inventory_infinite_element = element;
9948             else
9949               for (k = 0; k < collect_count; k++)
9950                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9951                   player->inventory_element[player->inventory_size++] =
9952                     element;
9953           }
9954           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9955                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9956                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9957           {
9958             if (player->inventory_infinite_element != EL_UNDEFINED &&
9959                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9960                                      action_arg_element_raw))
9961               player->inventory_infinite_element = EL_UNDEFINED;
9962
9963             for (k = 0, j = 0; j < player->inventory_size; j++)
9964             {
9965               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9966                                         action_arg_element_raw))
9967                 player->inventory_element[k++] = player->inventory_element[j];
9968             }
9969
9970             player->inventory_size = k;
9971           }
9972           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9973           {
9974             if (player->inventory_size > 0)
9975             {
9976               for (j = 0; j < player->inventory_size - 1; j++)
9977                 player->inventory_element[j] = player->inventory_element[j + 1];
9978
9979               player->inventory_size--;
9980             }
9981           }
9982           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9983           {
9984             if (player->inventory_size > 0)
9985               player->inventory_size--;
9986           }
9987           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9988           {
9989             player->inventory_infinite_element = EL_UNDEFINED;
9990             player->inventory_size = 0;
9991           }
9992           else if (action_arg == CA_ARG_INVENTORY_RESET)
9993           {
9994             player->inventory_infinite_element = EL_UNDEFINED;
9995             player->inventory_size = 0;
9996
9997             if (level.use_initial_inventory[i])
9998             {
9999               for (j = 0; j < level.initial_inventory_size[i]; j++)
10000               {
10001                 int element = level.initial_inventory_content[i][j];
10002                 int collect_count = element_info[element].collect_count_initial;
10003
10004                 if (!IS_CUSTOM_ELEMENT(element))
10005                   collect_count = 1;
10006
10007                 if (collect_count == 0)
10008                   player->inventory_infinite_element = element;
10009                 else
10010                   for (k = 0; k < collect_count; k++)
10011                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10012                       player->inventory_element[player->inventory_size++] =
10013                         element;
10014               }
10015             }
10016           }
10017         }
10018       }
10019
10020       break;
10021     }
10022
10023     /* ---------- CE actions  ---------------------------------------------- */
10024
10025     case CA_SET_CE_VALUE:
10026     {
10027       int last_ce_value = CustomValue[x][y];
10028
10029       CustomValue[x][y] = action_arg_number_new;
10030
10031       if (CustomValue[x][y] != last_ce_value)
10032       {
10033         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10034         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10035
10036         if (CustomValue[x][y] == 0)
10037         {
10038           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10039           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10040         }
10041       }
10042
10043       break;
10044     }
10045
10046     case CA_SET_CE_SCORE:
10047     {
10048       int last_ce_score = ei->collect_score;
10049
10050       ei->collect_score = action_arg_number_new;
10051
10052       if (ei->collect_score != last_ce_score)
10053       {
10054         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10055         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10056
10057         if (ei->collect_score == 0)
10058         {
10059           int xx, yy;
10060
10061           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10062           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10063
10064           /*
10065             This is a very special case that seems to be a mixture between
10066             CheckElementChange() and CheckTriggeredElementChange(): while
10067             the first one only affects single elements that are triggered
10068             directly, the second one affects multiple elements in the playfield
10069             that are triggered indirectly by another element. This is a third
10070             case: Changing the CE score always affects multiple identical CEs,
10071             so every affected CE must be checked, not only the single CE for
10072             which the CE score was changed in the first place (as every instance
10073             of that CE shares the same CE score, and therefore also can change)!
10074           */
10075           SCAN_PLAYFIELD(xx, yy)
10076           {
10077             if (Feld[xx][yy] == element)
10078               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10079                                  CE_SCORE_GETS_ZERO);
10080           }
10081         }
10082       }
10083
10084       break;
10085     }
10086
10087     case CA_SET_CE_ARTWORK:
10088     {
10089       int artwork_element = action_arg_element;
10090       boolean reset_frame = FALSE;
10091       int xx, yy;
10092
10093       if (action_arg == CA_ARG_ELEMENT_RESET)
10094         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10095                            element);
10096
10097       if (ei->gfx_element != artwork_element)
10098         reset_frame = TRUE;
10099
10100       ei->gfx_element = artwork_element;
10101
10102       SCAN_PLAYFIELD(xx, yy)
10103       {
10104         if (Feld[xx][yy] == element)
10105         {
10106           if (reset_frame)
10107           {
10108             ResetGfxAnimation(xx, yy);
10109             ResetRandomAnimationValue(xx, yy);
10110           }
10111
10112           TEST_DrawLevelField(xx, yy);
10113         }
10114       }
10115
10116       break;
10117     }
10118
10119     /* ---------- engine actions  ------------------------------------------ */
10120
10121     case CA_SET_ENGINE_SCAN_MODE:
10122     {
10123       InitPlayfieldScanMode(action_arg);
10124
10125       break;
10126     }
10127
10128     default:
10129       break;
10130   }
10131 }
10132
10133 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10134 {
10135   int old_element = Feld[x][y];
10136   int new_element = GetElementFromGroupElement(element);
10137   int previous_move_direction = MovDir[x][y];
10138   int last_ce_value = CustomValue[x][y];
10139   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10140   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10141   boolean add_player_onto_element = (new_element_is_player &&
10142                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10143                                      IS_WALKABLE(old_element));
10144
10145   if (!add_player_onto_element)
10146   {
10147     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10148       RemoveMovingField(x, y);
10149     else
10150       RemoveField(x, y);
10151
10152     Feld[x][y] = new_element;
10153
10154     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10155       MovDir[x][y] = previous_move_direction;
10156
10157     if (element_info[new_element].use_last_ce_value)
10158       CustomValue[x][y] = last_ce_value;
10159
10160     InitField_WithBug1(x, y, FALSE);
10161
10162     new_element = Feld[x][y];   /* element may have changed */
10163
10164     ResetGfxAnimation(x, y);
10165     ResetRandomAnimationValue(x, y);
10166
10167     TEST_DrawLevelField(x, y);
10168
10169     if (GFX_CRUMBLED(new_element))
10170       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10171   }
10172
10173   /* check if element under the player changes from accessible to unaccessible
10174      (needed for special case of dropping element which then changes) */
10175   /* (must be checked after creating new element for walkable group elements) */
10176   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10177       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10178   {
10179     Bang(x, y);
10180
10181     return;
10182   }
10183
10184   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10185   if (new_element_is_player)
10186     RelocatePlayer(x, y, new_element);
10187
10188   if (is_change)
10189     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10190
10191   TestIfBadThingTouchesPlayer(x, y);
10192   TestIfPlayerTouchesCustomElement(x, y);
10193   TestIfElementTouchesCustomElement(x, y);
10194 }
10195
10196 static void CreateField(int x, int y, int element)
10197 {
10198   CreateFieldExt(x, y, element, FALSE);
10199 }
10200
10201 static void CreateElementFromChange(int x, int y, int element)
10202 {
10203   element = GET_VALID_RUNTIME_ELEMENT(element);
10204
10205   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10206   {
10207     int old_element = Feld[x][y];
10208
10209     /* prevent changed element from moving in same engine frame
10210        unless both old and new element can either fall or move */
10211     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10212         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10213       Stop[x][y] = TRUE;
10214   }
10215
10216   CreateFieldExt(x, y, element, TRUE);
10217 }
10218
10219 static boolean ChangeElement(int x, int y, int element, int page)
10220 {
10221   struct ElementInfo *ei = &element_info[element];
10222   struct ElementChangeInfo *change = &ei->change_page[page];
10223   int ce_value = CustomValue[x][y];
10224   int ce_score = ei->collect_score;
10225   int target_element;
10226   int old_element = Feld[x][y];
10227
10228   /* always use default change event to prevent running into a loop */
10229   if (ChangeEvent[x][y] == -1)
10230     ChangeEvent[x][y] = CE_DELAY;
10231
10232   if (ChangeEvent[x][y] == CE_DELAY)
10233   {
10234     /* reset actual trigger element, trigger player and action element */
10235     change->actual_trigger_element = EL_EMPTY;
10236     change->actual_trigger_player = EL_EMPTY;
10237     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10238     change->actual_trigger_side = CH_SIDE_NONE;
10239     change->actual_trigger_ce_value = 0;
10240     change->actual_trigger_ce_score = 0;
10241   }
10242
10243   /* do not change elements more than a specified maximum number of changes */
10244   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10245     return FALSE;
10246
10247   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10248
10249   if (change->explode)
10250   {
10251     Bang(x, y);
10252
10253     return TRUE;
10254   }
10255
10256   if (change->use_target_content)
10257   {
10258     boolean complete_replace = TRUE;
10259     boolean can_replace[3][3];
10260     int xx, yy;
10261
10262     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10263     {
10264       boolean is_empty;
10265       boolean is_walkable;
10266       boolean is_diggable;
10267       boolean is_collectible;
10268       boolean is_removable;
10269       boolean is_destructible;
10270       int ex = x + xx - 1;
10271       int ey = y + yy - 1;
10272       int content_element = change->target_content.e[xx][yy];
10273       int e;
10274
10275       can_replace[xx][yy] = TRUE;
10276
10277       if (ex == x && ey == y)   /* do not check changing element itself */
10278         continue;
10279
10280       if (content_element == EL_EMPTY_SPACE)
10281       {
10282         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10283
10284         continue;
10285       }
10286
10287       if (!IN_LEV_FIELD(ex, ey))
10288       {
10289         can_replace[xx][yy] = FALSE;
10290         complete_replace = FALSE;
10291
10292         continue;
10293       }
10294
10295       e = Feld[ex][ey];
10296
10297       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10298         e = MovingOrBlocked2Element(ex, ey);
10299
10300       is_empty = (IS_FREE(ex, ey) ||
10301                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10302
10303       is_walkable     = (is_empty || IS_WALKABLE(e));
10304       is_diggable     = (is_empty || IS_DIGGABLE(e));
10305       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10306       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10307       is_removable    = (is_diggable || is_collectible);
10308
10309       can_replace[xx][yy] =
10310         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10311           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10312           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10313           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10314           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10315           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10316          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10317
10318       if (!can_replace[xx][yy])
10319         complete_replace = FALSE;
10320     }
10321
10322     if (!change->only_if_complete || complete_replace)
10323     {
10324       boolean something_has_changed = FALSE;
10325
10326       if (change->only_if_complete && change->use_random_replace &&
10327           RND(100) < change->random_percentage)
10328         return FALSE;
10329
10330       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10331       {
10332         int ex = x + xx - 1;
10333         int ey = y + yy - 1;
10334         int content_element;
10335
10336         if (can_replace[xx][yy] && (!change->use_random_replace ||
10337                                     RND(100) < change->random_percentage))
10338         {
10339           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10340             RemoveMovingField(ex, ey);
10341
10342           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10343
10344           content_element = change->target_content.e[xx][yy];
10345           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10346                                               ce_value, ce_score);
10347
10348           CreateElementFromChange(ex, ey, target_element);
10349
10350           something_has_changed = TRUE;
10351
10352           /* for symmetry reasons, freeze newly created border elements */
10353           if (ex != x || ey != y)
10354             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10355         }
10356       }
10357
10358       if (something_has_changed)
10359       {
10360         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10361         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10362       }
10363     }
10364   }
10365   else
10366   {
10367     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10368                                         ce_value, ce_score);
10369
10370     if (element == EL_DIAGONAL_GROWING ||
10371         element == EL_DIAGONAL_SHRINKING)
10372     {
10373       target_element = Store[x][y];
10374
10375       Store[x][y] = EL_EMPTY;
10376     }
10377
10378     CreateElementFromChange(x, y, target_element);
10379
10380     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10381     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10382   }
10383
10384   /* this uses direct change before indirect change */
10385   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10386
10387   return TRUE;
10388 }
10389
10390 static void HandleElementChange(int x, int y, int page)
10391 {
10392   int element = MovingOrBlocked2Element(x, y);
10393   struct ElementInfo *ei = &element_info[element];
10394   struct ElementChangeInfo *change = &ei->change_page[page];
10395   boolean handle_action_before_change = FALSE;
10396
10397 #ifdef DEBUG
10398   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10399       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10400   {
10401     printf("\n\n");
10402     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10403            x, y, element, element_info[element].token_name);
10404     printf("HandleElementChange(): This should never happen!\n");
10405     printf("\n\n");
10406   }
10407 #endif
10408
10409   /* this can happen with classic bombs on walkable, changing elements */
10410   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10411   {
10412     return;
10413   }
10414
10415   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10416   {
10417     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10418
10419     if (change->can_change)
10420     {
10421       /* !!! not clear why graphic animation should be reset at all here !!! */
10422       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10423       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10424
10425       /*
10426         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10427
10428         When using an animation frame delay of 1 (this only happens with
10429         "sp_zonk.moving.left/right" in the classic graphics), the default
10430         (non-moving) animation shows wrong animation frames (while the
10431         moving animation, like "sp_zonk.moving.left/right", is correct,
10432         so this graphical bug never shows up with the classic graphics).
10433         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10434         be drawn instead of the correct frames 0,1,2,3. This is caused by
10435         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10436         an element change: First when the change delay ("ChangeDelay[][]")
10437         counter has reached zero after decrementing, then a second time in
10438         the next frame (after "GfxFrame[][]" was already incremented) when
10439         "ChangeDelay[][]" is reset to the initial delay value again.
10440
10441         This causes frame 0 to be drawn twice, while the last frame won't
10442         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10443
10444         As some animations may already be cleverly designed around this bug
10445         (at least the "Snake Bite" snake tail animation does this), it cannot
10446         simply be fixed here without breaking such existing animations.
10447         Unfortunately, it cannot easily be detected if a graphics set was
10448         designed "before" or "after" the bug was fixed. As a workaround,
10449         a new graphics set option "game.graphics_engine_version" was added
10450         to be able to specify the game's major release version for which the
10451         graphics set was designed, which can then be used to decide if the
10452         bugfix should be used (version 4 and above) or not (version 3 or
10453         below, or if no version was specified at all, as with old sets).
10454
10455         (The wrong/fixed animation frames can be tested with the test level set
10456         "test_gfxframe" and level "000", which contains a specially prepared
10457         custom element at level position (x/y) == (11/9) which uses the zonk
10458         animation mentioned above. Using "game.graphics_engine_version: 4"
10459         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10460         This can also be seen from the debug output for this test element.)
10461       */
10462
10463       /* when a custom element is about to change (for example by change delay),
10464          do not reset graphic animation when the custom element is moving */
10465       if (game.graphics_engine_version < 4 &&
10466           !IS_MOVING(x, y))
10467       {
10468         ResetGfxAnimation(x, y);
10469         ResetRandomAnimationValue(x, y);
10470       }
10471
10472       if (change->pre_change_function)
10473         change->pre_change_function(x, y);
10474     }
10475   }
10476
10477   ChangeDelay[x][y]--;
10478
10479   if (ChangeDelay[x][y] != 0)           /* continue element change */
10480   {
10481     if (change->can_change)
10482     {
10483       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10484
10485       if (IS_ANIMATED(graphic))
10486         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10487
10488       if (change->change_function)
10489         change->change_function(x, y);
10490     }
10491   }
10492   else                                  /* finish element change */
10493   {
10494     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10495     {
10496       page = ChangePage[x][y];
10497       ChangePage[x][y] = -1;
10498
10499       change = &ei->change_page[page];
10500     }
10501
10502     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10503     {
10504       ChangeDelay[x][y] = 1;            /* try change after next move step */
10505       ChangePage[x][y] = page;          /* remember page to use for change */
10506
10507       return;
10508     }
10509
10510     /* special case: set new level random seed before changing element */
10511     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10512       handle_action_before_change = TRUE;
10513
10514     if (change->has_action && handle_action_before_change)
10515       ExecuteCustomElementAction(x, y, element, page);
10516
10517     if (change->can_change)
10518     {
10519       if (ChangeElement(x, y, element, page))
10520       {
10521         if (change->post_change_function)
10522           change->post_change_function(x, y);
10523       }
10524     }
10525
10526     if (change->has_action && !handle_action_before_change)
10527       ExecuteCustomElementAction(x, y, element, page);
10528   }
10529 }
10530
10531 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10532                                               int trigger_element,
10533                                               int trigger_event,
10534                                               int trigger_player,
10535                                               int trigger_side,
10536                                               int trigger_page)
10537 {
10538   boolean change_done_any = FALSE;
10539   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10540   int i;
10541
10542   if (!(trigger_events[trigger_element][trigger_event]))
10543     return FALSE;
10544
10545   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10546
10547   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10548   {
10549     int element = EL_CUSTOM_START + i;
10550     boolean change_done = FALSE;
10551     int p;
10552
10553     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10554         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10555       continue;
10556
10557     for (p = 0; p < element_info[element].num_change_pages; p++)
10558     {
10559       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10560
10561       if (change->can_change_or_has_action &&
10562           change->has_event[trigger_event] &&
10563           change->trigger_side & trigger_side &&
10564           change->trigger_player & trigger_player &&
10565           change->trigger_page & trigger_page_bits &&
10566           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10567       {
10568         change->actual_trigger_element = trigger_element;
10569         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10570         change->actual_trigger_player_bits = trigger_player;
10571         change->actual_trigger_side = trigger_side;
10572         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10573         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10574
10575         if ((change->can_change && !change_done) || change->has_action)
10576         {
10577           int x, y;
10578
10579           SCAN_PLAYFIELD(x, y)
10580           {
10581             if (Feld[x][y] == element)
10582             {
10583               if (change->can_change && !change_done)
10584               {
10585                 /* if element already changed in this frame, not only prevent
10586                    another element change (checked in ChangeElement()), but
10587                    also prevent additional element actions for this element */
10588
10589                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10590                     !level.use_action_after_change_bug)
10591                   continue;
10592
10593                 ChangeDelay[x][y] = 1;
10594                 ChangeEvent[x][y] = trigger_event;
10595
10596                 HandleElementChange(x, y, p);
10597               }
10598               else if (change->has_action)
10599               {
10600                 /* if element already changed in this frame, not only prevent
10601                    another element change (checked in ChangeElement()), but
10602                    also prevent additional element actions for this element */
10603
10604                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10605                     !level.use_action_after_change_bug)
10606                   continue;
10607
10608                 ExecuteCustomElementAction(x, y, element, p);
10609                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10610               }
10611             }
10612           }
10613
10614           if (change->can_change)
10615           {
10616             change_done = TRUE;
10617             change_done_any = TRUE;
10618           }
10619         }
10620       }
10621     }
10622   }
10623
10624   RECURSION_LOOP_DETECTION_END();
10625
10626   return change_done_any;
10627 }
10628
10629 static boolean CheckElementChangeExt(int x, int y,
10630                                      int element,
10631                                      int trigger_element,
10632                                      int trigger_event,
10633                                      int trigger_player,
10634                                      int trigger_side)
10635 {
10636   boolean change_done = FALSE;
10637   int p;
10638
10639   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10640       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10641     return FALSE;
10642
10643   if (Feld[x][y] == EL_BLOCKED)
10644   {
10645     Blocked2Moving(x, y, &x, &y);
10646     element = Feld[x][y];
10647   }
10648
10649   /* check if element has already changed or is about to change after moving */
10650   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10651        Feld[x][y] != element) ||
10652
10653       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10654        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10655         ChangePage[x][y] != -1)))
10656     return FALSE;
10657
10658   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10659
10660   for (p = 0; p < element_info[element].num_change_pages; p++)
10661   {
10662     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10663
10664     /* check trigger element for all events where the element that is checked
10665        for changing interacts with a directly adjacent element -- this is
10666        different to element changes that affect other elements to change on the
10667        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10668     boolean check_trigger_element =
10669       (trigger_event == CE_TOUCHING_X ||
10670        trigger_event == CE_HITTING_X ||
10671        trigger_event == CE_HIT_BY_X ||
10672        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10673
10674     if (change->can_change_or_has_action &&
10675         change->has_event[trigger_event] &&
10676         change->trigger_side & trigger_side &&
10677         change->trigger_player & trigger_player &&
10678         (!check_trigger_element ||
10679          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10680     {
10681       change->actual_trigger_element = trigger_element;
10682       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10683       change->actual_trigger_player_bits = trigger_player;
10684       change->actual_trigger_side = trigger_side;
10685       change->actual_trigger_ce_value = CustomValue[x][y];
10686       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10687
10688       /* special case: trigger element not at (x,y) position for some events */
10689       if (check_trigger_element)
10690       {
10691         static struct
10692         {
10693           int dx, dy;
10694         } move_xy[] =
10695           {
10696             {  0,  0 },
10697             { -1,  0 },
10698             { +1,  0 },
10699             {  0,  0 },
10700             {  0, -1 },
10701             {  0,  0 }, { 0, 0 }, { 0, 0 },
10702             {  0, +1 }
10703           };
10704
10705         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10706         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10707
10708         change->actual_trigger_ce_value = CustomValue[xx][yy];
10709         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10710       }
10711
10712       if (change->can_change && !change_done)
10713       {
10714         ChangeDelay[x][y] = 1;
10715         ChangeEvent[x][y] = trigger_event;
10716
10717         HandleElementChange(x, y, p);
10718
10719         change_done = TRUE;
10720       }
10721       else if (change->has_action)
10722       {
10723         ExecuteCustomElementAction(x, y, element, p);
10724         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10725       }
10726     }
10727   }
10728
10729   RECURSION_LOOP_DETECTION_END();
10730
10731   return change_done;
10732 }
10733
10734 static void PlayPlayerSound(struct PlayerInfo *player)
10735 {
10736   int jx = player->jx, jy = player->jy;
10737   int sound_element = player->artwork_element;
10738   int last_action = player->last_action_waiting;
10739   int action = player->action_waiting;
10740
10741   if (player->is_waiting)
10742   {
10743     if (action != last_action)
10744       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10745     else
10746       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10747   }
10748   else
10749   {
10750     if (action != last_action)
10751       StopSound(element_info[sound_element].sound[last_action]);
10752
10753     if (last_action == ACTION_SLEEPING)
10754       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10755   }
10756 }
10757
10758 static void PlayAllPlayersSound()
10759 {
10760   int i;
10761
10762   for (i = 0; i < MAX_PLAYERS; i++)
10763     if (stored_player[i].active)
10764       PlayPlayerSound(&stored_player[i]);
10765 }
10766
10767 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10768 {
10769   boolean last_waiting = player->is_waiting;
10770   int move_dir = player->MovDir;
10771
10772   player->dir_waiting = move_dir;
10773   player->last_action_waiting = player->action_waiting;
10774
10775   if (is_waiting)
10776   {
10777     if (!last_waiting)          /* not waiting -> waiting */
10778     {
10779       player->is_waiting = TRUE;
10780
10781       player->frame_counter_bored =
10782         FrameCounter +
10783         game.player_boring_delay_fixed +
10784         GetSimpleRandom(game.player_boring_delay_random);
10785       player->frame_counter_sleeping =
10786         FrameCounter +
10787         game.player_sleeping_delay_fixed +
10788         GetSimpleRandom(game.player_sleeping_delay_random);
10789
10790       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10791     }
10792
10793     if (game.player_sleeping_delay_fixed +
10794         game.player_sleeping_delay_random > 0 &&
10795         player->anim_delay_counter == 0 &&
10796         player->post_delay_counter == 0 &&
10797         FrameCounter >= player->frame_counter_sleeping)
10798       player->is_sleeping = TRUE;
10799     else if (game.player_boring_delay_fixed +
10800              game.player_boring_delay_random > 0 &&
10801              FrameCounter >= player->frame_counter_bored)
10802       player->is_bored = TRUE;
10803
10804     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10805                               player->is_bored ? ACTION_BORING :
10806                               ACTION_WAITING);
10807
10808     if (player->is_sleeping && player->use_murphy)
10809     {
10810       /* special case for sleeping Murphy when leaning against non-free tile */
10811
10812       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10813           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10814            !IS_MOVING(player->jx - 1, player->jy)))
10815         move_dir = MV_LEFT;
10816       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10817                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10818                 !IS_MOVING(player->jx + 1, player->jy)))
10819         move_dir = MV_RIGHT;
10820       else
10821         player->is_sleeping = FALSE;
10822
10823       player->dir_waiting = move_dir;
10824     }
10825
10826     if (player->is_sleeping)
10827     {
10828       if (player->num_special_action_sleeping > 0)
10829       {
10830         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10831         {
10832           int last_special_action = player->special_action_sleeping;
10833           int num_special_action = player->num_special_action_sleeping;
10834           int special_action =
10835             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10836              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10837              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10838              last_special_action + 1 : ACTION_SLEEPING);
10839           int special_graphic =
10840             el_act_dir2img(player->artwork_element, special_action, move_dir);
10841
10842           player->anim_delay_counter =
10843             graphic_info[special_graphic].anim_delay_fixed +
10844             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10845           player->post_delay_counter =
10846             graphic_info[special_graphic].post_delay_fixed +
10847             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10848
10849           player->special_action_sleeping = special_action;
10850         }
10851
10852         if (player->anim_delay_counter > 0)
10853         {
10854           player->action_waiting = player->special_action_sleeping;
10855           player->anim_delay_counter--;
10856         }
10857         else if (player->post_delay_counter > 0)
10858         {
10859           player->post_delay_counter--;
10860         }
10861       }
10862     }
10863     else if (player->is_bored)
10864     {
10865       if (player->num_special_action_bored > 0)
10866       {
10867         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10868         {
10869           int special_action =
10870             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10871           int special_graphic =
10872             el_act_dir2img(player->artwork_element, special_action, move_dir);
10873
10874           player->anim_delay_counter =
10875             graphic_info[special_graphic].anim_delay_fixed +
10876             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10877           player->post_delay_counter =
10878             graphic_info[special_graphic].post_delay_fixed +
10879             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10880
10881           player->special_action_bored = special_action;
10882         }
10883
10884         if (player->anim_delay_counter > 0)
10885         {
10886           player->action_waiting = player->special_action_bored;
10887           player->anim_delay_counter--;
10888         }
10889         else if (player->post_delay_counter > 0)
10890         {
10891           player->post_delay_counter--;
10892         }
10893       }
10894     }
10895   }
10896   else if (last_waiting)        /* waiting -> not waiting */
10897   {
10898     player->is_waiting = FALSE;
10899     player->is_bored = FALSE;
10900     player->is_sleeping = FALSE;
10901
10902     player->frame_counter_bored = -1;
10903     player->frame_counter_sleeping = -1;
10904
10905     player->anim_delay_counter = 0;
10906     player->post_delay_counter = 0;
10907
10908     player->dir_waiting = player->MovDir;
10909     player->action_waiting = ACTION_DEFAULT;
10910
10911     player->special_action_bored = ACTION_DEFAULT;
10912     player->special_action_sleeping = ACTION_DEFAULT;
10913   }
10914 }
10915
10916 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10917 {
10918   if ((!player->is_moving  && player->was_moving) ||
10919       (player->MovPos == 0 && player->was_moving) ||
10920       (player->is_snapping && !player->was_snapping) ||
10921       (player->is_dropping && !player->was_dropping))
10922   {
10923     if (!CheckSaveEngineSnapshotToList())
10924       return;
10925
10926     player->was_moving = FALSE;
10927     player->was_snapping = TRUE;
10928     player->was_dropping = TRUE;
10929   }
10930   else
10931   {
10932     if (player->is_moving)
10933       player->was_moving = TRUE;
10934
10935     if (!player->is_snapping)
10936       player->was_snapping = FALSE;
10937
10938     if (!player->is_dropping)
10939       player->was_dropping = FALSE;
10940   }
10941 }
10942
10943 static void CheckSingleStepMode(struct PlayerInfo *player)
10944 {
10945   if (tape.single_step && tape.recording && !tape.pausing)
10946   {
10947     /* as it is called "single step mode", just return to pause mode when the
10948        player stopped moving after one tile (or never starts moving at all) */
10949     if (!player->is_moving &&
10950         !player->is_pushing &&
10951         !player->is_dropping_pressed)
10952     {
10953       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10954       SnapField(player, 0, 0);                  /* stop snapping */
10955     }
10956   }
10957
10958   CheckSaveEngineSnapshot(player);
10959 }
10960
10961 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10962 {
10963   int left      = player_action & JOY_LEFT;
10964   int right     = player_action & JOY_RIGHT;
10965   int up        = player_action & JOY_UP;
10966   int down      = player_action & JOY_DOWN;
10967   int button1   = player_action & JOY_BUTTON_1;
10968   int button2   = player_action & JOY_BUTTON_2;
10969   int dx        = (left ? -1 : right ? 1 : 0);
10970   int dy        = (up   ? -1 : down  ? 1 : 0);
10971
10972   if (!player->active || tape.pausing)
10973     return 0;
10974
10975   if (player_action)
10976   {
10977     if (button1)
10978       SnapField(player, dx, dy);
10979     else
10980     {
10981       if (button2)
10982         DropElement(player);
10983
10984       MovePlayer(player, dx, dy);
10985     }
10986
10987     CheckSingleStepMode(player);
10988
10989     SetPlayerWaiting(player, FALSE);
10990
10991     return player_action;
10992   }
10993   else
10994   {
10995     /* no actions for this player (no input at player's configured device) */
10996
10997     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10998     SnapField(player, 0, 0);
10999     CheckGravityMovementWhenNotMoving(player);
11000
11001     if (player->MovPos == 0)
11002       SetPlayerWaiting(player, TRUE);
11003
11004     if (player->MovPos == 0)    /* needed for tape.playing */
11005       player->is_moving = FALSE;
11006
11007     player->is_dropping = FALSE;
11008     player->is_dropping_pressed = FALSE;
11009     player->drop_pressed_delay = 0;
11010
11011     CheckSingleStepMode(player);
11012
11013     return 0;
11014   }
11015 }
11016
11017 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11018                                          byte *tape_action)
11019 {
11020   if (!tape.use_mouse)
11021     return;
11022
11023   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11024   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11025   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11026 }
11027
11028 static void SetTapeActionFromMouseAction(byte *tape_action,
11029                                          struct MouseActionInfo *mouse_action)
11030 {
11031   if (!tape.use_mouse)
11032     return;
11033
11034   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11035   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11036   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11037 }
11038
11039 static void CheckLevelTime()
11040 {
11041   int i;
11042
11043   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11044   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11045   {
11046     if (level.native_em_level->lev->home == 0)  /* all players at home */
11047     {
11048       PlayerWins(local_player);
11049
11050       AllPlayersGone = TRUE;
11051
11052       level.native_em_level->lev->home = -1;
11053     }
11054
11055     if (level.native_em_level->ply[0]->alive == 0 &&
11056         level.native_em_level->ply[1]->alive == 0 &&
11057         level.native_em_level->ply[2]->alive == 0 &&
11058         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11059       AllPlayersGone = TRUE;
11060   }
11061   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11062   {
11063     if (game_sp.LevelSolved &&
11064         !game_sp.GameOver)                              /* game won */
11065     {
11066       PlayerWins(local_player);
11067
11068       game_sp.GameOver = TRUE;
11069
11070       AllPlayersGone = TRUE;
11071     }
11072
11073     if (game_sp.GameOver)                               /* game lost */
11074       AllPlayersGone = TRUE;
11075   }
11076   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11077   {
11078     if (game_mm.level_solved &&
11079         !game_mm.game_over)                             /* game won */
11080     {
11081       PlayerWins(local_player);
11082
11083       game_mm.game_over = TRUE;
11084
11085       AllPlayersGone = TRUE;
11086     }
11087
11088     if (game_mm.game_over)                              /* game lost */
11089       AllPlayersGone = TRUE;
11090   }
11091
11092   if (TimeFrames >= FRAMES_PER_SECOND)
11093   {
11094     TimeFrames = 0;
11095     TapeTime++;
11096
11097     for (i = 0; i < MAX_PLAYERS; i++)
11098     {
11099       struct PlayerInfo *player = &stored_player[i];
11100
11101       if (SHIELD_ON(player))
11102       {
11103         player->shield_normal_time_left--;
11104
11105         if (player->shield_deadly_time_left > 0)
11106           player->shield_deadly_time_left--;
11107       }
11108     }
11109
11110     if (!local_player->LevelSolved && !level.use_step_counter)
11111     {
11112       TimePlayed++;
11113
11114       if (TimeLeft > 0)
11115       {
11116         TimeLeft--;
11117
11118         if (TimeLeft <= 10 && setup.time_limit)
11119           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11120
11121         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11122            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11123
11124         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11125
11126         if (!TimeLeft && setup.time_limit)
11127         {
11128           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11129             level.native_em_level->lev->killed_out_of_time = TRUE;
11130           else
11131             for (i = 0; i < MAX_PLAYERS; i++)
11132               KillPlayer(&stored_player[i]);
11133         }
11134       }
11135       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11136       {
11137         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11138       }
11139
11140       level.native_em_level->lev->time =
11141         (game.no_time_limit ? TimePlayed : TimeLeft);
11142     }
11143
11144     if (tape.recording || tape.playing)
11145       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11146   }
11147
11148   if (tape.recording || tape.playing)
11149     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11150
11151   UpdateAndDisplayGameControlValues();
11152 }
11153
11154 void AdvanceFrameAndPlayerCounters(int player_nr)
11155 {
11156   int i;
11157
11158   /* advance frame counters (global frame counter and time frame counter) */
11159   FrameCounter++;
11160   TimeFrames++;
11161
11162   /* advance player counters (counters for move delay, move animation etc.) */
11163   for (i = 0; i < MAX_PLAYERS; i++)
11164   {
11165     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11166     int move_delay_value = stored_player[i].move_delay_value;
11167     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11168
11169     if (!advance_player_counters)       /* not all players may be affected */
11170       continue;
11171
11172     if (move_frames == 0)       /* less than one move per game frame */
11173     {
11174       int stepsize = TILEX / move_delay_value;
11175       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11176       int count = (stored_player[i].is_moving ?
11177                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11178
11179       if (count % delay == 0)
11180         move_frames = 1;
11181     }
11182
11183     stored_player[i].Frame += move_frames;
11184
11185     if (stored_player[i].MovPos != 0)
11186       stored_player[i].StepFrame += move_frames;
11187
11188     if (stored_player[i].move_delay > 0)
11189       stored_player[i].move_delay--;
11190
11191     /* due to bugs in previous versions, counter must count up, not down */
11192     if (stored_player[i].push_delay != -1)
11193       stored_player[i].push_delay++;
11194
11195     if (stored_player[i].drop_delay > 0)
11196       stored_player[i].drop_delay--;
11197
11198     if (stored_player[i].is_dropping_pressed)
11199       stored_player[i].drop_pressed_delay++;
11200   }
11201 }
11202
11203 void StartGameActions(boolean init_network_game, boolean record_tape,
11204                       int random_seed)
11205 {
11206   unsigned int new_random_seed = InitRND(random_seed);
11207
11208   if (record_tape)
11209     TapeStartRecording(new_random_seed);
11210
11211   if (init_network_game)
11212   {
11213     SendToServer_StartPlaying();
11214
11215     return;
11216   }
11217
11218   InitGame();
11219 }
11220
11221 void GameActionsExt()
11222 {
11223 #if 0
11224   static unsigned int game_frame_delay = 0;
11225 #endif
11226   unsigned int game_frame_delay_value;
11227   byte *recorded_player_action;
11228   byte summarized_player_action = 0;
11229   byte tape_action[MAX_PLAYERS];
11230   int i;
11231
11232   /* detect endless loops, caused by custom element programming */
11233   if (recursion_loop_detected && recursion_loop_depth == 0)
11234   {
11235     char *message = getStringCat3("Internal Error! Element ",
11236                                   EL_NAME(recursion_loop_element),
11237                                   " caused endless loop! Quit the game?");
11238
11239     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11240           EL_NAME(recursion_loop_element));
11241
11242     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11243
11244     recursion_loop_detected = FALSE;    /* if game should be continued */
11245
11246     free(message);
11247
11248     return;
11249   }
11250
11251   if (game.restart_level)
11252     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11253
11254   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11255   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11256   {
11257     if (level.native_em_level->lev->home == 0)  /* all players at home */
11258     {
11259       PlayerWins(local_player);
11260
11261       AllPlayersGone = TRUE;
11262
11263       level.native_em_level->lev->home = -1;
11264     }
11265
11266     if (level.native_em_level->ply[0]->alive == 0 &&
11267         level.native_em_level->ply[1]->alive == 0 &&
11268         level.native_em_level->ply[2]->alive == 0 &&
11269         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11270       AllPlayersGone = TRUE;
11271   }
11272   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11273   {
11274     if (game_sp.LevelSolved &&
11275         !game_sp.GameOver)                              /* game won */
11276     {
11277       PlayerWins(local_player);
11278
11279       game_sp.GameOver = TRUE;
11280
11281       AllPlayersGone = TRUE;
11282     }
11283
11284     if (game_sp.GameOver)                               /* game lost */
11285       AllPlayersGone = TRUE;
11286   }
11287   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11288   {
11289     if (game_mm.level_solved &&
11290         !game_mm.game_over)                             /* game won */
11291     {
11292       PlayerWins(local_player);
11293
11294       game_mm.game_over = TRUE;
11295
11296       AllPlayersGone = TRUE;
11297     }
11298
11299     if (game_mm.game_over)                              /* game lost */
11300       AllPlayersGone = TRUE;
11301   }
11302
11303   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11304     GameWon();
11305
11306   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11307     TapeStop();
11308
11309   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11310     return;
11311
11312   game_frame_delay_value =
11313     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11314
11315   if (tape.playing && tape.warp_forward && !tape.pausing)
11316     game_frame_delay_value = 0;
11317
11318   SetVideoFrameDelay(game_frame_delay_value);
11319
11320 #if 0
11321 #if 0
11322   /* ---------- main game synchronization point ---------- */
11323
11324   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11325
11326   printf("::: skip == %d\n", skip);
11327
11328 #else
11329   /* ---------- main game synchronization point ---------- */
11330
11331   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11332 #endif
11333 #endif
11334
11335   if (network_playing && !network_player_action_received)
11336   {
11337     /* try to get network player actions in time */
11338
11339     /* last chance to get network player actions without main loop delay */
11340     HandleNetworking();
11341
11342     /* game was quit by network peer */
11343     if (game_status != GAME_MODE_PLAYING)
11344       return;
11345
11346     if (!network_player_action_received)
11347       return;           /* failed to get network player actions in time */
11348
11349     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11350   }
11351
11352   if (tape.pausing)
11353     return;
11354
11355   /* at this point we know that we really continue executing the game */
11356
11357   network_player_action_received = FALSE;
11358
11359   /* when playing tape, read previously recorded player input from tape data */
11360   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11361
11362   local_player->effective_mouse_action = local_player->mouse_action;
11363
11364   if (recorded_player_action != NULL)
11365     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11366                                  recorded_player_action);
11367
11368   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11369   if (tape.pausing)
11370     return;
11371
11372   if (tape.set_centered_player)
11373   {
11374     game.centered_player_nr_next = tape.centered_player_nr_next;
11375     game.set_centered_player = TRUE;
11376   }
11377
11378   for (i = 0; i < MAX_PLAYERS; i++)
11379   {
11380     summarized_player_action |= stored_player[i].action;
11381
11382     if (!network_playing && (game.team_mode || tape.playing))
11383       stored_player[i].effective_action = stored_player[i].action;
11384   }
11385
11386   if (network_playing)
11387     SendToServer_MovePlayer(summarized_player_action);
11388
11389   // summarize all actions at local players mapped input device position
11390   // (this allows using different input devices in single player mode)
11391   if (!network.enabled && !game.team_mode)
11392     stored_player[map_player_action[local_player->index_nr]].effective_action =
11393       summarized_player_action;
11394
11395   if (tape.recording &&
11396       setup.team_mode &&
11397       setup.input_on_focus &&
11398       game.centered_player_nr != -1)
11399   {
11400     for (i = 0; i < MAX_PLAYERS; i++)
11401       stored_player[i].effective_action =
11402         (i == game.centered_player_nr ? summarized_player_action : 0);
11403   }
11404
11405   if (recorded_player_action != NULL)
11406     for (i = 0; i < MAX_PLAYERS; i++)
11407       stored_player[i].effective_action = recorded_player_action[i];
11408
11409   for (i = 0; i < MAX_PLAYERS; i++)
11410   {
11411     tape_action[i] = stored_player[i].effective_action;
11412
11413     /* (this may happen in the RND game engine if a player was not present on
11414        the playfield on level start, but appeared later from a custom element */
11415     if (setup.team_mode &&
11416         tape.recording &&
11417         tape_action[i] &&
11418         !tape.player_participates[i])
11419       tape.player_participates[i] = TRUE;
11420   }
11421
11422   SetTapeActionFromMouseAction(tape_action,
11423                                &local_player->effective_mouse_action);
11424
11425   /* only record actions from input devices, but not programmed actions */
11426   if (tape.recording)
11427     TapeRecordAction(tape_action);
11428
11429 #if USE_NEW_PLAYER_ASSIGNMENTS
11430   // !!! also map player actions in single player mode !!!
11431   // if (game.team_mode)
11432   if (1)
11433   {
11434     byte mapped_action[MAX_PLAYERS];
11435
11436 #if DEBUG_PLAYER_ACTIONS
11437     printf(":::");
11438     for (i = 0; i < MAX_PLAYERS; i++)
11439       printf(" %d, ", stored_player[i].effective_action);
11440 #endif
11441
11442     for (i = 0; i < MAX_PLAYERS; i++)
11443       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11444
11445     for (i = 0; i < MAX_PLAYERS; i++)
11446       stored_player[i].effective_action = mapped_action[i];
11447
11448 #if DEBUG_PLAYER_ACTIONS
11449     printf(" =>");
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451       printf(" %d, ", stored_player[i].effective_action);
11452     printf("\n");
11453 #endif
11454   }
11455 #if DEBUG_PLAYER_ACTIONS
11456   else
11457   {
11458     printf(":::");
11459     for (i = 0; i < MAX_PLAYERS; i++)
11460       printf(" %d, ", stored_player[i].effective_action);
11461     printf("\n");
11462   }
11463 #endif
11464 #endif
11465
11466   for (i = 0; i < MAX_PLAYERS; i++)
11467   {
11468     // allow engine snapshot in case of changed movement attempt
11469     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11470         (stored_player[i].effective_action & KEY_MOTION))
11471       game.snapshot.changed_action = TRUE;
11472
11473     // allow engine snapshot in case of snapping/dropping attempt
11474     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11475         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11476       game.snapshot.changed_action = TRUE;
11477
11478     game.snapshot.last_action[i] = stored_player[i].effective_action;
11479   }
11480
11481   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11482   {
11483     GameActions_EM_Main();
11484   }
11485   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11486   {
11487     GameActions_SP_Main();
11488   }
11489   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11490   {
11491     GameActions_MM_Main();
11492   }
11493   else
11494   {
11495     GameActions_RND_Main();
11496   }
11497
11498   BlitScreenToBitmap(backbuffer);
11499
11500   CheckLevelTime();
11501
11502   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11503
11504   if (global.show_frames_per_second)
11505   {
11506     static unsigned int fps_counter = 0;
11507     static int fps_frames = 0;
11508     unsigned int fps_delay_ms = Counter() - fps_counter;
11509
11510     fps_frames++;
11511
11512     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11513     {
11514       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11515
11516       fps_frames = 0;
11517       fps_counter = Counter();
11518
11519       /* always draw FPS to screen after FPS value was updated */
11520       redraw_mask |= REDRAW_FPS;
11521     }
11522
11523     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11524     if (GetDrawDeactivationMask() == REDRAW_NONE)
11525       redraw_mask |= REDRAW_FPS;
11526   }
11527 }
11528
11529 static void GameActions_CheckSaveEngineSnapshot()
11530 {
11531   if (!game.snapshot.save_snapshot)
11532     return;
11533
11534   // clear flag for saving snapshot _before_ saving snapshot
11535   game.snapshot.save_snapshot = FALSE;
11536
11537   SaveEngineSnapshotToList();
11538 }
11539
11540 void GameActions()
11541 {
11542   GameActionsExt();
11543
11544   GameActions_CheckSaveEngineSnapshot();
11545 }
11546
11547 void GameActions_EM_Main()
11548 {
11549   byte effective_action[MAX_PLAYERS];
11550   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11551   int i;
11552
11553   for (i = 0; i < MAX_PLAYERS; i++)
11554     effective_action[i] = stored_player[i].effective_action;
11555
11556   GameActions_EM(effective_action, warp_mode);
11557 }
11558
11559 void GameActions_SP_Main()
11560 {
11561   byte effective_action[MAX_PLAYERS];
11562   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11563   int i;
11564
11565   for (i = 0; i < MAX_PLAYERS; i++)
11566     effective_action[i] = stored_player[i].effective_action;
11567
11568   GameActions_SP(effective_action, warp_mode);
11569
11570   for (i = 0; i < MAX_PLAYERS; i++)
11571   {
11572     if (stored_player[i].force_dropping)
11573       stored_player[i].action |= KEY_BUTTON_DROP;
11574
11575     stored_player[i].force_dropping = FALSE;
11576   }
11577 }
11578
11579 void GameActions_MM_Main()
11580 {
11581   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11582
11583   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11584 }
11585
11586 void GameActions_RND_Main()
11587 {
11588   GameActions_RND();
11589 }
11590
11591 void GameActions_RND()
11592 {
11593   int magic_wall_x = 0, magic_wall_y = 0;
11594   int i, x, y, element, graphic, last_gfx_frame;
11595
11596   InitPlayfieldScanModeVars();
11597
11598   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11599   {
11600     SCAN_PLAYFIELD(x, y)
11601     {
11602       ChangeCount[x][y] = 0;
11603       ChangeEvent[x][y] = -1;
11604     }
11605   }
11606
11607   if (game.set_centered_player)
11608   {
11609     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11610
11611     /* switching to "all players" only possible if all players fit to screen */
11612     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11613     {
11614       game.centered_player_nr_next = game.centered_player_nr;
11615       game.set_centered_player = FALSE;
11616     }
11617
11618     /* do not switch focus to non-existing (or non-active) player */
11619     if (game.centered_player_nr_next >= 0 &&
11620         !stored_player[game.centered_player_nr_next].active)
11621     {
11622       game.centered_player_nr_next = game.centered_player_nr;
11623       game.set_centered_player = FALSE;
11624     }
11625   }
11626
11627   if (game.set_centered_player &&
11628       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11629   {
11630     int sx, sy;
11631
11632     if (game.centered_player_nr_next == -1)
11633     {
11634       setScreenCenteredToAllPlayers(&sx, &sy);
11635     }
11636     else
11637     {
11638       sx = stored_player[game.centered_player_nr_next].jx;
11639       sy = stored_player[game.centered_player_nr_next].jy;
11640     }
11641
11642     game.centered_player_nr = game.centered_player_nr_next;
11643     game.set_centered_player = FALSE;
11644
11645     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11646     DrawGameDoorValues();
11647   }
11648
11649   for (i = 0; i < MAX_PLAYERS; i++)
11650   {
11651     int actual_player_action = stored_player[i].effective_action;
11652
11653 #if 1
11654     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11655        - rnd_equinox_tetrachloride 048
11656        - rnd_equinox_tetrachloride_ii 096
11657        - rnd_emanuel_schmieg 002
11658        - doctor_sloan_ww 001, 020
11659     */
11660     if (stored_player[i].MovPos == 0)
11661       CheckGravityMovement(&stored_player[i]);
11662 #endif
11663
11664     /* overwrite programmed action with tape action */
11665     if (stored_player[i].programmed_action)
11666       actual_player_action = stored_player[i].programmed_action;
11667
11668     PlayerActions(&stored_player[i], actual_player_action);
11669
11670     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11671   }
11672
11673   ScrollScreen(NULL, SCROLL_GO_ON);
11674
11675   /* for backwards compatibility, the following code emulates a fixed bug that
11676      occured when pushing elements (causing elements that just made their last
11677      pushing step to already (if possible) make their first falling step in the
11678      same game frame, which is bad); this code is also needed to use the famous
11679      "spring push bug" which is used in older levels and might be wanted to be
11680      used also in newer levels, but in this case the buggy pushing code is only
11681      affecting the "spring" element and no other elements */
11682
11683   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11684   {
11685     for (i = 0; i < MAX_PLAYERS; i++)
11686     {
11687       struct PlayerInfo *player = &stored_player[i];
11688       int x = player->jx;
11689       int y = player->jy;
11690
11691       if (player->active && player->is_pushing && player->is_moving &&
11692           IS_MOVING(x, y) &&
11693           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11694            Feld[x][y] == EL_SPRING))
11695       {
11696         ContinueMoving(x, y);
11697
11698         /* continue moving after pushing (this is actually a bug) */
11699         if (!IS_MOVING(x, y))
11700           Stop[x][y] = FALSE;
11701       }
11702     }
11703   }
11704
11705   SCAN_PLAYFIELD(x, y)
11706   {
11707     ChangeCount[x][y] = 0;
11708     ChangeEvent[x][y] = -1;
11709
11710     /* this must be handled before main playfield loop */
11711     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11712     {
11713       MovDelay[x][y]--;
11714       if (MovDelay[x][y] <= 0)
11715         RemoveField(x, y);
11716     }
11717
11718     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11719     {
11720       MovDelay[x][y]--;
11721       if (MovDelay[x][y] <= 0)
11722       {
11723         RemoveField(x, y);
11724         TEST_DrawLevelField(x, y);
11725
11726         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11727       }
11728     }
11729
11730 #if DEBUG
11731     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11732     {
11733       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11734       printf("GameActions(): This should never happen!\n");
11735
11736       ChangePage[x][y] = -1;
11737     }
11738 #endif
11739
11740     Stop[x][y] = FALSE;
11741     if (WasJustMoving[x][y] > 0)
11742       WasJustMoving[x][y]--;
11743     if (WasJustFalling[x][y] > 0)
11744       WasJustFalling[x][y]--;
11745     if (CheckCollision[x][y] > 0)
11746       CheckCollision[x][y]--;
11747     if (CheckImpact[x][y] > 0)
11748       CheckImpact[x][y]--;
11749
11750     GfxFrame[x][y]++;
11751
11752     /* reset finished pushing action (not done in ContinueMoving() to allow
11753        continuous pushing animation for elements with zero push delay) */
11754     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11755     {
11756       ResetGfxAnimation(x, y);
11757       TEST_DrawLevelField(x, y);
11758     }
11759
11760 #if DEBUG
11761     if (IS_BLOCKED(x, y))
11762     {
11763       int oldx, oldy;
11764
11765       Blocked2Moving(x, y, &oldx, &oldy);
11766       if (!IS_MOVING(oldx, oldy))
11767       {
11768         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11769         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11770         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11771         printf("GameActions(): This should never happen!\n");
11772       }
11773     }
11774 #endif
11775   }
11776
11777   SCAN_PLAYFIELD(x, y)
11778   {
11779     element = Feld[x][y];
11780     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11781     last_gfx_frame = GfxFrame[x][y];
11782
11783     ResetGfxFrame(x, y);
11784
11785     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11786       DrawLevelGraphicAnimation(x, y, graphic);
11787
11788     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11789         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11790       ResetRandomAnimationValue(x, y);
11791
11792     SetRandomAnimationValue(x, y);
11793
11794     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11795
11796     if (IS_INACTIVE(element))
11797     {
11798       if (IS_ANIMATED(graphic))
11799         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11800
11801       continue;
11802     }
11803
11804     /* this may take place after moving, so 'element' may have changed */
11805     if (IS_CHANGING(x, y) &&
11806         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11807     {
11808       int page = element_info[element].event_page_nr[CE_DELAY];
11809
11810       HandleElementChange(x, y, page);
11811
11812       element = Feld[x][y];
11813       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11814     }
11815
11816     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11817     {
11818       StartMoving(x, y);
11819
11820       element = Feld[x][y];
11821       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11822
11823       if (IS_ANIMATED(graphic) &&
11824           !IS_MOVING(x, y) &&
11825           !Stop[x][y])
11826         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11827
11828       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11829         TEST_DrawTwinkleOnField(x, y);
11830     }
11831     else if (element == EL_ACID)
11832     {
11833       if (!Stop[x][y])
11834         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11835     }
11836     else if ((element == EL_EXIT_OPEN ||
11837               element == EL_EM_EXIT_OPEN ||
11838               element == EL_SP_EXIT_OPEN ||
11839               element == EL_STEEL_EXIT_OPEN ||
11840               element == EL_EM_STEEL_EXIT_OPEN ||
11841               element == EL_SP_TERMINAL ||
11842               element == EL_SP_TERMINAL_ACTIVE ||
11843               element == EL_EXTRA_TIME ||
11844               element == EL_SHIELD_NORMAL ||
11845               element == EL_SHIELD_DEADLY) &&
11846              IS_ANIMATED(graphic))
11847       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11848     else if (IS_MOVING(x, y))
11849       ContinueMoving(x, y);
11850     else if (IS_ACTIVE_BOMB(element))
11851       CheckDynamite(x, y);
11852     else if (element == EL_AMOEBA_GROWING)
11853       AmoebeWaechst(x, y);
11854     else if (element == EL_AMOEBA_SHRINKING)
11855       AmoebaDisappearing(x, y);
11856
11857 #if !USE_NEW_AMOEBA_CODE
11858     else if (IS_AMOEBALIVE(element))
11859       AmoebeAbleger(x, y);
11860 #endif
11861
11862     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11863       Life(x, y);
11864     else if (element == EL_EXIT_CLOSED)
11865       CheckExit(x, y);
11866     else if (element == EL_EM_EXIT_CLOSED)
11867       CheckExitEM(x, y);
11868     else if (element == EL_STEEL_EXIT_CLOSED)
11869       CheckExitSteel(x, y);
11870     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11871       CheckExitSteelEM(x, y);
11872     else if (element == EL_SP_EXIT_CLOSED)
11873       CheckExitSP(x, y);
11874     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11875              element == EL_EXPANDABLE_STEELWALL_GROWING)
11876       MauerWaechst(x, y);
11877     else if (element == EL_EXPANDABLE_WALL ||
11878              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11879              element == EL_EXPANDABLE_WALL_VERTICAL ||
11880              element == EL_EXPANDABLE_WALL_ANY ||
11881              element == EL_BD_EXPANDABLE_WALL)
11882       MauerAbleger(x, y);
11883     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11884              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11885              element == EL_EXPANDABLE_STEELWALL_ANY)
11886       MauerAblegerStahl(x, y);
11887     else if (element == EL_FLAMES)
11888       CheckForDragon(x, y);
11889     else if (element == EL_EXPLOSION)
11890       ; /* drawing of correct explosion animation is handled separately */
11891     else if (element == EL_ELEMENT_SNAPPING ||
11892              element == EL_DIAGONAL_SHRINKING ||
11893              element == EL_DIAGONAL_GROWING)
11894     {
11895       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11896
11897       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11898     }
11899     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11900       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11901
11902     if (IS_BELT_ACTIVE(element))
11903       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11904
11905     if (game.magic_wall_active)
11906     {
11907       int jx = local_player->jx, jy = local_player->jy;
11908
11909       /* play the element sound at the position nearest to the player */
11910       if ((element == EL_MAGIC_WALL_FULL ||
11911            element == EL_MAGIC_WALL_ACTIVE ||
11912            element == EL_MAGIC_WALL_EMPTYING ||
11913            element == EL_BD_MAGIC_WALL_FULL ||
11914            element == EL_BD_MAGIC_WALL_ACTIVE ||
11915            element == EL_BD_MAGIC_WALL_EMPTYING ||
11916            element == EL_DC_MAGIC_WALL_FULL ||
11917            element == EL_DC_MAGIC_WALL_ACTIVE ||
11918            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11919           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11920       {
11921         magic_wall_x = x;
11922         magic_wall_y = y;
11923       }
11924     }
11925   }
11926
11927 #if USE_NEW_AMOEBA_CODE
11928   /* new experimental amoeba growth stuff */
11929   if (!(FrameCounter % 8))
11930   {
11931     static unsigned int random = 1684108901;
11932
11933     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11934     {
11935       x = RND(lev_fieldx);
11936       y = RND(lev_fieldy);
11937       element = Feld[x][y];
11938
11939       if (!IS_PLAYER(x,y) &&
11940           (element == EL_EMPTY ||
11941            CAN_GROW_INTO(element) ||
11942            element == EL_QUICKSAND_EMPTY ||
11943            element == EL_QUICKSAND_FAST_EMPTY ||
11944            element == EL_ACID_SPLASH_LEFT ||
11945            element == EL_ACID_SPLASH_RIGHT))
11946       {
11947         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11948             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11949             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11950             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11951           Feld[x][y] = EL_AMOEBA_DROP;
11952       }
11953
11954       random = random * 129 + 1;
11955     }
11956   }
11957 #endif
11958
11959   game.explosions_delayed = FALSE;
11960
11961   SCAN_PLAYFIELD(x, y)
11962   {
11963     element = Feld[x][y];
11964
11965     if (ExplodeField[x][y])
11966       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11967     else if (element == EL_EXPLOSION)
11968       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11969
11970     ExplodeField[x][y] = EX_TYPE_NONE;
11971   }
11972
11973   game.explosions_delayed = TRUE;
11974
11975   if (game.magic_wall_active)
11976   {
11977     if (!(game.magic_wall_time_left % 4))
11978     {
11979       int element = Feld[magic_wall_x][magic_wall_y];
11980
11981       if (element == EL_BD_MAGIC_WALL_FULL ||
11982           element == EL_BD_MAGIC_WALL_ACTIVE ||
11983           element == EL_BD_MAGIC_WALL_EMPTYING)
11984         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11985       else if (element == EL_DC_MAGIC_WALL_FULL ||
11986                element == EL_DC_MAGIC_WALL_ACTIVE ||
11987                element == EL_DC_MAGIC_WALL_EMPTYING)
11988         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11989       else
11990         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11991     }
11992
11993     if (game.magic_wall_time_left > 0)
11994     {
11995       game.magic_wall_time_left--;
11996
11997       if (!game.magic_wall_time_left)
11998       {
11999         SCAN_PLAYFIELD(x, y)
12000         {
12001           element = Feld[x][y];
12002
12003           if (element == EL_MAGIC_WALL_ACTIVE ||
12004               element == EL_MAGIC_WALL_FULL)
12005           {
12006             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12007             TEST_DrawLevelField(x, y);
12008           }
12009           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12010                    element == EL_BD_MAGIC_WALL_FULL)
12011           {
12012             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12013             TEST_DrawLevelField(x, y);
12014           }
12015           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12016                    element == EL_DC_MAGIC_WALL_FULL)
12017           {
12018             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12019             TEST_DrawLevelField(x, y);
12020           }
12021         }
12022
12023         game.magic_wall_active = FALSE;
12024       }
12025     }
12026   }
12027
12028   if (game.light_time_left > 0)
12029   {
12030     game.light_time_left--;
12031
12032     if (game.light_time_left == 0)
12033       RedrawAllLightSwitchesAndInvisibleElements();
12034   }
12035
12036   if (game.timegate_time_left > 0)
12037   {
12038     game.timegate_time_left--;
12039
12040     if (game.timegate_time_left == 0)
12041       CloseAllOpenTimegates();
12042   }
12043
12044   if (game.lenses_time_left > 0)
12045   {
12046     game.lenses_time_left--;
12047
12048     if (game.lenses_time_left == 0)
12049       RedrawAllInvisibleElementsForLenses();
12050   }
12051
12052   if (game.magnify_time_left > 0)
12053   {
12054     game.magnify_time_left--;
12055
12056     if (game.magnify_time_left == 0)
12057       RedrawAllInvisibleElementsForMagnifier();
12058   }
12059
12060   for (i = 0; i < MAX_PLAYERS; i++)
12061   {
12062     struct PlayerInfo *player = &stored_player[i];
12063
12064     if (SHIELD_ON(player))
12065     {
12066       if (player->shield_deadly_time_left)
12067         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12068       else if (player->shield_normal_time_left)
12069         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12070     }
12071   }
12072
12073 #if USE_DELAYED_GFX_REDRAW
12074   SCAN_PLAYFIELD(x, y)
12075   {
12076     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12077     {
12078       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12079          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12080
12081       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12082         DrawLevelField(x, y);
12083
12084       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12085         DrawLevelFieldCrumbled(x, y);
12086
12087       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12088         DrawLevelFieldCrumbledNeighbours(x, y);
12089
12090       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12091         DrawTwinkleOnField(x, y);
12092     }
12093
12094     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12095   }
12096 #endif
12097
12098   DrawAllPlayers();
12099   PlayAllPlayersSound();
12100
12101   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12102   {
12103     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12104
12105     local_player->show_envelope = 0;
12106   }
12107
12108   /* use random number generator in every frame to make it less predictable */
12109   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12110     RND(1);
12111 }
12112
12113 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12114 {
12115   int min_x = x, min_y = y, max_x = x, max_y = y;
12116   int i;
12117
12118   for (i = 0; i < MAX_PLAYERS; i++)
12119   {
12120     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12121
12122     if (!stored_player[i].active || &stored_player[i] == player)
12123       continue;
12124
12125     min_x = MIN(min_x, jx);
12126     min_y = MIN(min_y, jy);
12127     max_x = MAX(max_x, jx);
12128     max_y = MAX(max_y, jy);
12129   }
12130
12131   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12132 }
12133
12134 static boolean AllPlayersInVisibleScreen()
12135 {
12136   int i;
12137
12138   for (i = 0; i < MAX_PLAYERS; i++)
12139   {
12140     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12141
12142     if (!stored_player[i].active)
12143       continue;
12144
12145     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12146       return FALSE;
12147   }
12148
12149   return TRUE;
12150 }
12151
12152 void ScrollLevel(int dx, int dy)
12153 {
12154   int scroll_offset = 2 * TILEX_VAR;
12155   int x, y;
12156
12157   BlitBitmap(drawto_field, drawto_field,
12158              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12159              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12160              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12161              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12162              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12163              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12164
12165   if (dx != 0)
12166   {
12167     x = (dx == 1 ? BX1 : BX2);
12168     for (y = BY1; y <= BY2; y++)
12169       DrawScreenField(x, y);
12170   }
12171
12172   if (dy != 0)
12173   {
12174     y = (dy == 1 ? BY1 : BY2);
12175     for (x = BX1; x <= BX2; x++)
12176       DrawScreenField(x, y);
12177   }
12178
12179   redraw_mask |= REDRAW_FIELD;
12180 }
12181
12182 static boolean canFallDown(struct PlayerInfo *player)
12183 {
12184   int jx = player->jx, jy = player->jy;
12185
12186   return (IN_LEV_FIELD(jx, jy + 1) &&
12187           (IS_FREE(jx, jy + 1) ||
12188            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12189           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12190           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12191 }
12192
12193 static boolean canPassField(int x, int y, int move_dir)
12194 {
12195   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12196   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12197   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12198   int nextx = x + dx;
12199   int nexty = y + dy;
12200   int element = Feld[x][y];
12201
12202   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12203           !CAN_MOVE(element) &&
12204           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12205           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12206           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12207 }
12208
12209 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12210 {
12211   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12212   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12213   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12214   int newx = x + dx;
12215   int newy = y + dy;
12216
12217   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12218           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12219           (IS_DIGGABLE(Feld[newx][newy]) ||
12220            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12221            canPassField(newx, newy, move_dir)));
12222 }
12223
12224 static void CheckGravityMovement(struct PlayerInfo *player)
12225 {
12226   if (player->gravity && !player->programmed_action)
12227   {
12228     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12229     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12230     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12231     int jx = player->jx, jy = player->jy;
12232     boolean player_is_moving_to_valid_field =
12233       (!player_is_snapping &&
12234        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12235         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12236     boolean player_can_fall_down = canFallDown(player);
12237
12238     if (player_can_fall_down &&
12239         !player_is_moving_to_valid_field)
12240       player->programmed_action = MV_DOWN;
12241   }
12242 }
12243
12244 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12245 {
12246   return CheckGravityMovement(player);
12247
12248   if (player->gravity && !player->programmed_action)
12249   {
12250     int jx = player->jx, jy = player->jy;
12251     boolean field_under_player_is_free =
12252       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12253     boolean player_is_standing_on_valid_field =
12254       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12255        (IS_WALKABLE(Feld[jx][jy]) &&
12256         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12257
12258     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12259       player->programmed_action = MV_DOWN;
12260   }
12261 }
12262
12263 /*
12264   MovePlayerOneStep()
12265   -----------------------------------------------------------------------------
12266   dx, dy:               direction (non-diagonal) to try to move the player to
12267   real_dx, real_dy:     direction as read from input device (can be diagonal)
12268 */
12269
12270 boolean MovePlayerOneStep(struct PlayerInfo *player,
12271                           int dx, int dy, int real_dx, int real_dy)
12272 {
12273   int jx = player->jx, jy = player->jy;
12274   int new_jx = jx + dx, new_jy = jy + dy;
12275   int can_move;
12276   boolean player_can_move = !player->cannot_move;
12277
12278   if (!player->active || (!dx && !dy))
12279     return MP_NO_ACTION;
12280
12281   player->MovDir = (dx < 0 ? MV_LEFT :
12282                     dx > 0 ? MV_RIGHT :
12283                     dy < 0 ? MV_UP :
12284                     dy > 0 ? MV_DOWN :  MV_NONE);
12285
12286   if (!IN_LEV_FIELD(new_jx, new_jy))
12287     return MP_NO_ACTION;
12288
12289   if (!player_can_move)
12290   {
12291     if (player->MovPos == 0)
12292     {
12293       player->is_moving = FALSE;
12294       player->is_digging = FALSE;
12295       player->is_collecting = FALSE;
12296       player->is_snapping = FALSE;
12297       player->is_pushing = FALSE;
12298     }
12299   }
12300
12301   if (!network.enabled && game.centered_player_nr == -1 &&
12302       !AllPlayersInSight(player, new_jx, new_jy))
12303     return MP_NO_ACTION;
12304
12305   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12306   if (can_move != MP_MOVING)
12307     return can_move;
12308
12309   /* check if DigField() has caused relocation of the player */
12310   if (player->jx != jx || player->jy != jy)
12311     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12312
12313   StorePlayer[jx][jy] = 0;
12314   player->last_jx = jx;
12315   player->last_jy = jy;
12316   player->jx = new_jx;
12317   player->jy = new_jy;
12318   StorePlayer[new_jx][new_jy] = player->element_nr;
12319
12320   if (player->move_delay_value_next != -1)
12321   {
12322     player->move_delay_value = player->move_delay_value_next;
12323     player->move_delay_value_next = -1;
12324   }
12325
12326   player->MovPos =
12327     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12328
12329   player->step_counter++;
12330
12331   PlayerVisit[jx][jy] = FrameCounter;
12332
12333   player->is_moving = TRUE;
12334
12335 #if 1
12336   /* should better be called in MovePlayer(), but this breaks some tapes */
12337   ScrollPlayer(player, SCROLL_INIT);
12338 #endif
12339
12340   return MP_MOVING;
12341 }
12342
12343 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12344 {
12345   int jx = player->jx, jy = player->jy;
12346   int old_jx = jx, old_jy = jy;
12347   int moved = MP_NO_ACTION;
12348
12349   if (!player->active)
12350     return FALSE;
12351
12352   if (!dx && !dy)
12353   {
12354     if (player->MovPos == 0)
12355     {
12356       player->is_moving = FALSE;
12357       player->is_digging = FALSE;
12358       player->is_collecting = FALSE;
12359       player->is_snapping = FALSE;
12360       player->is_pushing = FALSE;
12361     }
12362
12363     return FALSE;
12364   }
12365
12366   if (player->move_delay > 0)
12367     return FALSE;
12368
12369   player->move_delay = -1;              /* set to "uninitialized" value */
12370
12371   /* store if player is automatically moved to next field */
12372   player->is_auto_moving = (player->programmed_action != MV_NONE);
12373
12374   /* remove the last programmed player action */
12375   player->programmed_action = 0;
12376
12377   if (player->MovPos)
12378   {
12379     /* should only happen if pre-1.2 tape recordings are played */
12380     /* this is only for backward compatibility */
12381
12382     int original_move_delay_value = player->move_delay_value;
12383
12384 #if DEBUG
12385     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12386            tape.counter);
12387 #endif
12388
12389     /* scroll remaining steps with finest movement resolution */
12390     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12391
12392     while (player->MovPos)
12393     {
12394       ScrollPlayer(player, SCROLL_GO_ON);
12395       ScrollScreen(NULL, SCROLL_GO_ON);
12396
12397       AdvanceFrameAndPlayerCounters(player->index_nr);
12398
12399       DrawAllPlayers();
12400       BackToFront_WithFrameDelay(0);
12401     }
12402
12403     player->move_delay_value = original_move_delay_value;
12404   }
12405
12406   player->is_active = FALSE;
12407
12408   if (player->last_move_dir & MV_HORIZONTAL)
12409   {
12410     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12411       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12412   }
12413   else
12414   {
12415     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12416       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12417   }
12418
12419   if (!moved && !player->is_active)
12420   {
12421     player->is_moving = FALSE;
12422     player->is_digging = FALSE;
12423     player->is_collecting = FALSE;
12424     player->is_snapping = FALSE;
12425     player->is_pushing = FALSE;
12426   }
12427
12428   jx = player->jx;
12429   jy = player->jy;
12430
12431   if (moved & MP_MOVING && !ScreenMovPos &&
12432       (player->index_nr == game.centered_player_nr ||
12433        game.centered_player_nr == -1))
12434   {
12435     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12436     int offset = game.scroll_delay_value;
12437
12438     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12439     {
12440       /* actual player has left the screen -- scroll in that direction */
12441       if (jx != old_jx)         /* player has moved horizontally */
12442         scroll_x += (jx - old_jx);
12443       else                      /* player has moved vertically */
12444         scroll_y += (jy - old_jy);
12445     }
12446     else
12447     {
12448       if (jx != old_jx)         /* player has moved horizontally */
12449       {
12450         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12451             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12452           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12453
12454         /* don't scroll over playfield boundaries */
12455         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12456           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12457
12458         /* don't scroll more than one field at a time */
12459         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12460
12461         /* don't scroll against the player's moving direction */
12462         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12463             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12464           scroll_x = old_scroll_x;
12465       }
12466       else                      /* player has moved vertically */
12467       {
12468         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12469             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12470           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12471
12472         /* don't scroll over playfield boundaries */
12473         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12474           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12475
12476         /* don't scroll more than one field at a time */
12477         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12478
12479         /* don't scroll against the player's moving direction */
12480         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12481             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12482           scroll_y = old_scroll_y;
12483       }
12484     }
12485
12486     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12487     {
12488       if (!network.enabled && game.centered_player_nr == -1 &&
12489           !AllPlayersInVisibleScreen())
12490       {
12491         scroll_x = old_scroll_x;
12492         scroll_y = old_scroll_y;
12493       }
12494       else
12495       {
12496         ScrollScreen(player, SCROLL_INIT);
12497         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12498       }
12499     }
12500   }
12501
12502   player->StepFrame = 0;
12503
12504   if (moved & MP_MOVING)
12505   {
12506     if (old_jx != jx && old_jy == jy)
12507       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12508     else if (old_jx == jx && old_jy != jy)
12509       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12510
12511     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12512
12513     player->last_move_dir = player->MovDir;
12514     player->is_moving = TRUE;
12515     player->is_snapping = FALSE;
12516     player->is_switching = FALSE;
12517     player->is_dropping = FALSE;
12518     player->is_dropping_pressed = FALSE;
12519     player->drop_pressed_delay = 0;
12520
12521 #if 0
12522     /* should better be called here than above, but this breaks some tapes */
12523     ScrollPlayer(player, SCROLL_INIT);
12524 #endif
12525   }
12526   else
12527   {
12528     CheckGravityMovementWhenNotMoving(player);
12529
12530     player->is_moving = FALSE;
12531
12532     /* at this point, the player is allowed to move, but cannot move right now
12533        (e.g. because of something blocking the way) -- ensure that the player
12534        is also allowed to move in the next frame (in old versions before 3.1.1,
12535        the player was forced to wait again for eight frames before next try) */
12536
12537     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12538       player->move_delay = 0;   /* allow direct movement in the next frame */
12539   }
12540
12541   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12542     player->move_delay = player->move_delay_value;
12543
12544   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12545   {
12546     TestIfPlayerTouchesBadThing(jx, jy);
12547     TestIfPlayerTouchesCustomElement(jx, jy);
12548   }
12549
12550   if (!player->active)
12551     RemovePlayer(player);
12552
12553   return moved;
12554 }
12555
12556 void ScrollPlayer(struct PlayerInfo *player, int mode)
12557 {
12558   int jx = player->jx, jy = player->jy;
12559   int last_jx = player->last_jx, last_jy = player->last_jy;
12560   int move_stepsize = TILEX / player->move_delay_value;
12561
12562   if (!player->active)
12563     return;
12564
12565   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12566     return;
12567
12568   if (mode == SCROLL_INIT)
12569   {
12570     player->actual_frame_counter = FrameCounter;
12571     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12572
12573     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12574         Feld[last_jx][last_jy] == EL_EMPTY)
12575     {
12576       int last_field_block_delay = 0;   /* start with no blocking at all */
12577       int block_delay_adjustment = player->block_delay_adjustment;
12578
12579       /* if player blocks last field, add delay for exactly one move */
12580       if (player->block_last_field)
12581       {
12582         last_field_block_delay += player->move_delay_value;
12583
12584         /* when blocking enabled, prevent moving up despite gravity */
12585         if (player->gravity && player->MovDir == MV_UP)
12586           block_delay_adjustment = -1;
12587       }
12588
12589       /* add block delay adjustment (also possible when not blocking) */
12590       last_field_block_delay += block_delay_adjustment;
12591
12592       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12593       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12594     }
12595
12596     if (player->MovPos != 0)    /* player has not yet reached destination */
12597       return;
12598   }
12599   else if (!FrameReached(&player->actual_frame_counter, 1))
12600     return;
12601
12602   if (player->MovPos != 0)
12603   {
12604     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12605     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12606
12607     /* before DrawPlayer() to draw correct player graphic for this case */
12608     if (player->MovPos == 0)
12609       CheckGravityMovement(player);
12610   }
12611
12612   if (player->MovPos == 0)      /* player reached destination field */
12613   {
12614     if (player->move_delay_reset_counter > 0)
12615     {
12616       player->move_delay_reset_counter--;
12617
12618       if (player->move_delay_reset_counter == 0)
12619       {
12620         /* continue with normal speed after quickly moving through gate */
12621         HALVE_PLAYER_SPEED(player);
12622
12623         /* be able to make the next move without delay */
12624         player->move_delay = 0;
12625       }
12626     }
12627
12628     player->last_jx = jx;
12629     player->last_jy = jy;
12630
12631     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12632         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12633         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12634         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12635         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12636         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12637         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12638         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12639     {
12640       DrawPlayer(player);       /* needed here only to cleanup last field */
12641       RemovePlayer(player);
12642
12643       if (local_player->friends_still_needed == 0 ||
12644           IS_SP_ELEMENT(Feld[jx][jy]))
12645         PlayerWins(player);
12646     }
12647
12648     /* this breaks one level: "machine", level 000 */
12649     {
12650       int move_direction = player->MovDir;
12651       int enter_side = MV_DIR_OPPOSITE(move_direction);
12652       int leave_side = move_direction;
12653       int old_jx = last_jx;
12654       int old_jy = last_jy;
12655       int old_element = Feld[old_jx][old_jy];
12656       int new_element = Feld[jx][jy];
12657
12658       if (IS_CUSTOM_ELEMENT(old_element))
12659         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12660                                    CE_LEFT_BY_PLAYER,
12661                                    player->index_bit, leave_side);
12662
12663       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12664                                           CE_PLAYER_LEAVES_X,
12665                                           player->index_bit, leave_side);
12666
12667       if (IS_CUSTOM_ELEMENT(new_element))
12668         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12669                                    player->index_bit, enter_side);
12670
12671       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12672                                           CE_PLAYER_ENTERS_X,
12673                                           player->index_bit, enter_side);
12674
12675       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12676                                         CE_MOVE_OF_X, move_direction);
12677     }
12678
12679     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12680     {
12681       TestIfPlayerTouchesBadThing(jx, jy);
12682       TestIfPlayerTouchesCustomElement(jx, jy);
12683
12684       /* needed because pushed element has not yet reached its destination,
12685          so it would trigger a change event at its previous field location */
12686       if (!player->is_pushing)
12687         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12688
12689       if (!player->active)
12690         RemovePlayer(player);
12691     }
12692
12693     if (!local_player->LevelSolved && level.use_step_counter)
12694     {
12695       int i;
12696
12697       TimePlayed++;
12698
12699       if (TimeLeft > 0)
12700       {
12701         TimeLeft--;
12702
12703         if (TimeLeft <= 10 && setup.time_limit)
12704           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12705
12706         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12707
12708         DisplayGameControlValues();
12709
12710         if (!TimeLeft && setup.time_limit)
12711           for (i = 0; i < MAX_PLAYERS; i++)
12712             KillPlayer(&stored_player[i]);
12713       }
12714       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12715       {
12716         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12717
12718         DisplayGameControlValues();
12719       }
12720     }
12721
12722     if (tape.single_step && tape.recording && !tape.pausing &&
12723         !player->programmed_action)
12724       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12725
12726     if (!player->programmed_action)
12727       CheckSaveEngineSnapshot(player);
12728   }
12729 }
12730
12731 void ScrollScreen(struct PlayerInfo *player, int mode)
12732 {
12733   static unsigned int screen_frame_counter = 0;
12734
12735   if (mode == SCROLL_INIT)
12736   {
12737     /* set scrolling step size according to actual player's moving speed */
12738     ScrollStepSize = TILEX / player->move_delay_value;
12739
12740     screen_frame_counter = FrameCounter;
12741     ScreenMovDir = player->MovDir;
12742     ScreenMovPos = player->MovPos;
12743     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12744     return;
12745   }
12746   else if (!FrameReached(&screen_frame_counter, 1))
12747     return;
12748
12749   if (ScreenMovPos)
12750   {
12751     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12752     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12753     redraw_mask |= REDRAW_FIELD;
12754   }
12755   else
12756     ScreenMovDir = MV_NONE;
12757 }
12758
12759 void TestIfPlayerTouchesCustomElement(int x, int y)
12760 {
12761   static int xy[4][2] =
12762   {
12763     { 0, -1 },
12764     { -1, 0 },
12765     { +1, 0 },
12766     { 0, +1 }
12767   };
12768   static int trigger_sides[4][2] =
12769   {
12770     /* center side       border side */
12771     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12772     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12773     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12774     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12775   };
12776   static int touch_dir[4] =
12777   {
12778     MV_LEFT | MV_RIGHT,
12779     MV_UP   | MV_DOWN,
12780     MV_UP   | MV_DOWN,
12781     MV_LEFT | MV_RIGHT
12782   };
12783   int center_element = Feld[x][y];      /* should always be non-moving! */
12784   int i;
12785
12786   for (i = 0; i < NUM_DIRECTIONS; i++)
12787   {
12788     int xx = x + xy[i][0];
12789     int yy = y + xy[i][1];
12790     int center_side = trigger_sides[i][0];
12791     int border_side = trigger_sides[i][1];
12792     int border_element;
12793
12794     if (!IN_LEV_FIELD(xx, yy))
12795       continue;
12796
12797     if (IS_PLAYER(x, y))                /* player found at center element */
12798     {
12799       struct PlayerInfo *player = PLAYERINFO(x, y);
12800
12801       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12802         border_element = Feld[xx][yy];          /* may be moving! */
12803       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12804         border_element = Feld[xx][yy];
12805       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12806         border_element = MovingOrBlocked2Element(xx, yy);
12807       else
12808         continue;               /* center and border element do not touch */
12809
12810       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12811                                  player->index_bit, border_side);
12812       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12813                                           CE_PLAYER_TOUCHES_X,
12814                                           player->index_bit, border_side);
12815
12816       {
12817         /* use player element that is initially defined in the level playfield,
12818            not the player element that corresponds to the runtime player number
12819            (example: a level that contains EL_PLAYER_3 as the only player would
12820            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12821         int player_element = PLAYERINFO(x, y)->initial_element;
12822
12823         CheckElementChangeBySide(xx, yy, border_element, player_element,
12824                                  CE_TOUCHING_X, border_side);
12825       }
12826     }
12827     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12828     {
12829       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12830
12831       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12832       {
12833         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12834           continue;             /* center and border element do not touch */
12835       }
12836
12837       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12838                                  player->index_bit, center_side);
12839       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12840                                           CE_PLAYER_TOUCHES_X,
12841                                           player->index_bit, center_side);
12842
12843       {
12844         /* use player element that is initially defined in the level playfield,
12845            not the player element that corresponds to the runtime player number
12846            (example: a level that contains EL_PLAYER_3 as the only player would
12847            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12848         int player_element = PLAYERINFO(xx, yy)->initial_element;
12849
12850         CheckElementChangeBySide(x, y, center_element, player_element,
12851                                  CE_TOUCHING_X, center_side);
12852       }
12853
12854       break;
12855     }
12856   }
12857 }
12858
12859 void TestIfElementTouchesCustomElement(int x, int y)
12860 {
12861   static int xy[4][2] =
12862   {
12863     { 0, -1 },
12864     { -1, 0 },
12865     { +1, 0 },
12866     { 0, +1 }
12867   };
12868   static int trigger_sides[4][2] =
12869   {
12870     /* center side      border side */
12871     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12872     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12873     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12874     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12875   };
12876   static int touch_dir[4] =
12877   {
12878     MV_LEFT | MV_RIGHT,
12879     MV_UP   | MV_DOWN,
12880     MV_UP   | MV_DOWN,
12881     MV_LEFT | MV_RIGHT
12882   };
12883   boolean change_center_element = FALSE;
12884   int center_element = Feld[x][y];      /* should always be non-moving! */
12885   int border_element_old[NUM_DIRECTIONS];
12886   int i;
12887
12888   for (i = 0; i < NUM_DIRECTIONS; i++)
12889   {
12890     int xx = x + xy[i][0];
12891     int yy = y + xy[i][1];
12892     int border_element;
12893
12894     border_element_old[i] = -1;
12895
12896     if (!IN_LEV_FIELD(xx, yy))
12897       continue;
12898
12899     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12900       border_element = Feld[xx][yy];    /* may be moving! */
12901     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12902       border_element = Feld[xx][yy];
12903     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12904       border_element = MovingOrBlocked2Element(xx, yy);
12905     else
12906       continue;                 /* center and border element do not touch */
12907
12908     border_element_old[i] = border_element;
12909   }
12910
12911   for (i = 0; i < NUM_DIRECTIONS; i++)
12912   {
12913     int xx = x + xy[i][0];
12914     int yy = y + xy[i][1];
12915     int center_side = trigger_sides[i][0];
12916     int border_element = border_element_old[i];
12917
12918     if (border_element == -1)
12919       continue;
12920
12921     /* check for change of border element */
12922     CheckElementChangeBySide(xx, yy, border_element, center_element,
12923                              CE_TOUCHING_X, center_side);
12924
12925     /* (center element cannot be player, so we dont have to check this here) */
12926   }
12927
12928   for (i = 0; i < NUM_DIRECTIONS; i++)
12929   {
12930     int xx = x + xy[i][0];
12931     int yy = y + xy[i][1];
12932     int border_side = trigger_sides[i][1];
12933     int border_element = border_element_old[i];
12934
12935     if (border_element == -1)
12936       continue;
12937
12938     /* check for change of center element (but change it only once) */
12939     if (!change_center_element)
12940       change_center_element =
12941         CheckElementChangeBySide(x, y, center_element, border_element,
12942                                  CE_TOUCHING_X, border_side);
12943
12944     if (IS_PLAYER(xx, yy))
12945     {
12946       /* use player element that is initially defined in the level playfield,
12947          not the player element that corresponds to the runtime player number
12948          (example: a level that contains EL_PLAYER_3 as the only player would
12949          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12950       int player_element = PLAYERINFO(xx, yy)->initial_element;
12951
12952       CheckElementChangeBySide(x, y, center_element, player_element,
12953                                CE_TOUCHING_X, border_side);
12954     }
12955   }
12956 }
12957
12958 void TestIfElementHitsCustomElement(int x, int y, int direction)
12959 {
12960   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12961   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12962   int hitx = x + dx, hity = y + dy;
12963   int hitting_element = Feld[x][y];
12964   int touched_element;
12965
12966   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12967     return;
12968
12969   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12970                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12971
12972   if (IN_LEV_FIELD(hitx, hity))
12973   {
12974     int opposite_direction = MV_DIR_OPPOSITE(direction);
12975     int hitting_side = direction;
12976     int touched_side = opposite_direction;
12977     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12978                           MovDir[hitx][hity] != direction ||
12979                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12980
12981     object_hit = TRUE;
12982
12983     if (object_hit)
12984     {
12985       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12986                                CE_HITTING_X, touched_side);
12987
12988       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12989                                CE_HIT_BY_X, hitting_side);
12990
12991       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12992                                CE_HIT_BY_SOMETHING, opposite_direction);
12993
12994       if (IS_PLAYER(hitx, hity))
12995       {
12996         /* use player element that is initially defined in the level playfield,
12997            not the player element that corresponds to the runtime player number
12998            (example: a level that contains EL_PLAYER_3 as the only player would
12999            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13000         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13001
13002         CheckElementChangeBySide(x, y, hitting_element, player_element,
13003                                  CE_HITTING_X, touched_side);
13004       }
13005     }
13006   }
13007
13008   /* "hitting something" is also true when hitting the playfield border */
13009   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13010                            CE_HITTING_SOMETHING, direction);
13011 }
13012
13013 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13014 {
13015   int i, kill_x = -1, kill_y = -1;
13016
13017   int bad_element = -1;
13018   static int test_xy[4][2] =
13019   {
13020     { 0, -1 },
13021     { -1, 0 },
13022     { +1, 0 },
13023     { 0, +1 }
13024   };
13025   static int test_dir[4] =
13026   {
13027     MV_UP,
13028     MV_LEFT,
13029     MV_RIGHT,
13030     MV_DOWN
13031   };
13032
13033   for (i = 0; i < NUM_DIRECTIONS; i++)
13034   {
13035     int test_x, test_y, test_move_dir, test_element;
13036
13037     test_x = good_x + test_xy[i][0];
13038     test_y = good_y + test_xy[i][1];
13039
13040     if (!IN_LEV_FIELD(test_x, test_y))
13041       continue;
13042
13043     test_move_dir =
13044       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13045
13046     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13047
13048     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13049        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13050     */
13051     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13052         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13053     {
13054       kill_x = test_x;
13055       kill_y = test_y;
13056       bad_element = test_element;
13057
13058       break;
13059     }
13060   }
13061
13062   if (kill_x != -1 || kill_y != -1)
13063   {
13064     if (IS_PLAYER(good_x, good_y))
13065     {
13066       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13067
13068       if (player->shield_deadly_time_left > 0 &&
13069           !IS_INDESTRUCTIBLE(bad_element))
13070         Bang(kill_x, kill_y);
13071       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13072         KillPlayer(player);
13073     }
13074     else
13075       Bang(good_x, good_y);
13076   }
13077 }
13078
13079 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13080 {
13081   int i, kill_x = -1, kill_y = -1;
13082   int bad_element = Feld[bad_x][bad_y];
13083   static int test_xy[4][2] =
13084   {
13085     { 0, -1 },
13086     { -1, 0 },
13087     { +1, 0 },
13088     { 0, +1 }
13089   };
13090   static int touch_dir[4] =
13091   {
13092     MV_LEFT | MV_RIGHT,
13093     MV_UP   | MV_DOWN,
13094     MV_UP   | MV_DOWN,
13095     MV_LEFT | MV_RIGHT
13096   };
13097   static int test_dir[4] =
13098   {
13099     MV_UP,
13100     MV_LEFT,
13101     MV_RIGHT,
13102     MV_DOWN
13103   };
13104
13105   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13106     return;
13107
13108   for (i = 0; i < NUM_DIRECTIONS; i++)
13109   {
13110     int test_x, test_y, test_move_dir, test_element;
13111
13112     test_x = bad_x + test_xy[i][0];
13113     test_y = bad_y + test_xy[i][1];
13114
13115     if (!IN_LEV_FIELD(test_x, test_y))
13116       continue;
13117
13118     test_move_dir =
13119       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13120
13121     test_element = Feld[test_x][test_y];
13122
13123     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13124        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13125     */
13126     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13127         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13128     {
13129       /* good thing is player or penguin that does not move away */
13130       if (IS_PLAYER(test_x, test_y))
13131       {
13132         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13133
13134         if (bad_element == EL_ROBOT && player->is_moving)
13135           continue;     /* robot does not kill player if he is moving */
13136
13137         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13138         {
13139           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13140             continue;           /* center and border element do not touch */
13141         }
13142
13143         kill_x = test_x;
13144         kill_y = test_y;
13145
13146         break;
13147       }
13148       else if (test_element == EL_PENGUIN)
13149       {
13150         kill_x = test_x;
13151         kill_y = test_y;
13152
13153         break;
13154       }
13155     }
13156   }
13157
13158   if (kill_x != -1 || kill_y != -1)
13159   {
13160     if (IS_PLAYER(kill_x, kill_y))
13161     {
13162       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13163
13164       if (player->shield_deadly_time_left > 0 &&
13165           !IS_INDESTRUCTIBLE(bad_element))
13166         Bang(bad_x, bad_y);
13167       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13168         KillPlayer(player);
13169     }
13170     else
13171       Bang(kill_x, kill_y);
13172   }
13173 }
13174
13175 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13176 {
13177   int bad_element = Feld[bad_x][bad_y];
13178   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13179   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13180   int test_x = bad_x + dx, test_y = bad_y + dy;
13181   int test_move_dir, test_element;
13182   int kill_x = -1, kill_y = -1;
13183
13184   if (!IN_LEV_FIELD(test_x, test_y))
13185     return;
13186
13187   test_move_dir =
13188     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13189
13190   test_element = Feld[test_x][test_y];
13191
13192   if (test_move_dir != bad_move_dir)
13193   {
13194     /* good thing can be player or penguin that does not move away */
13195     if (IS_PLAYER(test_x, test_y))
13196     {
13197       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13198
13199       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13200          player as being hit when he is moving towards the bad thing, because
13201          the "get hit by" condition would be lost after the player stops) */
13202       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13203         return;         /* player moves away from bad thing */
13204
13205       kill_x = test_x;
13206       kill_y = test_y;
13207     }
13208     else if (test_element == EL_PENGUIN)
13209     {
13210       kill_x = test_x;
13211       kill_y = test_y;
13212     }
13213   }
13214
13215   if (kill_x != -1 || kill_y != -1)
13216   {
13217     if (IS_PLAYER(kill_x, kill_y))
13218     {
13219       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13220
13221       if (player->shield_deadly_time_left > 0 &&
13222           !IS_INDESTRUCTIBLE(bad_element))
13223         Bang(bad_x, bad_y);
13224       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13225         KillPlayer(player);
13226     }
13227     else
13228       Bang(kill_x, kill_y);
13229   }
13230 }
13231
13232 void TestIfPlayerTouchesBadThing(int x, int y)
13233 {
13234   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13235 }
13236
13237 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13238 {
13239   TestIfGoodThingHitsBadThing(x, y, move_dir);
13240 }
13241
13242 void TestIfBadThingTouchesPlayer(int x, int y)
13243 {
13244   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13245 }
13246
13247 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13248 {
13249   TestIfBadThingHitsGoodThing(x, y, move_dir);
13250 }
13251
13252 void TestIfFriendTouchesBadThing(int x, int y)
13253 {
13254   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13255 }
13256
13257 void TestIfBadThingTouchesFriend(int x, int y)
13258 {
13259   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13260 }
13261
13262 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13263 {
13264   int i, kill_x = bad_x, kill_y = bad_y;
13265   static int xy[4][2] =
13266   {
13267     { 0, -1 },
13268     { -1, 0 },
13269     { +1, 0 },
13270     { 0, +1 }
13271   };
13272
13273   for (i = 0; i < NUM_DIRECTIONS; i++)
13274   {
13275     int x, y, element;
13276
13277     x = bad_x + xy[i][0];
13278     y = bad_y + xy[i][1];
13279     if (!IN_LEV_FIELD(x, y))
13280       continue;
13281
13282     element = Feld[x][y];
13283     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13284         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13285     {
13286       kill_x = x;
13287       kill_y = y;
13288       break;
13289     }
13290   }
13291
13292   if (kill_x != bad_x || kill_y != bad_y)
13293     Bang(bad_x, bad_y);
13294 }
13295
13296 void KillPlayer(struct PlayerInfo *player)
13297 {
13298   int jx = player->jx, jy = player->jy;
13299
13300   if (!player->active)
13301     return;
13302
13303 #if 0
13304   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13305          player->killed, player->active, player->reanimated);
13306 #endif
13307
13308   /* the following code was introduced to prevent an infinite loop when calling
13309      -> Bang()
13310      -> CheckTriggeredElementChangeExt()
13311      -> ExecuteCustomElementAction()
13312      -> KillPlayer()
13313      -> (infinitely repeating the above sequence of function calls)
13314      which occurs when killing the player while having a CE with the setting
13315      "kill player X when explosion of <player X>"; the solution using a new
13316      field "player->killed" was chosen for backwards compatibility, although
13317      clever use of the fields "player->active" etc. would probably also work */
13318 #if 1
13319   if (player->killed)
13320     return;
13321 #endif
13322
13323   player->killed = TRUE;
13324
13325   /* remove accessible field at the player's position */
13326   Feld[jx][jy] = EL_EMPTY;
13327
13328   /* deactivate shield (else Bang()/Explode() would not work right) */
13329   player->shield_normal_time_left = 0;
13330   player->shield_deadly_time_left = 0;
13331
13332 #if 0
13333   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13334          player->killed, player->active, player->reanimated);
13335 #endif
13336
13337   Bang(jx, jy);
13338
13339 #if 0
13340   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13341          player->killed, player->active, player->reanimated);
13342 #endif
13343
13344   if (player->reanimated)       /* killed player may have been reanimated */
13345     player->killed = player->reanimated = FALSE;
13346   else
13347     BuryPlayer(player);
13348 }
13349
13350 static void KillPlayerUnlessEnemyProtected(int x, int y)
13351 {
13352   if (!PLAYER_ENEMY_PROTECTED(x, y))
13353     KillPlayer(PLAYERINFO(x, y));
13354 }
13355
13356 static void KillPlayerUnlessExplosionProtected(int x, int y)
13357 {
13358   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13359     KillPlayer(PLAYERINFO(x, y));
13360 }
13361
13362 void BuryPlayer(struct PlayerInfo *player)
13363 {
13364   int jx = player->jx, jy = player->jy;
13365
13366   if (!player->active)
13367     return;
13368
13369   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13370   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13371
13372   player->GameOver = TRUE;
13373   RemovePlayer(player);
13374 }
13375
13376 void RemovePlayer(struct PlayerInfo *player)
13377 {
13378   int jx = player->jx, jy = player->jy;
13379   int i, found = FALSE;
13380
13381   player->present = FALSE;
13382   player->active = FALSE;
13383
13384   if (!ExplodeField[jx][jy])
13385     StorePlayer[jx][jy] = 0;
13386
13387   if (player->is_moving)
13388     TEST_DrawLevelField(player->last_jx, player->last_jy);
13389
13390   for (i = 0; i < MAX_PLAYERS; i++)
13391     if (stored_player[i].active)
13392       found = TRUE;
13393
13394   if (!found)
13395     AllPlayersGone = TRUE;
13396
13397   ExitX = ZX = jx;
13398   ExitY = ZY = jy;
13399 }
13400
13401 static void setFieldForSnapping(int x, int y, int element, int direction)
13402 {
13403   struct ElementInfo *ei = &element_info[element];
13404   int direction_bit = MV_DIR_TO_BIT(direction);
13405   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13406   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13407                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13408
13409   Feld[x][y] = EL_ELEMENT_SNAPPING;
13410   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13411
13412   ResetGfxAnimation(x, y);
13413
13414   GfxElement[x][y] = element;
13415   GfxAction[x][y] = action;
13416   GfxDir[x][y] = direction;
13417   GfxFrame[x][y] = -1;
13418 }
13419
13420 /*
13421   =============================================================================
13422   checkDiagonalPushing()
13423   -----------------------------------------------------------------------------
13424   check if diagonal input device direction results in pushing of object
13425   (by checking if the alternative direction is walkable, diggable, ...)
13426   =============================================================================
13427 */
13428
13429 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13430                                     int x, int y, int real_dx, int real_dy)
13431 {
13432   int jx, jy, dx, dy, xx, yy;
13433
13434   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13435     return TRUE;
13436
13437   /* diagonal direction: check alternative direction */
13438   jx = player->jx;
13439   jy = player->jy;
13440   dx = x - jx;
13441   dy = y - jy;
13442   xx = jx + (dx == 0 ? real_dx : 0);
13443   yy = jy + (dy == 0 ? real_dy : 0);
13444
13445   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13446 }
13447
13448 /*
13449   =============================================================================
13450   DigField()
13451   -----------------------------------------------------------------------------
13452   x, y:                 field next to player (non-diagonal) to try to dig to
13453   real_dx, real_dy:     direction as read from input device (can be diagonal)
13454   =============================================================================
13455 */
13456
13457 static int DigField(struct PlayerInfo *player,
13458                     int oldx, int oldy, int x, int y,
13459                     int real_dx, int real_dy, int mode)
13460 {
13461   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13462   boolean player_was_pushing = player->is_pushing;
13463   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13464   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13465   int jx = oldx, jy = oldy;
13466   int dx = x - jx, dy = y - jy;
13467   int nextx = x + dx, nexty = y + dy;
13468   int move_direction = (dx == -1 ? MV_LEFT  :
13469                         dx == +1 ? MV_RIGHT :
13470                         dy == -1 ? MV_UP    :
13471                         dy == +1 ? MV_DOWN  : MV_NONE);
13472   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13473   int dig_side = MV_DIR_OPPOSITE(move_direction);
13474   int old_element = Feld[jx][jy];
13475   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13476   int collect_count;
13477
13478   if (is_player)                /* function can also be called by EL_PENGUIN */
13479   {
13480     if (player->MovPos == 0)
13481     {
13482       player->is_digging = FALSE;
13483       player->is_collecting = FALSE;
13484     }
13485
13486     if (player->MovPos == 0)    /* last pushing move finished */
13487       player->is_pushing = FALSE;
13488
13489     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13490     {
13491       player->is_switching = FALSE;
13492       player->push_delay = -1;
13493
13494       return MP_NO_ACTION;
13495     }
13496   }
13497
13498   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13499     old_element = Back[jx][jy];
13500
13501   /* in case of element dropped at player position, check background */
13502   else if (Back[jx][jy] != EL_EMPTY &&
13503            game.engine_version >= VERSION_IDENT(2,2,0,0))
13504     old_element = Back[jx][jy];
13505
13506   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13507     return MP_NO_ACTION;        /* field has no opening in this direction */
13508
13509   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13510     return MP_NO_ACTION;        /* field has no opening in this direction */
13511
13512   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13513   {
13514     SplashAcid(x, y);
13515
13516     Feld[jx][jy] = player->artwork_element;
13517     InitMovingField(jx, jy, MV_DOWN);
13518     Store[jx][jy] = EL_ACID;
13519     ContinueMoving(jx, jy);
13520     BuryPlayer(player);
13521
13522     return MP_DONT_RUN_INTO;
13523   }
13524
13525   if (player_can_move && DONT_RUN_INTO(element))
13526   {
13527     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13528
13529     return MP_DONT_RUN_INTO;
13530   }
13531
13532   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13533     return MP_NO_ACTION;
13534
13535   collect_count = element_info[element].collect_count_initial;
13536
13537   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13538     return MP_NO_ACTION;
13539
13540   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13541     player_can_move = player_can_move_or_snap;
13542
13543   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13544       game.engine_version >= VERSION_IDENT(2,2,0,0))
13545   {
13546     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13547                                player->index_bit, dig_side);
13548     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13549                                         player->index_bit, dig_side);
13550
13551     if (element == EL_DC_LANDMINE)
13552       Bang(x, y);
13553
13554     if (Feld[x][y] != element)          /* field changed by snapping */
13555       return MP_ACTION;
13556
13557     return MP_NO_ACTION;
13558   }
13559
13560   if (player->gravity && is_player && !player->is_auto_moving &&
13561       canFallDown(player) && move_direction != MV_DOWN &&
13562       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13563     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13564
13565   if (player_can_move &&
13566       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13567   {
13568     int sound_element = SND_ELEMENT(element);
13569     int sound_action = ACTION_WALKING;
13570
13571     if (IS_RND_GATE(element))
13572     {
13573       if (!player->key[RND_GATE_NR(element)])
13574         return MP_NO_ACTION;
13575     }
13576     else if (IS_RND_GATE_GRAY(element))
13577     {
13578       if (!player->key[RND_GATE_GRAY_NR(element)])
13579         return MP_NO_ACTION;
13580     }
13581     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13582     {
13583       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13584         return MP_NO_ACTION;
13585     }
13586     else if (element == EL_EXIT_OPEN ||
13587              element == EL_EM_EXIT_OPEN ||
13588              element == EL_EM_EXIT_OPENING ||
13589              element == EL_STEEL_EXIT_OPEN ||
13590              element == EL_EM_STEEL_EXIT_OPEN ||
13591              element == EL_EM_STEEL_EXIT_OPENING ||
13592              element == EL_SP_EXIT_OPEN ||
13593              element == EL_SP_EXIT_OPENING)
13594     {
13595       sound_action = ACTION_PASSING;    /* player is passing exit */
13596     }
13597     else if (element == EL_EMPTY)
13598     {
13599       sound_action = ACTION_MOVING;             /* nothing to walk on */
13600     }
13601
13602     /* play sound from background or player, whatever is available */
13603     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13604       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13605     else
13606       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13607   }
13608   else if (player_can_move &&
13609            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13610   {
13611     if (!ACCESS_FROM(element, opposite_direction))
13612       return MP_NO_ACTION;      /* field not accessible from this direction */
13613
13614     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13615       return MP_NO_ACTION;
13616
13617     if (IS_EM_GATE(element))
13618     {
13619       if (!player->key[EM_GATE_NR(element)])
13620         return MP_NO_ACTION;
13621     }
13622     else if (IS_EM_GATE_GRAY(element))
13623     {
13624       if (!player->key[EM_GATE_GRAY_NR(element)])
13625         return MP_NO_ACTION;
13626     }
13627     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13628     {
13629       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13630         return MP_NO_ACTION;
13631     }
13632     else if (IS_EMC_GATE(element))
13633     {
13634       if (!player->key[EMC_GATE_NR(element)])
13635         return MP_NO_ACTION;
13636     }
13637     else if (IS_EMC_GATE_GRAY(element))
13638     {
13639       if (!player->key[EMC_GATE_GRAY_NR(element)])
13640         return MP_NO_ACTION;
13641     }
13642     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13643     {
13644       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13645         return MP_NO_ACTION;
13646     }
13647     else if (element == EL_DC_GATE_WHITE ||
13648              element == EL_DC_GATE_WHITE_GRAY ||
13649              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13650     {
13651       if (player->num_white_keys == 0)
13652         return MP_NO_ACTION;
13653
13654       player->num_white_keys--;
13655     }
13656     else if (IS_SP_PORT(element))
13657     {
13658       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13659           element == EL_SP_GRAVITY_PORT_RIGHT ||
13660           element == EL_SP_GRAVITY_PORT_UP ||
13661           element == EL_SP_GRAVITY_PORT_DOWN)
13662         player->gravity = !player->gravity;
13663       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13664                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13665                element == EL_SP_GRAVITY_ON_PORT_UP ||
13666                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13667         player->gravity = TRUE;
13668       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13669                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13670                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13671                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13672         player->gravity = FALSE;
13673     }
13674
13675     /* automatically move to the next field with double speed */
13676     player->programmed_action = move_direction;
13677
13678     if (player->move_delay_reset_counter == 0)
13679     {
13680       player->move_delay_reset_counter = 2;     /* two double speed steps */
13681
13682       DOUBLE_PLAYER_SPEED(player);
13683     }
13684
13685     PlayLevelSoundAction(x, y, ACTION_PASSING);
13686   }
13687   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13688   {
13689     RemoveField(x, y);
13690
13691     if (mode != DF_SNAP)
13692     {
13693       GfxElement[x][y] = GFX_ELEMENT(element);
13694       player->is_digging = TRUE;
13695     }
13696
13697     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13698
13699     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13700                                         player->index_bit, dig_side);
13701
13702     if (mode == DF_SNAP)
13703     {
13704       if (level.block_snap_field)
13705         setFieldForSnapping(x, y, element, move_direction);
13706       else
13707         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13708
13709       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13710                                           player->index_bit, dig_side);
13711     }
13712   }
13713   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13714   {
13715     RemoveField(x, y);
13716
13717     if (is_player && mode != DF_SNAP)
13718     {
13719       GfxElement[x][y] = element;
13720       player->is_collecting = TRUE;
13721     }
13722
13723     if (element == EL_SPEED_PILL)
13724     {
13725       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13726     }
13727     else if (element == EL_EXTRA_TIME && level.time > 0)
13728     {
13729       TimeLeft += level.extra_time;
13730
13731       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13732
13733       DisplayGameControlValues();
13734     }
13735     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13736     {
13737       player->shield_normal_time_left += level.shield_normal_time;
13738       if (element == EL_SHIELD_DEADLY)
13739         player->shield_deadly_time_left += level.shield_deadly_time;
13740     }
13741     else if (element == EL_DYNAMITE ||
13742              element == EL_EM_DYNAMITE ||
13743              element == EL_SP_DISK_RED)
13744     {
13745       if (player->inventory_size < MAX_INVENTORY_SIZE)
13746         player->inventory_element[player->inventory_size++] = element;
13747
13748       DrawGameDoorValues();
13749     }
13750     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13751     {
13752       player->dynabomb_count++;
13753       player->dynabombs_left++;
13754     }
13755     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13756     {
13757       player->dynabomb_size++;
13758     }
13759     else if (element == EL_DYNABOMB_INCREASE_POWER)
13760     {
13761       player->dynabomb_xl = TRUE;
13762     }
13763     else if (IS_KEY(element))
13764     {
13765       player->key[KEY_NR(element)] = TRUE;
13766
13767       DrawGameDoorValues();
13768     }
13769     else if (element == EL_DC_KEY_WHITE)
13770     {
13771       player->num_white_keys++;
13772
13773       /* display white keys? */
13774       /* DrawGameDoorValues(); */
13775     }
13776     else if (IS_ENVELOPE(element))
13777     {
13778       player->show_envelope = element;
13779     }
13780     else if (element == EL_EMC_LENSES)
13781     {
13782       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13783
13784       RedrawAllInvisibleElementsForLenses();
13785     }
13786     else if (element == EL_EMC_MAGNIFIER)
13787     {
13788       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13789
13790       RedrawAllInvisibleElementsForMagnifier();
13791     }
13792     else if (IS_DROPPABLE(element) ||
13793              IS_THROWABLE(element))     /* can be collected and dropped */
13794     {
13795       int i;
13796
13797       if (collect_count == 0)
13798         player->inventory_infinite_element = element;
13799       else
13800         for (i = 0; i < collect_count; i++)
13801           if (player->inventory_size < MAX_INVENTORY_SIZE)
13802             player->inventory_element[player->inventory_size++] = element;
13803
13804       DrawGameDoorValues();
13805     }
13806     else if (collect_count > 0)
13807     {
13808       local_player->gems_still_needed -= collect_count;
13809       if (local_player->gems_still_needed < 0)
13810         local_player->gems_still_needed = 0;
13811
13812       game.snapshot.collected_item = TRUE;
13813
13814       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13815
13816       DisplayGameControlValues();
13817     }
13818
13819     RaiseScoreElement(element);
13820     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13821
13822     if (is_player)
13823       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13824                                           player->index_bit, dig_side);
13825
13826     if (mode == DF_SNAP)
13827     {
13828       if (level.block_snap_field)
13829         setFieldForSnapping(x, y, element, move_direction);
13830       else
13831         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13832
13833       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13834                                           player->index_bit, dig_side);
13835     }
13836   }
13837   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13838   {
13839     if (mode == DF_SNAP && element != EL_BD_ROCK)
13840       return MP_NO_ACTION;
13841
13842     if (CAN_FALL(element) && dy)
13843       return MP_NO_ACTION;
13844
13845     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13846         !(element == EL_SPRING && level.use_spring_bug))
13847       return MP_NO_ACTION;
13848
13849     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13850         ((move_direction & MV_VERTICAL &&
13851           ((element_info[element].move_pattern & MV_LEFT &&
13852             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13853            (element_info[element].move_pattern & MV_RIGHT &&
13854             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13855          (move_direction & MV_HORIZONTAL &&
13856           ((element_info[element].move_pattern & MV_UP &&
13857             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13858            (element_info[element].move_pattern & MV_DOWN &&
13859             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13860       return MP_NO_ACTION;
13861
13862     /* do not push elements already moving away faster than player */
13863     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13864         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13865       return MP_NO_ACTION;
13866
13867     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13868     {
13869       if (player->push_delay_value == -1 || !player_was_pushing)
13870         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13871     }
13872     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13873     {
13874       if (player->push_delay_value == -1)
13875         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13876     }
13877     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13878     {
13879       if (!player->is_pushing)
13880         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13881     }
13882
13883     player->is_pushing = TRUE;
13884     player->is_active = TRUE;
13885
13886     if (!(IN_LEV_FIELD(nextx, nexty) &&
13887           (IS_FREE(nextx, nexty) ||
13888            (IS_SB_ELEMENT(element) &&
13889             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13890            (IS_CUSTOM_ELEMENT(element) &&
13891             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13892       return MP_NO_ACTION;
13893
13894     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13895       return MP_NO_ACTION;
13896
13897     if (player->push_delay == -1)       /* new pushing; restart delay */
13898       player->push_delay = 0;
13899
13900     if (player->push_delay < player->push_delay_value &&
13901         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13902         element != EL_SPRING && element != EL_BALLOON)
13903     {
13904       /* make sure that there is no move delay before next try to push */
13905       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13906         player->move_delay = 0;
13907
13908       return MP_NO_ACTION;
13909     }
13910
13911     if (IS_CUSTOM_ELEMENT(element) &&
13912         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13913     {
13914       if (!DigFieldByCE(nextx, nexty, element))
13915         return MP_NO_ACTION;
13916     }
13917
13918     if (IS_SB_ELEMENT(element))
13919     {
13920       if (element == EL_SOKOBAN_FIELD_FULL)
13921       {
13922         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13923         local_player->sokobanfields_still_needed++;
13924       }
13925
13926       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13927       {
13928         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13929         local_player->sokobanfields_still_needed--;
13930       }
13931
13932       Feld[x][y] = EL_SOKOBAN_OBJECT;
13933
13934       if (Back[x][y] == Back[nextx][nexty])
13935         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13936       else if (Back[x][y] != 0)
13937         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13938                                     ACTION_EMPTYING);
13939       else
13940         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13941                                     ACTION_FILLING);
13942
13943       if (local_player->sokobanfields_still_needed == 0 &&
13944           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13945       {
13946         PlayerWins(player);
13947
13948         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13949       }
13950     }
13951     else
13952       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13953
13954     InitMovingField(x, y, move_direction);
13955     GfxAction[x][y] = ACTION_PUSHING;
13956
13957     if (mode == DF_SNAP)
13958       ContinueMoving(x, y);
13959     else
13960       MovPos[x][y] = (dx != 0 ? dx : dy);
13961
13962     Pushed[x][y] = TRUE;
13963     Pushed[nextx][nexty] = TRUE;
13964
13965     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13966       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13967     else
13968       player->push_delay_value = -1;    /* get new value later */
13969
13970     /* check for element change _after_ element has been pushed */
13971     if (game.use_change_when_pushing_bug)
13972     {
13973       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13974                                  player->index_bit, dig_side);
13975       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13976                                           player->index_bit, dig_side);
13977     }
13978   }
13979   else if (IS_SWITCHABLE(element))
13980   {
13981     if (PLAYER_SWITCHING(player, x, y))
13982     {
13983       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13984                                           player->index_bit, dig_side);
13985
13986       return MP_ACTION;
13987     }
13988
13989     player->is_switching = TRUE;
13990     player->switch_x = x;
13991     player->switch_y = y;
13992
13993     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13994
13995     if (element == EL_ROBOT_WHEEL)
13996     {
13997       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13998       ZX = x;
13999       ZY = y;
14000
14001       game.robot_wheel_active = TRUE;
14002
14003       TEST_DrawLevelField(x, y);
14004     }
14005     else if (element == EL_SP_TERMINAL)
14006     {
14007       int xx, yy;
14008
14009       SCAN_PLAYFIELD(xx, yy)
14010       {
14011         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14012         {
14013           Bang(xx, yy);
14014         }
14015         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14016         {
14017           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14018
14019           ResetGfxAnimation(xx, yy);
14020           TEST_DrawLevelField(xx, yy);
14021         }
14022       }
14023     }
14024     else if (IS_BELT_SWITCH(element))
14025     {
14026       ToggleBeltSwitch(x, y);
14027     }
14028     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14029              element == EL_SWITCHGATE_SWITCH_DOWN ||
14030              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14031              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14032     {
14033       ToggleSwitchgateSwitch(x, y);
14034     }
14035     else if (element == EL_LIGHT_SWITCH ||
14036              element == EL_LIGHT_SWITCH_ACTIVE)
14037     {
14038       ToggleLightSwitch(x, y);
14039     }
14040     else if (element == EL_TIMEGATE_SWITCH ||
14041              element == EL_DC_TIMEGATE_SWITCH)
14042     {
14043       ActivateTimegateSwitch(x, y);
14044     }
14045     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14046              element == EL_BALLOON_SWITCH_RIGHT ||
14047              element == EL_BALLOON_SWITCH_UP    ||
14048              element == EL_BALLOON_SWITCH_DOWN  ||
14049              element == EL_BALLOON_SWITCH_NONE  ||
14050              element == EL_BALLOON_SWITCH_ANY)
14051     {
14052       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14053                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14054                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14055                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14056                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14057                              move_direction);
14058     }
14059     else if (element == EL_LAMP)
14060     {
14061       Feld[x][y] = EL_LAMP_ACTIVE;
14062       local_player->lights_still_needed--;
14063
14064       ResetGfxAnimation(x, y);
14065       TEST_DrawLevelField(x, y);
14066     }
14067     else if (element == EL_TIME_ORB_FULL)
14068     {
14069       Feld[x][y] = EL_TIME_ORB_EMPTY;
14070
14071       if (level.time > 0 || level.use_time_orb_bug)
14072       {
14073         TimeLeft += level.time_orb_time;
14074         game.no_time_limit = FALSE;
14075
14076         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14077
14078         DisplayGameControlValues();
14079       }
14080
14081       ResetGfxAnimation(x, y);
14082       TEST_DrawLevelField(x, y);
14083     }
14084     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14085              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14086     {
14087       int xx, yy;
14088
14089       game.ball_state = !game.ball_state;
14090
14091       SCAN_PLAYFIELD(xx, yy)
14092       {
14093         int e = Feld[xx][yy];
14094
14095         if (game.ball_state)
14096         {
14097           if (e == EL_EMC_MAGIC_BALL)
14098             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14099           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14100             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14101         }
14102         else
14103         {
14104           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14105             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14106           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14107             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14108         }
14109       }
14110     }
14111
14112     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14113                                         player->index_bit, dig_side);
14114
14115     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14116                                         player->index_bit, dig_side);
14117
14118     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14119                                         player->index_bit, dig_side);
14120
14121     return MP_ACTION;
14122   }
14123   else
14124   {
14125     if (!PLAYER_SWITCHING(player, x, y))
14126     {
14127       player->is_switching = TRUE;
14128       player->switch_x = x;
14129       player->switch_y = y;
14130
14131       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14132                                  player->index_bit, dig_side);
14133       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14134                                           player->index_bit, dig_side);
14135
14136       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14137                                  player->index_bit, dig_side);
14138       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14139                                           player->index_bit, dig_side);
14140     }
14141
14142     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14143                                player->index_bit, dig_side);
14144     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14145                                         player->index_bit, dig_side);
14146
14147     return MP_NO_ACTION;
14148   }
14149
14150   player->push_delay = -1;
14151
14152   if (is_player)                /* function can also be called by EL_PENGUIN */
14153   {
14154     if (Feld[x][y] != element)          /* really digged/collected something */
14155     {
14156       player->is_collecting = !player->is_digging;
14157       player->is_active = TRUE;
14158     }
14159   }
14160
14161   return MP_MOVING;
14162 }
14163
14164 static boolean DigFieldByCE(int x, int y, int digging_element)
14165 {
14166   int element = Feld[x][y];
14167
14168   if (!IS_FREE(x, y))
14169   {
14170     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14171                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14172                   ACTION_BREAKING);
14173
14174     /* no element can dig solid indestructible elements */
14175     if (IS_INDESTRUCTIBLE(element) &&
14176         !IS_DIGGABLE(element) &&
14177         !IS_COLLECTIBLE(element))
14178       return FALSE;
14179
14180     if (AmoebaNr[x][y] &&
14181         (element == EL_AMOEBA_FULL ||
14182          element == EL_BD_AMOEBA ||
14183          element == EL_AMOEBA_GROWING))
14184     {
14185       AmoebaCnt[AmoebaNr[x][y]]--;
14186       AmoebaCnt2[AmoebaNr[x][y]]--;
14187     }
14188
14189     if (IS_MOVING(x, y))
14190       RemoveMovingField(x, y);
14191     else
14192     {
14193       RemoveField(x, y);
14194       TEST_DrawLevelField(x, y);
14195     }
14196
14197     /* if digged element was about to explode, prevent the explosion */
14198     ExplodeField[x][y] = EX_TYPE_NONE;
14199
14200     PlayLevelSoundAction(x, y, action);
14201   }
14202
14203   Store[x][y] = EL_EMPTY;
14204
14205   /* this makes it possible to leave the removed element again */
14206   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14207     Store[x][y] = element;
14208
14209   return TRUE;
14210 }
14211
14212 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14213 {
14214   int jx = player->jx, jy = player->jy;
14215   int x = jx + dx, y = jy + dy;
14216   int snap_direction = (dx == -1 ? MV_LEFT  :
14217                         dx == +1 ? MV_RIGHT :
14218                         dy == -1 ? MV_UP    :
14219                         dy == +1 ? MV_DOWN  : MV_NONE);
14220   boolean can_continue_snapping = (level.continuous_snapping &&
14221                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14222
14223   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14224     return FALSE;
14225
14226   if (!player->active || !IN_LEV_FIELD(x, y))
14227     return FALSE;
14228
14229   if (dx && dy)
14230     return FALSE;
14231
14232   if (!dx && !dy)
14233   {
14234     if (player->MovPos == 0)
14235       player->is_pushing = FALSE;
14236
14237     player->is_snapping = FALSE;
14238
14239     if (player->MovPos == 0)
14240     {
14241       player->is_moving = FALSE;
14242       player->is_digging = FALSE;
14243       player->is_collecting = FALSE;
14244     }
14245
14246     return FALSE;
14247   }
14248
14249   /* prevent snapping with already pressed snap key when not allowed */
14250   if (player->is_snapping && !can_continue_snapping)
14251     return FALSE;
14252
14253   player->MovDir = snap_direction;
14254
14255   if (player->MovPos == 0)
14256   {
14257     player->is_moving = FALSE;
14258     player->is_digging = FALSE;
14259     player->is_collecting = FALSE;
14260   }
14261
14262   player->is_dropping = FALSE;
14263   player->is_dropping_pressed = FALSE;
14264   player->drop_pressed_delay = 0;
14265
14266   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14267     return FALSE;
14268
14269   player->is_snapping = TRUE;
14270   player->is_active = TRUE;
14271
14272   if (player->MovPos == 0)
14273   {
14274     player->is_moving = FALSE;
14275     player->is_digging = FALSE;
14276     player->is_collecting = FALSE;
14277   }
14278
14279   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14280     TEST_DrawLevelField(player->last_jx, player->last_jy);
14281
14282   TEST_DrawLevelField(x, y);
14283
14284   return TRUE;
14285 }
14286
14287 static boolean DropElement(struct PlayerInfo *player)
14288 {
14289   int old_element, new_element;
14290   int dropx = player->jx, dropy = player->jy;
14291   int drop_direction = player->MovDir;
14292   int drop_side = drop_direction;
14293   int drop_element = get_next_dropped_element(player);
14294
14295   /* do not drop an element on top of another element; when holding drop key
14296      pressed without moving, dropped element must move away before the next
14297      element can be dropped (this is especially important if the next element
14298      is dynamite, which can be placed on background for historical reasons) */
14299   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14300     return MP_ACTION;
14301
14302   if (IS_THROWABLE(drop_element))
14303   {
14304     dropx += GET_DX_FROM_DIR(drop_direction);
14305     dropy += GET_DY_FROM_DIR(drop_direction);
14306
14307     if (!IN_LEV_FIELD(dropx, dropy))
14308       return FALSE;
14309   }
14310
14311   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14312   new_element = drop_element;           /* default: no change when dropping */
14313
14314   /* check if player is active, not moving and ready to drop */
14315   if (!player->active || player->MovPos || player->drop_delay > 0)
14316     return FALSE;
14317
14318   /* check if player has anything that can be dropped */
14319   if (new_element == EL_UNDEFINED)
14320     return FALSE;
14321
14322   /* only set if player has anything that can be dropped */
14323   player->is_dropping_pressed = TRUE;
14324
14325   /* check if drop key was pressed long enough for EM style dynamite */
14326   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14327     return FALSE;
14328
14329   /* check if anything can be dropped at the current position */
14330   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14331     return FALSE;
14332
14333   /* collected custom elements can only be dropped on empty fields */
14334   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14335     return FALSE;
14336
14337   if (old_element != EL_EMPTY)
14338     Back[dropx][dropy] = old_element;   /* store old element on this field */
14339
14340   ResetGfxAnimation(dropx, dropy);
14341   ResetRandomAnimationValue(dropx, dropy);
14342
14343   if (player->inventory_size > 0 ||
14344       player->inventory_infinite_element != EL_UNDEFINED)
14345   {
14346     if (player->inventory_size > 0)
14347     {
14348       player->inventory_size--;
14349
14350       DrawGameDoorValues();
14351
14352       if (new_element == EL_DYNAMITE)
14353         new_element = EL_DYNAMITE_ACTIVE;
14354       else if (new_element == EL_EM_DYNAMITE)
14355         new_element = EL_EM_DYNAMITE_ACTIVE;
14356       else if (new_element == EL_SP_DISK_RED)
14357         new_element = EL_SP_DISK_RED_ACTIVE;
14358     }
14359
14360     Feld[dropx][dropy] = new_element;
14361
14362     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14363       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14364                           el2img(Feld[dropx][dropy]), 0);
14365
14366     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14367
14368     /* needed if previous element just changed to "empty" in the last frame */
14369     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14370
14371     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14372                                player->index_bit, drop_side);
14373     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14374                                         CE_PLAYER_DROPS_X,
14375                                         player->index_bit, drop_side);
14376
14377     TestIfElementTouchesCustomElement(dropx, dropy);
14378   }
14379   else          /* player is dropping a dyna bomb */
14380   {
14381     player->dynabombs_left--;
14382
14383     Feld[dropx][dropy] = new_element;
14384
14385     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14386       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14387                           el2img(Feld[dropx][dropy]), 0);
14388
14389     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14390   }
14391
14392   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14393     InitField_WithBug1(dropx, dropy, FALSE);
14394
14395   new_element = Feld[dropx][dropy];     /* element might have changed */
14396
14397   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14398       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14399   {
14400     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14401       MovDir[dropx][dropy] = drop_direction;
14402
14403     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14404
14405     /* do not cause impact style collision by dropping elements that can fall */
14406     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14407   }
14408
14409   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14410   player->is_dropping = TRUE;
14411
14412   player->drop_pressed_delay = 0;
14413   player->is_dropping_pressed = FALSE;
14414
14415   player->drop_x = dropx;
14416   player->drop_y = dropy;
14417
14418   return TRUE;
14419 }
14420
14421 /* ------------------------------------------------------------------------- */
14422 /* game sound playing functions                                              */
14423 /* ------------------------------------------------------------------------- */
14424
14425 static int *loop_sound_frame = NULL;
14426 static int *loop_sound_volume = NULL;
14427
14428 void InitPlayLevelSound()
14429 {
14430   int num_sounds = getSoundListSize();
14431
14432   checked_free(loop_sound_frame);
14433   checked_free(loop_sound_volume);
14434
14435   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14436   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14437 }
14438
14439 static void PlayLevelSound(int x, int y, int nr)
14440 {
14441   int sx = SCREENX(x), sy = SCREENY(y);
14442   int volume, stereo_position;
14443   int max_distance = 8;
14444   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14445
14446   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14447       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14448     return;
14449
14450   if (!IN_LEV_FIELD(x, y) ||
14451       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14452       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14453     return;
14454
14455   volume = SOUND_MAX_VOLUME;
14456
14457   if (!IN_SCR_FIELD(sx, sy))
14458   {
14459     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14460     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14461
14462     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14463   }
14464
14465   stereo_position = (SOUND_MAX_LEFT +
14466                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14467                      (SCR_FIELDX + 2 * max_distance));
14468
14469   if (IS_LOOP_SOUND(nr))
14470   {
14471     /* This assures that quieter loop sounds do not overwrite louder ones,
14472        while restarting sound volume comparison with each new game frame. */
14473
14474     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14475       return;
14476
14477     loop_sound_volume[nr] = volume;
14478     loop_sound_frame[nr] = FrameCounter;
14479   }
14480
14481   PlaySoundExt(nr, volume, stereo_position, type);
14482 }
14483
14484 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14485 {
14486   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14487                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14488                  y < LEVELY(BY1) ? LEVELY(BY1) :
14489                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14490                  sound_action);
14491 }
14492
14493 static void PlayLevelSoundAction(int x, int y, int action)
14494 {
14495   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14496 }
14497
14498 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14499 {
14500   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14501
14502   if (sound_effect != SND_UNDEFINED)
14503     PlayLevelSound(x, y, sound_effect);
14504 }
14505
14506 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14507                                               int action)
14508 {
14509   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14510
14511   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14512     PlayLevelSound(x, y, sound_effect);
14513 }
14514
14515 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14516 {
14517   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14518
14519   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14520     PlayLevelSound(x, y, sound_effect);
14521 }
14522
14523 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14524 {
14525   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14526
14527   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14528     StopSound(sound_effect);
14529 }
14530
14531 static int getLevelMusicNr()
14532 {
14533   if (levelset.music[level_nr] != MUS_UNDEFINED)
14534     return levelset.music[level_nr];            /* from config file */
14535   else
14536     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14537 }
14538
14539 static void FadeLevelSounds()
14540 {
14541   FadeSounds();
14542 }
14543
14544 static void FadeLevelMusic()
14545 {
14546   int music_nr = getLevelMusicNr();
14547   char *curr_music = getCurrentlyPlayingMusicFilename();
14548   char *next_music = getMusicInfoEntryFilename(music_nr);
14549
14550   if (!strEqual(curr_music, next_music))
14551     FadeMusic();
14552 }
14553
14554 void FadeLevelSoundsAndMusic()
14555 {
14556   FadeLevelSounds();
14557   FadeLevelMusic();
14558 }
14559
14560 static void PlayLevelMusic()
14561 {
14562   int music_nr = getLevelMusicNr();
14563   char *curr_music = getCurrentlyPlayingMusicFilename();
14564   char *next_music = getMusicInfoEntryFilename(music_nr);
14565
14566   if (!strEqual(curr_music, next_music))
14567     PlayMusic(music_nr);
14568 }
14569
14570 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14571 {
14572   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14573   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14574   int x = xx - 1 - offset;
14575   int y = yy - 1 - offset;
14576
14577   switch (sample)
14578   {
14579     case SAMPLE_blank:
14580       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14581       break;
14582
14583     case SAMPLE_roll:
14584       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14585       break;
14586
14587     case SAMPLE_stone:
14588       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14589       break;
14590
14591     case SAMPLE_nut:
14592       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14593       break;
14594
14595     case SAMPLE_crack:
14596       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14597       break;
14598
14599     case SAMPLE_bug:
14600       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14601       break;
14602
14603     case SAMPLE_tank:
14604       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14605       break;
14606
14607     case SAMPLE_android_clone:
14608       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14609       break;
14610
14611     case SAMPLE_android_move:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14613       break;
14614
14615     case SAMPLE_spring:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14617       break;
14618
14619     case SAMPLE_slurp:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14621       break;
14622
14623     case SAMPLE_eater:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14625       break;
14626
14627     case SAMPLE_eater_eat:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14629       break;
14630
14631     case SAMPLE_alien:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14633       break;
14634
14635     case SAMPLE_collect:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14637       break;
14638
14639     case SAMPLE_diamond:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14641       break;
14642
14643     case SAMPLE_squash:
14644       /* !!! CHECK THIS !!! */
14645 #if 1
14646       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14647 #else
14648       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14649 #endif
14650       break;
14651
14652     case SAMPLE_wonderfall:
14653       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14654       break;
14655
14656     case SAMPLE_drip:
14657       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14658       break;
14659
14660     case SAMPLE_push:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14662       break;
14663
14664     case SAMPLE_dirt:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14666       break;
14667
14668     case SAMPLE_acid:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14670       break;
14671
14672     case SAMPLE_ball:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14674       break;
14675
14676     case SAMPLE_grow:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14678       break;
14679
14680     case SAMPLE_wonder:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14682       break;
14683
14684     case SAMPLE_door:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14686       break;
14687
14688     case SAMPLE_exit_open:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14690       break;
14691
14692     case SAMPLE_exit_leave:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14694       break;
14695
14696     case SAMPLE_dynamite:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14698       break;
14699
14700     case SAMPLE_tick:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14702       break;
14703
14704     case SAMPLE_press:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14706       break;
14707
14708     case SAMPLE_wheel:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14710       break;
14711
14712     case SAMPLE_boom:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14714       break;
14715
14716     case SAMPLE_die:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14718       break;
14719
14720     case SAMPLE_time:
14721       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14722       break;
14723
14724     default:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14726       break;
14727   }
14728 }
14729
14730 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14731 {
14732   int element = map_element_SP_to_RND(element_sp);
14733   int action = map_action_SP_to_RND(action_sp);
14734   int offset = (setup.sp_show_border_elements ? 0 : 1);
14735   int x = xx - offset;
14736   int y = yy - offset;
14737
14738   PlayLevelSoundElementAction(x, y, element, action);
14739 }
14740
14741 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14742 {
14743   int element = map_element_MM_to_RND(element_mm);
14744   int action = map_action_MM_to_RND(action_mm);
14745   int offset = 0;
14746   int x = xx - offset;
14747   int y = yy - offset;
14748
14749   if (!IS_MM_ELEMENT(element))
14750     element = EL_MM_DEFAULT;
14751
14752   PlayLevelSoundElementAction(x, y, element, action);
14753 }
14754
14755 void PlaySound_MM(int sound_mm)
14756 {
14757   int sound = map_sound_MM_to_RND(sound_mm);
14758
14759   if (sound == SND_UNDEFINED)
14760     return;
14761
14762   PlaySound(sound);
14763 }
14764
14765 void PlaySoundLoop_MM(int sound_mm)
14766 {
14767   int sound = map_sound_MM_to_RND(sound_mm);
14768
14769   if (sound == SND_UNDEFINED)
14770     return;
14771
14772   PlaySoundLoop(sound);
14773 }
14774
14775 void StopSound_MM(int sound_mm)
14776 {
14777   int sound = map_sound_MM_to_RND(sound_mm);
14778
14779   if (sound == SND_UNDEFINED)
14780     return;
14781
14782   StopSound(sound);
14783 }
14784
14785 void RaiseScore(int value)
14786 {
14787   local_player->score += value;
14788
14789   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14790
14791   DisplayGameControlValues();
14792 }
14793
14794 void RaiseScoreElement(int element)
14795 {
14796   switch (element)
14797   {
14798     case EL_EMERALD:
14799     case EL_BD_DIAMOND:
14800     case EL_EMERALD_YELLOW:
14801     case EL_EMERALD_RED:
14802     case EL_EMERALD_PURPLE:
14803     case EL_SP_INFOTRON:
14804       RaiseScore(level.score[SC_EMERALD]);
14805       break;
14806     case EL_DIAMOND:
14807       RaiseScore(level.score[SC_DIAMOND]);
14808       break;
14809     case EL_CRYSTAL:
14810       RaiseScore(level.score[SC_CRYSTAL]);
14811       break;
14812     case EL_PEARL:
14813       RaiseScore(level.score[SC_PEARL]);
14814       break;
14815     case EL_BUG:
14816     case EL_BD_BUTTERFLY:
14817     case EL_SP_ELECTRON:
14818       RaiseScore(level.score[SC_BUG]);
14819       break;
14820     case EL_SPACESHIP:
14821     case EL_BD_FIREFLY:
14822     case EL_SP_SNIKSNAK:
14823       RaiseScore(level.score[SC_SPACESHIP]);
14824       break;
14825     case EL_YAMYAM:
14826     case EL_DARK_YAMYAM:
14827       RaiseScore(level.score[SC_YAMYAM]);
14828       break;
14829     case EL_ROBOT:
14830       RaiseScore(level.score[SC_ROBOT]);
14831       break;
14832     case EL_PACMAN:
14833       RaiseScore(level.score[SC_PACMAN]);
14834       break;
14835     case EL_NUT:
14836       RaiseScore(level.score[SC_NUT]);
14837       break;
14838     case EL_DYNAMITE:
14839     case EL_EM_DYNAMITE:
14840     case EL_SP_DISK_RED:
14841     case EL_DYNABOMB_INCREASE_NUMBER:
14842     case EL_DYNABOMB_INCREASE_SIZE:
14843     case EL_DYNABOMB_INCREASE_POWER:
14844       RaiseScore(level.score[SC_DYNAMITE]);
14845       break;
14846     case EL_SHIELD_NORMAL:
14847     case EL_SHIELD_DEADLY:
14848       RaiseScore(level.score[SC_SHIELD]);
14849       break;
14850     case EL_EXTRA_TIME:
14851       RaiseScore(level.extra_time_score);
14852       break;
14853     case EL_KEY_1:
14854     case EL_KEY_2:
14855     case EL_KEY_3:
14856     case EL_KEY_4:
14857     case EL_EM_KEY_1:
14858     case EL_EM_KEY_2:
14859     case EL_EM_KEY_3:
14860     case EL_EM_KEY_4:
14861     case EL_EMC_KEY_5:
14862     case EL_EMC_KEY_6:
14863     case EL_EMC_KEY_7:
14864     case EL_EMC_KEY_8:
14865     case EL_DC_KEY_WHITE:
14866       RaiseScore(level.score[SC_KEY]);
14867       break;
14868     default:
14869       RaiseScore(element_info[element].collect_score);
14870       break;
14871   }
14872 }
14873
14874 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14875 {
14876   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14877   {
14878     /* closing door required in case of envelope style request dialogs */
14879     if (!skip_request)
14880       CloseDoor(DOOR_CLOSE_1);
14881
14882     if (network.enabled)
14883       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14884     else
14885     {
14886       if (quick_quit)
14887         FadeSkipNextFadeIn();
14888
14889       SetGameStatus(GAME_MODE_MAIN);
14890
14891       DrawMainMenu();
14892     }
14893   }
14894   else          /* continue playing the game */
14895   {
14896     if (tape.playing && tape.deactivate_display)
14897       TapeDeactivateDisplayOff(TRUE);
14898
14899     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14900
14901     if (tape.playing && tape.deactivate_display)
14902       TapeDeactivateDisplayOn();
14903   }
14904 }
14905
14906 void RequestQuitGame(boolean ask_if_really_quit)
14907 {
14908   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14909   boolean skip_request = AllPlayersGone || quick_quit;
14910
14911   RequestQuitGameExt(skip_request, quick_quit,
14912                      "Do you really want to quit the game?");
14913 }
14914
14915 void RequestRestartGame(char *message)
14916 {
14917   game.restart_game_message = NULL;
14918
14919   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14920   {
14921     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14922   }
14923   else
14924   {
14925     SetGameStatus(GAME_MODE_MAIN);
14926
14927     DrawMainMenu();
14928   }
14929 }
14930
14931
14932 /* ------------------------------------------------------------------------- */
14933 /* random generator functions                                                */
14934 /* ------------------------------------------------------------------------- */
14935
14936 unsigned int InitEngineRandom_RND(int seed)
14937 {
14938   game.num_random_calls = 0;
14939
14940   return InitEngineRandom(seed);
14941 }
14942
14943 unsigned int RND(int max)
14944 {
14945   if (max > 0)
14946   {
14947     game.num_random_calls++;
14948
14949     return GetEngineRandom(max);
14950   }
14951
14952   return 0;
14953 }
14954
14955
14956 /* ------------------------------------------------------------------------- */
14957 /* game engine snapshot handling functions                                   */
14958 /* ------------------------------------------------------------------------- */
14959
14960 struct EngineSnapshotInfo
14961 {
14962   /* runtime values for custom element collect score */
14963   int collect_score[NUM_CUSTOM_ELEMENTS];
14964
14965   /* runtime values for group element choice position */
14966   int choice_pos[NUM_GROUP_ELEMENTS];
14967
14968   /* runtime values for belt position animations */
14969   int belt_graphic[4][NUM_BELT_PARTS];
14970   int belt_anim_mode[4][NUM_BELT_PARTS];
14971 };
14972
14973 static struct EngineSnapshotInfo engine_snapshot_rnd;
14974 static char *snapshot_level_identifier = NULL;
14975 static int snapshot_level_nr = -1;
14976
14977 static void SaveEngineSnapshotValues_RND()
14978 {
14979   static int belt_base_active_element[4] =
14980   {
14981     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14982     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14983     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14984     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14985   };
14986   int i, j;
14987
14988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14989   {
14990     int element = EL_CUSTOM_START + i;
14991
14992     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14993   }
14994
14995   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14996   {
14997     int element = EL_GROUP_START + i;
14998
14999     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15000   }
15001
15002   for (i = 0; i < 4; i++)
15003   {
15004     for (j = 0; j < NUM_BELT_PARTS; j++)
15005     {
15006       int element = belt_base_active_element[i] + j;
15007       int graphic = el2img(element);
15008       int anim_mode = graphic_info[graphic].anim_mode;
15009
15010       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15011       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15012     }
15013   }
15014 }
15015
15016 static void LoadEngineSnapshotValues_RND()
15017 {
15018   unsigned int num_random_calls = game.num_random_calls;
15019   int i, j;
15020
15021   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15022   {
15023     int element = EL_CUSTOM_START + i;
15024
15025     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15026   }
15027
15028   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15029   {
15030     int element = EL_GROUP_START + i;
15031
15032     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15033   }
15034
15035   for (i = 0; i < 4; i++)
15036   {
15037     for (j = 0; j < NUM_BELT_PARTS; j++)
15038     {
15039       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15040       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15041
15042       graphic_info[graphic].anim_mode = anim_mode;
15043     }
15044   }
15045
15046   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15047   {
15048     InitRND(tape.random_seed);
15049     for (i = 0; i < num_random_calls; i++)
15050       RND(1);
15051   }
15052
15053   if (game.num_random_calls != num_random_calls)
15054   {
15055     Error(ERR_INFO, "number of random calls out of sync");
15056     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15057     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15058     Error(ERR_EXIT, "this should not happen -- please debug");
15059   }
15060 }
15061
15062 void FreeEngineSnapshotSingle()
15063 {
15064   FreeSnapshotSingle();
15065
15066   setString(&snapshot_level_identifier, NULL);
15067   snapshot_level_nr = -1;
15068 }
15069
15070 void FreeEngineSnapshotList()
15071 {
15072   FreeSnapshotList();
15073 }
15074
15075 ListNode *SaveEngineSnapshotBuffers()
15076 {
15077   ListNode *buffers = NULL;
15078
15079   /* copy some special values to a structure better suited for the snapshot */
15080
15081   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15082     SaveEngineSnapshotValues_RND();
15083   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15084     SaveEngineSnapshotValues_EM();
15085   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15086     SaveEngineSnapshotValues_SP(&buffers);
15087   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15088     SaveEngineSnapshotValues_MM(&buffers);
15089
15090   /* save values stored in special snapshot structure */
15091
15092   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15093     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15094   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15095     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15096   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15097     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15098   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15099     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15100
15101   /* save further RND engine values */
15102
15103   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15104   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15105   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15106
15107   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15108   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15109   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15110   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15111
15112   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15113   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15114   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15115   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15116   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15117
15118   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15120   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15121
15122   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15123
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15125
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15127   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15128
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15131   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15147
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15150
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15157
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15163
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15166
15167 #if 0
15168   ListNode *node = engine_snapshot_list_rnd;
15169   int num_bytes = 0;
15170
15171   while (node != NULL)
15172   {
15173     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15174
15175     node = node->next;
15176   }
15177
15178   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15179 #endif
15180
15181   return buffers;
15182 }
15183
15184 void SaveEngineSnapshotSingle()
15185 {
15186   ListNode *buffers = SaveEngineSnapshotBuffers();
15187
15188   /* finally save all snapshot buffers to single snapshot */
15189   SaveSnapshotSingle(buffers);
15190
15191   /* save level identification information */
15192   setString(&snapshot_level_identifier, leveldir_current->identifier);
15193   snapshot_level_nr = level_nr;
15194 }
15195
15196 boolean CheckSaveEngineSnapshotToList()
15197 {
15198   boolean save_snapshot =
15199     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15200      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15201       game.snapshot.changed_action) ||
15202      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15203       game.snapshot.collected_item));
15204
15205   game.snapshot.changed_action = FALSE;
15206   game.snapshot.collected_item = FALSE;
15207   game.snapshot.save_snapshot = save_snapshot;
15208
15209   return save_snapshot;
15210 }
15211
15212 void SaveEngineSnapshotToList()
15213 {
15214   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15215       tape.quick_resume)
15216     return;
15217
15218   ListNode *buffers = SaveEngineSnapshotBuffers();
15219
15220   /* finally save all snapshot buffers to snapshot list */
15221   SaveSnapshotToList(buffers);
15222 }
15223
15224 void SaveEngineSnapshotToListInitial()
15225 {
15226   FreeEngineSnapshotList();
15227
15228   SaveEngineSnapshotToList();
15229 }
15230
15231 void LoadEngineSnapshotValues()
15232 {
15233   /* restore special values from snapshot structure */
15234
15235   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15236     LoadEngineSnapshotValues_RND();
15237   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15238     LoadEngineSnapshotValues_EM();
15239   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15240     LoadEngineSnapshotValues_SP();
15241   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15242     LoadEngineSnapshotValues_MM();
15243 }
15244
15245 void LoadEngineSnapshotSingle()
15246 {
15247   LoadSnapshotSingle();
15248
15249   LoadEngineSnapshotValues();
15250 }
15251
15252 void LoadEngineSnapshot_Undo(int steps)
15253 {
15254   LoadSnapshotFromList_Older(steps);
15255
15256   LoadEngineSnapshotValues();
15257 }
15258
15259 void LoadEngineSnapshot_Redo(int steps)
15260 {
15261   LoadSnapshotFromList_Newer(steps);
15262
15263   LoadEngineSnapshotValues();
15264 }
15265
15266 boolean CheckEngineSnapshotSingle()
15267 {
15268   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15269           snapshot_level_nr == level_nr);
15270 }
15271
15272 boolean CheckEngineSnapshotList()
15273 {
15274   return CheckSnapshotList();
15275 }
15276
15277
15278 /* ---------- new game button stuff ---------------------------------------- */
15279
15280 static struct
15281 {
15282   int graphic;
15283   struct XY *pos;
15284   int gadget_id;
15285   boolean *setup_value;
15286   boolean allowed_on_tape;
15287   char *infotext;
15288 } gamebutton_info[NUM_GAME_BUTTONS] =
15289 {
15290   {
15291     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15292     GAME_CTRL_ID_STOP,                          NULL,
15293     TRUE,                                       "stop game"
15294   },
15295   {
15296     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15297     GAME_CTRL_ID_PAUSE,                         NULL,
15298     TRUE,                                       "pause game"
15299   },
15300   {
15301     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15302     GAME_CTRL_ID_PLAY,                          NULL,
15303     TRUE,                                       "play game"
15304   },
15305   {
15306     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15307     GAME_CTRL_ID_UNDO,                          NULL,
15308     TRUE,                                       "undo step"
15309   },
15310   {
15311     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15312     GAME_CTRL_ID_REDO,                          NULL,
15313     TRUE,                                       "redo step"
15314   },
15315   {
15316     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15317     GAME_CTRL_ID_SAVE,                          NULL,
15318     TRUE,                                       "save game"
15319   },
15320   {
15321     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15322     GAME_CTRL_ID_PAUSE2,                        NULL,
15323     TRUE,                                       "pause game"
15324   },
15325   {
15326     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15327     GAME_CTRL_ID_LOAD,                          NULL,
15328     TRUE,                                       "load game"
15329   },
15330   {
15331     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15332     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15333     FALSE,                                      "stop game"
15334   },
15335   {
15336     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15337     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15338     FALSE,                                      "pause game"
15339   },
15340   {
15341     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15342     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15343     FALSE,                                      "play game"
15344   },
15345   {
15346     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15347     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15348     TRUE,                                       "background music on/off"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15352     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15353     TRUE,                                       "sound loops on/off"
15354   },
15355   {
15356     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15357     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15358     TRUE,                                       "normal sounds on/off"
15359   },
15360   {
15361     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15362     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15363     FALSE,                                      "background music on/off"
15364   },
15365   {
15366     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15367     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15368     FALSE,                                      "sound loops on/off"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15372     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15373     FALSE,                                      "normal sounds on/off"
15374   }
15375 };
15376
15377 void CreateGameButtons()
15378 {
15379   int i;
15380
15381   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15382   {
15383     int graphic = gamebutton_info[i].graphic;
15384     struct GraphicInfo *gfx = &graphic_info[graphic];
15385     struct XY *pos = gamebutton_info[i].pos;
15386     struct GadgetInfo *gi;
15387     int button_type;
15388     boolean checked;
15389     unsigned int event_mask;
15390     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15391     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15392     int base_x = (on_tape ? VX : DX);
15393     int base_y = (on_tape ? VY : DY);
15394     int gd_x   = gfx->src_x;
15395     int gd_y   = gfx->src_y;
15396     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15397     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15398     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15399     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15400     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15401     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15402     int id = i;
15403
15404     if (gfx->bitmap == NULL)
15405     {
15406       game_gadget[id] = NULL;
15407
15408       continue;
15409     }
15410
15411     if (id == GAME_CTRL_ID_STOP ||
15412         id == GAME_CTRL_ID_PANEL_STOP ||
15413         id == GAME_CTRL_ID_PLAY ||
15414         id == GAME_CTRL_ID_PANEL_PLAY ||
15415         id == GAME_CTRL_ID_SAVE ||
15416         id == GAME_CTRL_ID_LOAD)
15417     {
15418       button_type = GD_TYPE_NORMAL_BUTTON;
15419       checked = FALSE;
15420       event_mask = GD_EVENT_RELEASED;
15421     }
15422     else if (id == GAME_CTRL_ID_UNDO ||
15423              id == GAME_CTRL_ID_REDO)
15424     {
15425       button_type = GD_TYPE_NORMAL_BUTTON;
15426       checked = FALSE;
15427       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15428     }
15429     else
15430     {
15431       button_type = GD_TYPE_CHECK_BUTTON;
15432       checked = (gamebutton_info[i].setup_value != NULL ?
15433                  *gamebutton_info[i].setup_value : FALSE);
15434       event_mask = GD_EVENT_PRESSED;
15435     }
15436
15437     gi = CreateGadget(GDI_CUSTOM_ID, id,
15438                       GDI_IMAGE_ID, graphic,
15439                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15440                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15441                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15442                       GDI_WIDTH, gfx->width,
15443                       GDI_HEIGHT, gfx->height,
15444                       GDI_TYPE, button_type,
15445                       GDI_STATE, GD_BUTTON_UNPRESSED,
15446                       GDI_CHECKED, checked,
15447                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15448                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15449                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15450                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15451                       GDI_DIRECT_DRAW, FALSE,
15452                       GDI_EVENT_MASK, event_mask,
15453                       GDI_CALLBACK_ACTION, HandleGameButtons,
15454                       GDI_END);
15455
15456     if (gi == NULL)
15457       Error(ERR_EXIT, "cannot create gadget");
15458
15459     game_gadget[id] = gi;
15460   }
15461 }
15462
15463 void FreeGameButtons()
15464 {
15465   int i;
15466
15467   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15468     FreeGadget(game_gadget[i]);
15469 }
15470
15471 static void UnmapGameButtonsAtSamePosition(int id)
15472 {
15473   int i;
15474
15475   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15476     if (i != id &&
15477         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15478         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15479       UnmapGadget(game_gadget[i]);
15480 }
15481
15482 static void UnmapGameButtonsAtSamePosition_All()
15483 {
15484   if (setup.show_snapshot_buttons)
15485   {
15486     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15487     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15488     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15489   }
15490   else
15491   {
15492     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15493     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15494     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15495
15496     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15497     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15498     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15499   }
15500 }
15501
15502 static void MapGameButtonsAtSamePosition(int id)
15503 {
15504   int i;
15505
15506   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15507     if (i != id &&
15508         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15509         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15510       MapGadget(game_gadget[i]);
15511
15512   UnmapGameButtonsAtSamePosition_All();
15513 }
15514
15515 void MapUndoRedoButtons()
15516 {
15517   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15518   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15519
15520   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15521   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15522
15523   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15524 }
15525
15526 void UnmapUndoRedoButtons()
15527 {
15528   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15529   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15530
15531   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15532   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15533
15534   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15535 }
15536
15537 void MapGameButtonsExt(boolean on_tape)
15538 {
15539   int i;
15540
15541   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15542     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15543         i != GAME_CTRL_ID_UNDO &&
15544         i != GAME_CTRL_ID_REDO)
15545       MapGadget(game_gadget[i]);
15546
15547   UnmapGameButtonsAtSamePosition_All();
15548
15549   RedrawGameButtons();
15550 }
15551
15552 void UnmapGameButtonsExt(boolean on_tape)
15553 {
15554   int i;
15555
15556   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15557     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15558       UnmapGadget(game_gadget[i]);
15559 }
15560
15561 void RedrawGameButtonsExt(boolean on_tape)
15562 {
15563   int i;
15564
15565   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15566     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15567       RedrawGadget(game_gadget[i]);
15568
15569   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15570   redraw_mask &= ~REDRAW_ALL;
15571 }
15572
15573 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15574 {
15575   if (gi == NULL)
15576     return;
15577
15578   gi->checked = state;
15579 }
15580
15581 void RedrawSoundButtonGadget(int id)
15582 {
15583   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15584              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15585              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15586              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15587              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15588              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15589              id);
15590
15591   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15592   RedrawGadget(game_gadget[id2]);
15593 }
15594
15595 void MapGameButtons()
15596 {
15597   MapGameButtonsExt(FALSE);
15598 }
15599
15600 void UnmapGameButtons()
15601 {
15602   UnmapGameButtonsExt(FALSE);
15603 }
15604
15605 void RedrawGameButtons()
15606 {
15607   RedrawGameButtonsExt(FALSE);
15608 }
15609
15610 void MapGameButtonsOnTape()
15611 {
15612   MapGameButtonsExt(TRUE);
15613 }
15614
15615 void UnmapGameButtonsOnTape()
15616 {
15617   UnmapGameButtonsExt(TRUE);
15618 }
15619
15620 void RedrawGameButtonsOnTape()
15621 {
15622   RedrawGameButtonsExt(TRUE);
15623 }
15624
15625 void GameUndoRedoExt()
15626 {
15627   ClearPlayerAction();
15628
15629   tape.pausing = TRUE;
15630
15631   RedrawPlayfield();
15632   UpdateAndDisplayGameControlValues();
15633
15634   DrawCompleteVideoDisplay();
15635   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15636   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15637   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15638
15639   BackToFront();
15640 }
15641
15642 void GameUndo(int steps)
15643 {
15644   if (!CheckEngineSnapshotList())
15645     return;
15646
15647   LoadEngineSnapshot_Undo(steps);
15648
15649   GameUndoRedoExt();
15650 }
15651
15652 void GameRedo(int steps)
15653 {
15654   if (!CheckEngineSnapshotList())
15655     return;
15656
15657   LoadEngineSnapshot_Redo(steps);
15658
15659   GameUndoRedoExt();
15660 }
15661
15662 static void HandleGameButtonsExt(int id, int button)
15663 {
15664   static boolean game_undo_executed = FALSE;
15665   int steps = BUTTON_STEPSIZE(button);
15666   boolean handle_game_buttons =
15667     (game_status == GAME_MODE_PLAYING ||
15668      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15669
15670   if (!handle_game_buttons)
15671     return;
15672
15673   switch (id)
15674   {
15675     case GAME_CTRL_ID_STOP:
15676     case GAME_CTRL_ID_PANEL_STOP:
15677       if (game_status == GAME_MODE_MAIN)
15678         break;
15679
15680       if (tape.playing)
15681         TapeStop();
15682       else
15683         RequestQuitGame(TRUE);
15684
15685       break;
15686
15687     case GAME_CTRL_ID_PAUSE:
15688     case GAME_CTRL_ID_PAUSE2:
15689     case GAME_CTRL_ID_PANEL_PAUSE:
15690       if (network.enabled && game_status == GAME_MODE_PLAYING)
15691       {
15692         if (tape.pausing)
15693           SendToServer_ContinuePlaying();
15694         else
15695           SendToServer_PausePlaying();
15696       }
15697       else
15698         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15699
15700       game_undo_executed = FALSE;
15701
15702       break;
15703
15704     case GAME_CTRL_ID_PLAY:
15705     case GAME_CTRL_ID_PANEL_PLAY:
15706       if (game_status == GAME_MODE_MAIN)
15707       {
15708         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15709       }
15710       else if (tape.pausing)
15711       {
15712         if (network.enabled)
15713           SendToServer_ContinuePlaying();
15714         else
15715           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15716       }
15717       break;
15718
15719     case GAME_CTRL_ID_UNDO:
15720       // Important: When using "save snapshot when collecting an item" mode,
15721       // load last (current) snapshot for first "undo" after pressing "pause"
15722       // (else the last-but-one snapshot would be loaded, because the snapshot
15723       // pointer already points to the last snapshot when pressing "pause",
15724       // which is fine for "every step/move" mode, but not for "every collect")
15725       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15726           !game_undo_executed)
15727         steps--;
15728
15729       game_undo_executed = TRUE;
15730
15731       GameUndo(steps);
15732       break;
15733
15734     case GAME_CTRL_ID_REDO:
15735       GameRedo(steps);
15736       break;
15737
15738     case GAME_CTRL_ID_SAVE:
15739       TapeQuickSave();
15740       break;
15741
15742     case GAME_CTRL_ID_LOAD:
15743       TapeQuickLoad();
15744       break;
15745
15746     case SOUND_CTRL_ID_MUSIC:
15747     case SOUND_CTRL_ID_PANEL_MUSIC:
15748       if (setup.sound_music)
15749       { 
15750         setup.sound_music = FALSE;
15751
15752         FadeMusic();
15753       }
15754       else if (audio.music_available)
15755       { 
15756         setup.sound = setup.sound_music = TRUE;
15757
15758         SetAudioMode(setup.sound);
15759
15760         if (game_status == GAME_MODE_PLAYING)
15761           PlayLevelMusic();
15762       }
15763
15764       RedrawSoundButtonGadget(id);
15765
15766       break;
15767
15768     case SOUND_CTRL_ID_LOOPS:
15769     case SOUND_CTRL_ID_PANEL_LOOPS:
15770       if (setup.sound_loops)
15771         setup.sound_loops = FALSE;
15772       else if (audio.loops_available)
15773       {
15774         setup.sound = setup.sound_loops = TRUE;
15775
15776         SetAudioMode(setup.sound);
15777       }
15778
15779       RedrawSoundButtonGadget(id);
15780
15781       break;
15782
15783     case SOUND_CTRL_ID_SIMPLE:
15784     case SOUND_CTRL_ID_PANEL_SIMPLE:
15785       if (setup.sound_simple)
15786         setup.sound_simple = FALSE;
15787       else if (audio.sound_available)
15788       {
15789         setup.sound = setup.sound_simple = TRUE;
15790
15791         SetAudioMode(setup.sound);
15792       }
15793
15794       RedrawSoundButtonGadget(id);
15795
15796       break;
15797
15798     default:
15799       break;
15800   }
15801 }
15802
15803 static void HandleGameButtons(struct GadgetInfo *gi)
15804 {
15805   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15806 }
15807
15808 void HandleSoundButtonKeys(Key key)
15809 {
15810   if (key == setup.shortcut.sound_simple)
15811     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15812   else if (key == setup.shortcut.sound_loops)
15813     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15814   else if (key == setup.shortcut.sound_music)
15815     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15816 }