2cf0e9aa46a468e4eacf98bf528d34964359d322
[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   int last_level_nr = level_nr;
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   {
4697     level_nr++;         /* advance to next level */
4698     TapeErase();        /* start with empty tape */
4699
4700     if (setup.auto_play_next_level)
4701     {
4702       LoadLevel(level_nr);
4703
4704       SaveLevelSetup_SeriesInfo();
4705     }
4706   }
4707
4708   hi_pos = NewHiScore(last_level_nr);
4709
4710   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4711   {
4712     SetGameStatus(GAME_MODE_SCORES);
4713
4714     DrawHallOfFame(last_level_nr, hi_pos);
4715   }
4716   else if (!setup.auto_play_next_level || !setup.increment_levels)
4717   {
4718     SetGameStatus(GAME_MODE_MAIN);
4719
4720     DrawMainMenu();
4721   }
4722   else
4723   {
4724     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4725   }
4726 }
4727
4728 int NewHiScore(int level_nr)
4729 {
4730   int k, l;
4731   int position = -1;
4732   boolean one_score_entry_per_name = !program.many_scores_per_name;
4733
4734   LoadScore(level_nr);
4735
4736   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4737       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4738     return -1;
4739
4740   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4741   {
4742     if (local_player->score_final > highscore[k].Score)
4743     {
4744       /* player has made it to the hall of fame */
4745
4746       if (k < MAX_SCORE_ENTRIES - 1)
4747       {
4748         int m = MAX_SCORE_ENTRIES - 1;
4749
4750         if (one_score_entry_per_name)
4751         {
4752           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4753             if (strEqual(setup.player_name, highscore[l].Name))
4754               m = l;
4755
4756           if (m == k)   /* player's new highscore overwrites his old one */
4757             goto put_into_list;
4758         }
4759
4760         for (l = m; l > k; l--)
4761         {
4762           strcpy(highscore[l].Name, highscore[l - 1].Name);
4763           highscore[l].Score = highscore[l - 1].Score;
4764         }
4765       }
4766
4767       put_into_list:
4768
4769       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4770       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4771       highscore[k].Score = local_player->score_final; 
4772       position = k;
4773
4774       break;
4775     }
4776     else if (one_score_entry_per_name &&
4777              !strncmp(setup.player_name, highscore[k].Name,
4778                       MAX_PLAYER_NAME_LEN))
4779       break;    /* player already there with a higher score */
4780   }
4781
4782   if (position >= 0) 
4783     SaveScore(level_nr);
4784
4785   return position;
4786 }
4787
4788 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4789 {
4790   int element = Feld[x][y];
4791   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4792   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4793   int horiz_move = (dx != 0);
4794   int sign = (horiz_move ? dx : dy);
4795   int step = sign * element_info[element].move_stepsize;
4796
4797   /* special values for move stepsize for spring and things on conveyor belt */
4798   if (horiz_move)
4799   {
4800     if (CAN_FALL(element) &&
4801         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4802       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4803     else if (element == EL_SPRING)
4804       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4805   }
4806
4807   return step;
4808 }
4809
4810 inline static int getElementMoveStepsize(int x, int y)
4811 {
4812   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4813 }
4814
4815 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4816 {
4817   if (player->GfxAction != action || player->GfxDir != dir)
4818   {
4819     player->GfxAction = action;
4820     player->GfxDir = dir;
4821     player->Frame = 0;
4822     player->StepFrame = 0;
4823   }
4824 }
4825
4826 static void ResetGfxFrame(int x, int y)
4827 {
4828   // profiling showed that "autotest" spends 10~20% of its time in this function
4829   if (DrawingDeactivatedField())
4830     return;
4831
4832   int element = Feld[x][y];
4833   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4834
4835   if (graphic_info[graphic].anim_global_sync)
4836     GfxFrame[x][y] = FrameCounter;
4837   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4838     GfxFrame[x][y] = CustomValue[x][y];
4839   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4840     GfxFrame[x][y] = element_info[element].collect_score;
4841   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4842     GfxFrame[x][y] = ChangeDelay[x][y];
4843 }
4844
4845 static void ResetGfxAnimation(int x, int y)
4846 {
4847   GfxAction[x][y] = ACTION_DEFAULT;
4848   GfxDir[x][y] = MovDir[x][y];
4849   GfxFrame[x][y] = 0;
4850
4851   ResetGfxFrame(x, y);
4852 }
4853
4854 static void ResetRandomAnimationValue(int x, int y)
4855 {
4856   GfxRandom[x][y] = INIT_GFX_RANDOM();
4857 }
4858
4859 void InitMovingField(int x, int y, int direction)
4860 {
4861   int element = Feld[x][y];
4862   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4863   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4864   int newx = x + dx;
4865   int newy = y + dy;
4866   boolean is_moving_before, is_moving_after;
4867
4868   /* check if element was/is moving or being moved before/after mode change */
4869   is_moving_before = (WasJustMoving[x][y] != 0);
4870   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4871
4872   /* reset animation only for moving elements which change direction of moving
4873      or which just started or stopped moving
4874      (else CEs with property "can move" / "not moving" are reset each frame) */
4875   if (is_moving_before != is_moving_after ||
4876       direction != MovDir[x][y])
4877     ResetGfxAnimation(x, y);
4878
4879   MovDir[x][y] = direction;
4880   GfxDir[x][y] = direction;
4881
4882   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4883                      direction == MV_DOWN && CAN_FALL(element) ?
4884                      ACTION_FALLING : ACTION_MOVING);
4885
4886   /* this is needed for CEs with property "can move" / "not moving" */
4887
4888   if (is_moving_after)
4889   {
4890     if (Feld[newx][newy] == EL_EMPTY)
4891       Feld[newx][newy] = EL_BLOCKED;
4892
4893     MovDir[newx][newy] = MovDir[x][y];
4894
4895     CustomValue[newx][newy] = CustomValue[x][y];
4896
4897     GfxFrame[newx][newy] = GfxFrame[x][y];
4898     GfxRandom[newx][newy] = GfxRandom[x][y];
4899     GfxAction[newx][newy] = GfxAction[x][y];
4900     GfxDir[newx][newy] = GfxDir[x][y];
4901   }
4902 }
4903
4904 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4905 {
4906   int direction = MovDir[x][y];
4907   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4908   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4909
4910   *goes_to_x = newx;
4911   *goes_to_y = newy;
4912 }
4913
4914 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4915 {
4916   int oldx = x, oldy = y;
4917   int direction = MovDir[x][y];
4918
4919   if (direction == MV_LEFT)
4920     oldx++;
4921   else if (direction == MV_RIGHT)
4922     oldx--;
4923   else if (direction == MV_UP)
4924     oldy++;
4925   else if (direction == MV_DOWN)
4926     oldy--;
4927
4928   *comes_from_x = oldx;
4929   *comes_from_y = oldy;
4930 }
4931
4932 int MovingOrBlocked2Element(int x, int y)
4933 {
4934   int element = Feld[x][y];
4935
4936   if (element == EL_BLOCKED)
4937   {
4938     int oldx, oldy;
4939
4940     Blocked2Moving(x, y, &oldx, &oldy);
4941     return Feld[oldx][oldy];
4942   }
4943   else
4944     return element;
4945 }
4946
4947 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4948 {
4949   /* like MovingOrBlocked2Element(), but if element is moving
4950      and (x,y) is the field the moving element is just leaving,
4951      return EL_BLOCKED instead of the element value */
4952   int element = Feld[x][y];
4953
4954   if (IS_MOVING(x, y))
4955   {
4956     if (element == EL_BLOCKED)
4957     {
4958       int oldx, oldy;
4959
4960       Blocked2Moving(x, y, &oldx, &oldy);
4961       return Feld[oldx][oldy];
4962     }
4963     else
4964       return EL_BLOCKED;
4965   }
4966   else
4967     return element;
4968 }
4969
4970 static void RemoveField(int x, int y)
4971 {
4972   Feld[x][y] = EL_EMPTY;
4973
4974   MovPos[x][y] = 0;
4975   MovDir[x][y] = 0;
4976   MovDelay[x][y] = 0;
4977
4978   CustomValue[x][y] = 0;
4979
4980   AmoebaNr[x][y] = 0;
4981   ChangeDelay[x][y] = 0;
4982   ChangePage[x][y] = -1;
4983   Pushed[x][y] = FALSE;
4984
4985   GfxElement[x][y] = EL_UNDEFINED;
4986   GfxAction[x][y] = ACTION_DEFAULT;
4987   GfxDir[x][y] = MV_NONE;
4988 }
4989
4990 void RemoveMovingField(int x, int y)
4991 {
4992   int oldx = x, oldy = y, newx = x, newy = y;
4993   int element = Feld[x][y];
4994   int next_element = EL_UNDEFINED;
4995
4996   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4997     return;
4998
4999   if (IS_MOVING(x, y))
5000   {
5001     Moving2Blocked(x, y, &newx, &newy);
5002
5003     if (Feld[newx][newy] != EL_BLOCKED)
5004     {
5005       /* element is moving, but target field is not free (blocked), but
5006          already occupied by something different (example: acid pool);
5007          in this case, only remove the moving field, but not the target */
5008
5009       RemoveField(oldx, oldy);
5010
5011       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5012
5013       TEST_DrawLevelField(oldx, oldy);
5014
5015       return;
5016     }
5017   }
5018   else if (element == EL_BLOCKED)
5019   {
5020     Blocked2Moving(x, y, &oldx, &oldy);
5021     if (!IS_MOVING(oldx, oldy))
5022       return;
5023   }
5024
5025   if (element == EL_BLOCKED &&
5026       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5027        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5028        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5029        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5030        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5031        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5032     next_element = get_next_element(Feld[oldx][oldy]);
5033
5034   RemoveField(oldx, oldy);
5035   RemoveField(newx, newy);
5036
5037   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5038
5039   if (next_element != EL_UNDEFINED)
5040     Feld[oldx][oldy] = next_element;
5041
5042   TEST_DrawLevelField(oldx, oldy);
5043   TEST_DrawLevelField(newx, newy);
5044 }
5045
5046 void DrawDynamite(int x, int y)
5047 {
5048   int sx = SCREENX(x), sy = SCREENY(y);
5049   int graphic = el2img(Feld[x][y]);
5050   int frame;
5051
5052   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5053     return;
5054
5055   if (IS_WALKABLE_INSIDE(Back[x][y]))
5056     return;
5057
5058   if (Back[x][y])
5059     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5060   else if (Store[x][y])
5061     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5062
5063   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5064
5065   if (Back[x][y] || Store[x][y])
5066     DrawGraphicThruMask(sx, sy, graphic, frame);
5067   else
5068     DrawGraphic(sx, sy, graphic, frame);
5069 }
5070
5071 void CheckDynamite(int x, int y)
5072 {
5073   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5074   {
5075     MovDelay[x][y]--;
5076
5077     if (MovDelay[x][y] != 0)
5078     {
5079       DrawDynamite(x, y);
5080       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5081
5082       return;
5083     }
5084   }
5085
5086   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5087
5088   Bang(x, y);
5089 }
5090
5091 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5092 {
5093   boolean num_checked_players = 0;
5094   int i;
5095
5096   for (i = 0; i < MAX_PLAYERS; i++)
5097   {
5098     if (stored_player[i].active)
5099     {
5100       int sx = stored_player[i].jx;
5101       int sy = stored_player[i].jy;
5102
5103       if (num_checked_players == 0)
5104       {
5105         *sx1 = *sx2 = sx;
5106         *sy1 = *sy2 = sy;
5107       }
5108       else
5109       {
5110         *sx1 = MIN(*sx1, sx);
5111         *sy1 = MIN(*sy1, sy);
5112         *sx2 = MAX(*sx2, sx);
5113         *sy2 = MAX(*sy2, sy);
5114       }
5115
5116       num_checked_players++;
5117     }
5118   }
5119 }
5120
5121 static boolean checkIfAllPlayersFitToScreen_RND()
5122 {
5123   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5124
5125   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5126
5127   return (sx2 - sx1 < SCR_FIELDX &&
5128           sy2 - sy1 < SCR_FIELDY);
5129 }
5130
5131 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5132 {
5133   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5134
5135   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5136
5137   *sx = (sx1 + sx2) / 2;
5138   *sy = (sy1 + sy2) / 2;
5139 }
5140
5141 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5142                         boolean center_screen, boolean quick_relocation)
5143 {
5144   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5145   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5146   boolean no_delay = (tape.warp_forward);
5147   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5148   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5149   int new_scroll_x, new_scroll_y;
5150
5151   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5152   {
5153     /* case 1: quick relocation inside visible screen (without scrolling) */
5154
5155     RedrawPlayfield();
5156
5157     return;
5158   }
5159
5160   if (!level.shifted_relocation || center_screen)
5161   {
5162     /* relocation _with_ centering of screen */
5163
5164     new_scroll_x = SCROLL_POSITION_X(x);
5165     new_scroll_y = SCROLL_POSITION_Y(y);
5166   }
5167   else
5168   {
5169     /* relocation _without_ centering of screen */
5170
5171     int center_scroll_x = SCROLL_POSITION_X(old_x);
5172     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5173     int offset_x = x + (scroll_x - center_scroll_x);
5174     int offset_y = y + (scroll_y - center_scroll_y);
5175
5176     /* for new screen position, apply previous offset to center position */
5177     new_scroll_x = SCROLL_POSITION_X(offset_x);
5178     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5179   }
5180
5181   if (quick_relocation)
5182   {
5183     /* case 2: quick relocation (redraw without visible scrolling) */
5184
5185     scroll_x = new_scroll_x;
5186     scroll_y = new_scroll_y;
5187
5188     RedrawPlayfield();
5189
5190     return;
5191   }
5192
5193   /* case 3: visible relocation (with scrolling to new position) */
5194
5195   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5196
5197   SetVideoFrameDelay(wait_delay_value);
5198
5199   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5200   {
5201     int dx = 0, dy = 0;
5202     int fx = FX, fy = FY;
5203
5204     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5205     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5206
5207     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5208       break;
5209
5210     scroll_x -= dx;
5211     scroll_y -= dy;
5212
5213     fx += dx * TILEX / 2;
5214     fy += dy * TILEY / 2;
5215
5216     ScrollLevel(dx, dy);
5217     DrawAllPlayers();
5218
5219     /* scroll in two steps of half tile size to make things smoother */
5220     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5221
5222     /* scroll second step to align at full tile size */
5223     BlitScreenToBitmap(window);
5224   }
5225
5226   DrawAllPlayers();
5227   BackToFront();
5228
5229   SetVideoFrameDelay(frame_delay_value_old);
5230 }
5231
5232 void RelocatePlayer(int jx, int jy, int el_player_raw)
5233 {
5234   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5235   int player_nr = GET_PLAYER_NR(el_player);
5236   struct PlayerInfo *player = &stored_player[player_nr];
5237   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5238   boolean no_delay = (tape.warp_forward);
5239   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5240   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5241   int old_jx = player->jx;
5242   int old_jy = player->jy;
5243   int old_element = Feld[old_jx][old_jy];
5244   int element = Feld[jx][jy];
5245   boolean player_relocated = (old_jx != jx || old_jy != jy);
5246
5247   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5248   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5249   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5250   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5251   int leave_side_horiz = move_dir_horiz;
5252   int leave_side_vert  = move_dir_vert;
5253   int enter_side = enter_side_horiz | enter_side_vert;
5254   int leave_side = leave_side_horiz | leave_side_vert;
5255
5256   if (player->GameOver)         /* do not reanimate dead player */
5257     return;
5258
5259   if (!player_relocated)        /* no need to relocate the player */
5260     return;
5261
5262   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5263   {
5264     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5265     DrawLevelField(jx, jy);
5266   }
5267
5268   if (player->present)
5269   {
5270     while (player->MovPos)
5271     {
5272       ScrollPlayer(player, SCROLL_GO_ON);
5273       ScrollScreen(NULL, SCROLL_GO_ON);
5274
5275       AdvanceFrameAndPlayerCounters(player->index_nr);
5276
5277       DrawPlayer(player);
5278
5279       BackToFront_WithFrameDelay(wait_delay_value);
5280     }
5281
5282     DrawPlayer(player);         /* needed here only to cleanup last field */
5283     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5284
5285     player->is_moving = FALSE;
5286   }
5287
5288   if (IS_CUSTOM_ELEMENT(old_element))
5289     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5290                                CE_LEFT_BY_PLAYER,
5291                                player->index_bit, leave_side);
5292
5293   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5294                                       CE_PLAYER_LEAVES_X,
5295                                       player->index_bit, leave_side);
5296
5297   Feld[jx][jy] = el_player;
5298   InitPlayerField(jx, jy, el_player, TRUE);
5299
5300   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5301      possible that the relocation target field did not contain a player element,
5302      but a walkable element, to which the new player was relocated -- in this
5303      case, restore that (already initialized!) element on the player field */
5304   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5305   {
5306     Feld[jx][jy] = element;     /* restore previously existing element */
5307   }
5308
5309   /* only visually relocate centered player */
5310   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5311                      FALSE, level.instant_relocation);
5312
5313   TestIfPlayerTouchesBadThing(jx, jy);
5314   TestIfPlayerTouchesCustomElement(jx, jy);
5315
5316   if (IS_CUSTOM_ELEMENT(element))
5317     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5318                                player->index_bit, enter_side);
5319
5320   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5321                                       player->index_bit, enter_side);
5322
5323   if (player->is_switching)
5324   {
5325     /* ensure that relocation while still switching an element does not cause
5326        a new element to be treated as also switched directly after relocation
5327        (this is important for teleporter switches that teleport the player to
5328        a place where another teleporter switch is in the same direction, which
5329        would then incorrectly be treated as immediately switched before the
5330        direction key that caused the switch was released) */
5331
5332     player->switch_x += jx - old_jx;
5333     player->switch_y += jy - old_jy;
5334   }
5335 }
5336
5337 void Explode(int ex, int ey, int phase, int mode)
5338 {
5339   int x, y;
5340   int last_phase;
5341   int border_element;
5342
5343   /* !!! eliminate this variable !!! */
5344   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5345
5346   if (game.explosions_delayed)
5347   {
5348     ExplodeField[ex][ey] = mode;
5349     return;
5350   }
5351
5352   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5353   {
5354     int center_element = Feld[ex][ey];
5355     int artwork_element, explosion_element;     /* set these values later */
5356
5357     /* remove things displayed in background while burning dynamite */
5358     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5359       Back[ex][ey] = 0;
5360
5361     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5362     {
5363       /* put moving element to center field (and let it explode there) */
5364       center_element = MovingOrBlocked2Element(ex, ey);
5365       RemoveMovingField(ex, ey);
5366       Feld[ex][ey] = center_element;
5367     }
5368
5369     /* now "center_element" is finally determined -- set related values now */
5370     artwork_element = center_element;           /* for custom player artwork */
5371     explosion_element = center_element;         /* for custom player artwork */
5372
5373     if (IS_PLAYER(ex, ey))
5374     {
5375       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5376
5377       artwork_element = stored_player[player_nr].artwork_element;
5378
5379       if (level.use_explosion_element[player_nr])
5380       {
5381         explosion_element = level.explosion_element[player_nr];
5382         artwork_element = explosion_element;
5383       }
5384     }
5385
5386     if (mode == EX_TYPE_NORMAL ||
5387         mode == EX_TYPE_CENTER ||
5388         mode == EX_TYPE_CROSS)
5389       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5390
5391     last_phase = element_info[explosion_element].explosion_delay + 1;
5392
5393     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5394     {
5395       int xx = x - ex + 1;
5396       int yy = y - ey + 1;
5397       int element;
5398
5399       if (!IN_LEV_FIELD(x, y) ||
5400           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5401           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5402         continue;
5403
5404       element = Feld[x][y];
5405
5406       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5407       {
5408         element = MovingOrBlocked2Element(x, y);
5409
5410         if (!IS_EXPLOSION_PROOF(element))
5411           RemoveMovingField(x, y);
5412       }
5413
5414       /* indestructible elements can only explode in center (but not flames) */
5415       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5416                                            mode == EX_TYPE_BORDER)) ||
5417           element == EL_FLAMES)
5418         continue;
5419
5420       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5421          behaviour, for example when touching a yamyam that explodes to rocks
5422          with active deadly shield, a rock is created under the player !!! */
5423       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5424 #if 0
5425       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5426           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5427            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5428 #else
5429       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5430 #endif
5431       {
5432         if (IS_ACTIVE_BOMB(element))
5433         {
5434           /* re-activate things under the bomb like gate or penguin */
5435           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5436           Back[x][y] = 0;
5437         }
5438
5439         continue;
5440       }
5441
5442       /* save walkable background elements while explosion on same tile */
5443       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5444           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5445         Back[x][y] = element;
5446
5447       /* ignite explodable elements reached by other explosion */
5448       if (element == EL_EXPLOSION)
5449         element = Store2[x][y];
5450
5451       if (AmoebaNr[x][y] &&
5452           (element == EL_AMOEBA_FULL ||
5453            element == EL_BD_AMOEBA ||
5454            element == EL_AMOEBA_GROWING))
5455       {
5456         AmoebaCnt[AmoebaNr[x][y]]--;
5457         AmoebaCnt2[AmoebaNr[x][y]]--;
5458       }
5459
5460       RemoveField(x, y);
5461
5462       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5463       {
5464         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5465
5466         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5467
5468         if (PLAYERINFO(ex, ey)->use_murphy)
5469           Store[x][y] = EL_EMPTY;
5470       }
5471
5472       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5473          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5474       else if (ELEM_IS_PLAYER(center_element))
5475         Store[x][y] = EL_EMPTY;
5476       else if (center_element == EL_YAMYAM)
5477         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5478       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5479         Store[x][y] = element_info[center_element].content.e[xx][yy];
5480 #if 1
5481       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5482          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5483          otherwise) -- FIX THIS !!! */
5484       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5485         Store[x][y] = element_info[element].content.e[1][1];
5486 #else
5487       else if (!CAN_EXPLODE(element))
5488         Store[x][y] = element_info[element].content.e[1][1];
5489 #endif
5490       else
5491         Store[x][y] = EL_EMPTY;
5492
5493       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5494           center_element == EL_AMOEBA_TO_DIAMOND)
5495         Store2[x][y] = element;
5496
5497       Feld[x][y] = EL_EXPLOSION;
5498       GfxElement[x][y] = artwork_element;
5499
5500       ExplodePhase[x][y] = 1;
5501       ExplodeDelay[x][y] = last_phase;
5502
5503       Stop[x][y] = TRUE;
5504     }
5505
5506     if (center_element == EL_YAMYAM)
5507       game.yamyam_content_nr =
5508         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5509
5510     return;
5511   }
5512
5513   if (Stop[ex][ey])
5514     return;
5515
5516   x = ex;
5517   y = ey;
5518
5519   if (phase == 1)
5520     GfxFrame[x][y] = 0;         /* restart explosion animation */
5521
5522   last_phase = ExplodeDelay[x][y];
5523
5524   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5525
5526   /* this can happen if the player leaves an explosion just in time */
5527   if (GfxElement[x][y] == EL_UNDEFINED)
5528     GfxElement[x][y] = EL_EMPTY;
5529
5530   border_element = Store2[x][y];
5531   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5532     border_element = StorePlayer[x][y];
5533
5534   if (phase == element_info[border_element].ignition_delay ||
5535       phase == last_phase)
5536   {
5537     boolean border_explosion = FALSE;
5538
5539     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5540         !PLAYER_EXPLOSION_PROTECTED(x, y))
5541     {
5542       KillPlayerUnlessExplosionProtected(x, y);
5543       border_explosion = TRUE;
5544     }
5545     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5546     {
5547       Feld[x][y] = Store2[x][y];
5548       Store2[x][y] = 0;
5549       Bang(x, y);
5550       border_explosion = TRUE;
5551     }
5552     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5553     {
5554       AmoebeUmwandeln(x, y);
5555       Store2[x][y] = 0;
5556       border_explosion = TRUE;
5557     }
5558
5559     /* if an element just explodes due to another explosion (chain-reaction),
5560        do not immediately end the new explosion when it was the last frame of
5561        the explosion (as it would be done in the following "if"-statement!) */
5562     if (border_explosion && phase == last_phase)
5563       return;
5564   }
5565
5566   if (phase == last_phase)
5567   {
5568     int element;
5569
5570     element = Feld[x][y] = Store[x][y];
5571     Store[x][y] = Store2[x][y] = 0;
5572     GfxElement[x][y] = EL_UNDEFINED;
5573
5574     /* player can escape from explosions and might therefore be still alive */
5575     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5576         element <= EL_PLAYER_IS_EXPLODING_4)
5577     {
5578       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5579       int explosion_element = EL_PLAYER_1 + player_nr;
5580       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5581       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5582
5583       if (level.use_explosion_element[player_nr])
5584         explosion_element = level.explosion_element[player_nr];
5585
5586       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5587                     element_info[explosion_element].content.e[xx][yy]);
5588     }
5589
5590     /* restore probably existing indestructible background element */
5591     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5592       element = Feld[x][y] = Back[x][y];
5593     Back[x][y] = 0;
5594
5595     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5596     GfxDir[x][y] = MV_NONE;
5597     ChangeDelay[x][y] = 0;
5598     ChangePage[x][y] = -1;
5599
5600     CustomValue[x][y] = 0;
5601
5602     InitField_WithBug2(x, y, FALSE);
5603
5604     TEST_DrawLevelField(x, y);
5605
5606     TestIfElementTouchesCustomElement(x, y);
5607
5608     if (GFX_CRUMBLED(element))
5609       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5610
5611     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5612       StorePlayer[x][y] = 0;
5613
5614     if (ELEM_IS_PLAYER(element))
5615       RelocatePlayer(x, y, element);
5616   }
5617   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5618   {
5619     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5620     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5621
5622     if (phase == delay)
5623       TEST_DrawLevelFieldCrumbled(x, y);
5624
5625     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5626     {
5627       DrawLevelElement(x, y, Back[x][y]);
5628       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5629     }
5630     else if (IS_WALKABLE_UNDER(Back[x][y]))
5631     {
5632       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5633       DrawLevelElementThruMask(x, y, Back[x][y]);
5634     }
5635     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5636       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5637   }
5638 }
5639
5640 void DynaExplode(int ex, int ey)
5641 {
5642   int i, j;
5643   int dynabomb_element = Feld[ex][ey];
5644   int dynabomb_size = 1;
5645   boolean dynabomb_xl = FALSE;
5646   struct PlayerInfo *player;
5647   static int xy[4][2] =
5648   {
5649     { 0, -1 },
5650     { -1, 0 },
5651     { +1, 0 },
5652     { 0, +1 }
5653   };
5654
5655   if (IS_ACTIVE_BOMB(dynabomb_element))
5656   {
5657     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5658     dynabomb_size = player->dynabomb_size;
5659     dynabomb_xl = player->dynabomb_xl;
5660     player->dynabombs_left++;
5661   }
5662
5663   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5664
5665   for (i = 0; i < NUM_DIRECTIONS; i++)
5666   {
5667     for (j = 1; j <= dynabomb_size; j++)
5668     {
5669       int x = ex + j * xy[i][0];
5670       int y = ey + j * xy[i][1];
5671       int element;
5672
5673       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5674         break;
5675
5676       element = Feld[x][y];
5677
5678       /* do not restart explosions of fields with active bombs */
5679       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5680         continue;
5681
5682       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5683
5684       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5685           !IS_DIGGABLE(element) && !dynabomb_xl)
5686         break;
5687     }
5688   }
5689 }
5690
5691 void Bang(int x, int y)
5692 {
5693   int element = MovingOrBlocked2Element(x, y);
5694   int explosion_type = EX_TYPE_NORMAL;
5695
5696   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5697   {
5698     struct PlayerInfo *player = PLAYERINFO(x, y);
5699
5700     element = Feld[x][y] = player->initial_element;
5701
5702     if (level.use_explosion_element[player->index_nr])
5703     {
5704       int explosion_element = level.explosion_element[player->index_nr];
5705
5706       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5707         explosion_type = EX_TYPE_CROSS;
5708       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5709         explosion_type = EX_TYPE_CENTER;
5710     }
5711   }
5712
5713   switch (element)
5714   {
5715     case EL_BUG:
5716     case EL_SPACESHIP:
5717     case EL_BD_BUTTERFLY:
5718     case EL_BD_FIREFLY:
5719     case EL_YAMYAM:
5720     case EL_DARK_YAMYAM:
5721     case EL_ROBOT:
5722     case EL_PACMAN:
5723     case EL_MOLE:
5724       RaiseScoreElement(element);
5725       break;
5726
5727     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5728     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5729     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5730     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5731     case EL_DYNABOMB_INCREASE_NUMBER:
5732     case EL_DYNABOMB_INCREASE_SIZE:
5733     case EL_DYNABOMB_INCREASE_POWER:
5734       explosion_type = EX_TYPE_DYNA;
5735       break;
5736
5737     case EL_DC_LANDMINE:
5738       explosion_type = EX_TYPE_CENTER;
5739       break;
5740
5741     case EL_PENGUIN:
5742     case EL_LAMP:
5743     case EL_LAMP_ACTIVE:
5744     case EL_AMOEBA_TO_DIAMOND:
5745       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5746         explosion_type = EX_TYPE_CENTER;
5747       break;
5748
5749     default:
5750       if (element_info[element].explosion_type == EXPLODES_CROSS)
5751         explosion_type = EX_TYPE_CROSS;
5752       else if (element_info[element].explosion_type == EXPLODES_1X1)
5753         explosion_type = EX_TYPE_CENTER;
5754       break;
5755   }
5756
5757   if (explosion_type == EX_TYPE_DYNA)
5758     DynaExplode(x, y);
5759   else
5760     Explode(x, y, EX_PHASE_START, explosion_type);
5761
5762   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5763 }
5764
5765 void SplashAcid(int x, int y)
5766 {
5767   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5768       (!IN_LEV_FIELD(x - 1, y - 2) ||
5769        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5770     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5771
5772   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5773       (!IN_LEV_FIELD(x + 1, y - 2) ||
5774        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5775     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5776
5777   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5778 }
5779
5780 static void InitBeltMovement()
5781 {
5782   static int belt_base_element[4] =
5783   {
5784     EL_CONVEYOR_BELT_1_LEFT,
5785     EL_CONVEYOR_BELT_2_LEFT,
5786     EL_CONVEYOR_BELT_3_LEFT,
5787     EL_CONVEYOR_BELT_4_LEFT
5788   };
5789   static int belt_base_active_element[4] =
5790   {
5791     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5792     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5793     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5794     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5795   };
5796
5797   int x, y, i, j;
5798
5799   /* set frame order for belt animation graphic according to belt direction */
5800   for (i = 0; i < NUM_BELTS; i++)
5801   {
5802     int belt_nr = i;
5803
5804     for (j = 0; j < NUM_BELT_PARTS; j++)
5805     {
5806       int element = belt_base_active_element[belt_nr] + j;
5807       int graphic_1 = el2img(element);
5808       int graphic_2 = el2panelimg(element);
5809
5810       if (game.belt_dir[i] == MV_LEFT)
5811       {
5812         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5813         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5814       }
5815       else
5816       {
5817         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5818         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5819       }
5820     }
5821   }
5822
5823   SCAN_PLAYFIELD(x, y)
5824   {
5825     int element = Feld[x][y];
5826
5827     for (i = 0; i < NUM_BELTS; i++)
5828     {
5829       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5830       {
5831         int e_belt_nr = getBeltNrFromBeltElement(element);
5832         int belt_nr = i;
5833
5834         if (e_belt_nr == belt_nr)
5835         {
5836           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5837
5838           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5839         }
5840       }
5841     }
5842   }
5843 }
5844
5845 static void ToggleBeltSwitch(int x, int y)
5846 {
5847   static int belt_base_element[4] =
5848   {
5849     EL_CONVEYOR_BELT_1_LEFT,
5850     EL_CONVEYOR_BELT_2_LEFT,
5851     EL_CONVEYOR_BELT_3_LEFT,
5852     EL_CONVEYOR_BELT_4_LEFT
5853   };
5854   static int belt_base_active_element[4] =
5855   {
5856     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5857     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5858     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5859     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5860   };
5861   static int belt_base_switch_element[4] =
5862   {
5863     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5864     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5865     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5866     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5867   };
5868   static int belt_move_dir[4] =
5869   {
5870     MV_LEFT,
5871     MV_NONE,
5872     MV_RIGHT,
5873     MV_NONE,
5874   };
5875
5876   int element = Feld[x][y];
5877   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5878   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5879   int belt_dir = belt_move_dir[belt_dir_nr];
5880   int xx, yy, i;
5881
5882   if (!IS_BELT_SWITCH(element))
5883     return;
5884
5885   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5886   game.belt_dir[belt_nr] = belt_dir;
5887
5888   if (belt_dir_nr == 3)
5889     belt_dir_nr = 1;
5890
5891   /* set frame order for belt animation graphic according to belt direction */
5892   for (i = 0; i < NUM_BELT_PARTS; i++)
5893   {
5894     int element = belt_base_active_element[belt_nr] + i;
5895     int graphic_1 = el2img(element);
5896     int graphic_2 = el2panelimg(element);
5897
5898     if (belt_dir == MV_LEFT)
5899     {
5900       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5901       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5902     }
5903     else
5904     {
5905       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5906       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5907     }
5908   }
5909
5910   SCAN_PLAYFIELD(xx, yy)
5911   {
5912     int element = Feld[xx][yy];
5913
5914     if (IS_BELT_SWITCH(element))
5915     {
5916       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5917
5918       if (e_belt_nr == belt_nr)
5919       {
5920         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5921         TEST_DrawLevelField(xx, yy);
5922       }
5923     }
5924     else if (IS_BELT(element) && belt_dir != MV_NONE)
5925     {
5926       int e_belt_nr = getBeltNrFromBeltElement(element);
5927
5928       if (e_belt_nr == belt_nr)
5929       {
5930         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5931
5932         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5933         TEST_DrawLevelField(xx, yy);
5934       }
5935     }
5936     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5937     {
5938       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5939
5940       if (e_belt_nr == belt_nr)
5941       {
5942         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5943
5944         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5945         TEST_DrawLevelField(xx, yy);
5946       }
5947     }
5948   }
5949 }
5950
5951 static void ToggleSwitchgateSwitch(int x, int y)
5952 {
5953   int xx, yy;
5954
5955   game.switchgate_pos = !game.switchgate_pos;
5956
5957   SCAN_PLAYFIELD(xx, yy)
5958   {
5959     int element = Feld[xx][yy];
5960
5961     if (element == EL_SWITCHGATE_SWITCH_UP)
5962     {
5963       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5964       TEST_DrawLevelField(xx, yy);
5965     }
5966     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5967     {
5968       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5969       TEST_DrawLevelField(xx, yy);
5970     }
5971     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5972     {
5973       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5974       TEST_DrawLevelField(xx, yy);
5975     }
5976     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5977     {
5978       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5979       TEST_DrawLevelField(xx, yy);
5980     }
5981     else if (element == EL_SWITCHGATE_OPEN ||
5982              element == EL_SWITCHGATE_OPENING)
5983     {
5984       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5985
5986       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5987     }
5988     else if (element == EL_SWITCHGATE_CLOSED ||
5989              element == EL_SWITCHGATE_CLOSING)
5990     {
5991       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5992
5993       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5994     }
5995   }
5996 }
5997
5998 static int getInvisibleActiveFromInvisibleElement(int element)
5999 {
6000   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6001           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6002           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6003           element);
6004 }
6005
6006 static int getInvisibleFromInvisibleActiveElement(int element)
6007 {
6008   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6009           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6010           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6011           element);
6012 }
6013
6014 static void RedrawAllLightSwitchesAndInvisibleElements()
6015 {
6016   int x, y;
6017
6018   SCAN_PLAYFIELD(x, y)
6019   {
6020     int element = Feld[x][y];
6021
6022     if (element == EL_LIGHT_SWITCH &&
6023         game.light_time_left > 0)
6024     {
6025       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6026       TEST_DrawLevelField(x, y);
6027     }
6028     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6029              game.light_time_left == 0)
6030     {
6031       Feld[x][y] = EL_LIGHT_SWITCH;
6032       TEST_DrawLevelField(x, y);
6033     }
6034     else if (element == EL_EMC_DRIPPER &&
6035              game.light_time_left > 0)
6036     {
6037       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6038       TEST_DrawLevelField(x, y);
6039     }
6040     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6041              game.light_time_left == 0)
6042     {
6043       Feld[x][y] = EL_EMC_DRIPPER;
6044       TEST_DrawLevelField(x, y);
6045     }
6046     else if (element == EL_INVISIBLE_STEELWALL ||
6047              element == EL_INVISIBLE_WALL ||
6048              element == EL_INVISIBLE_SAND)
6049     {
6050       if (game.light_time_left > 0)
6051         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6052
6053       TEST_DrawLevelField(x, y);
6054
6055       /* uncrumble neighbour fields, if needed */
6056       if (element == EL_INVISIBLE_SAND)
6057         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6058     }
6059     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6060              element == EL_INVISIBLE_WALL_ACTIVE ||
6061              element == EL_INVISIBLE_SAND_ACTIVE)
6062     {
6063       if (game.light_time_left == 0)
6064         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6065
6066       TEST_DrawLevelField(x, y);
6067
6068       /* re-crumble neighbour fields, if needed */
6069       if (element == EL_INVISIBLE_SAND)
6070         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6071     }
6072   }
6073 }
6074
6075 static void RedrawAllInvisibleElementsForLenses()
6076 {
6077   int x, y;
6078
6079   SCAN_PLAYFIELD(x, y)
6080   {
6081     int element = Feld[x][y];
6082
6083     if (element == EL_EMC_DRIPPER &&
6084         game.lenses_time_left > 0)
6085     {
6086       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6087       TEST_DrawLevelField(x, y);
6088     }
6089     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6090              game.lenses_time_left == 0)
6091     {
6092       Feld[x][y] = EL_EMC_DRIPPER;
6093       TEST_DrawLevelField(x, y);
6094     }
6095     else if (element == EL_INVISIBLE_STEELWALL ||
6096              element == EL_INVISIBLE_WALL ||
6097              element == EL_INVISIBLE_SAND)
6098     {
6099       if (game.lenses_time_left > 0)
6100         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6101
6102       TEST_DrawLevelField(x, y);
6103
6104       /* uncrumble neighbour fields, if needed */
6105       if (element == EL_INVISIBLE_SAND)
6106         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6107     }
6108     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6109              element == EL_INVISIBLE_WALL_ACTIVE ||
6110              element == EL_INVISIBLE_SAND_ACTIVE)
6111     {
6112       if (game.lenses_time_left == 0)
6113         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6114
6115       TEST_DrawLevelField(x, y);
6116
6117       /* re-crumble neighbour fields, if needed */
6118       if (element == EL_INVISIBLE_SAND)
6119         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6120     }
6121   }
6122 }
6123
6124 static void RedrawAllInvisibleElementsForMagnifier()
6125 {
6126   int x, y;
6127
6128   SCAN_PLAYFIELD(x, y)
6129   {
6130     int element = Feld[x][y];
6131
6132     if (element == EL_EMC_FAKE_GRASS &&
6133         game.magnify_time_left > 0)
6134     {
6135       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6136       TEST_DrawLevelField(x, y);
6137     }
6138     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6139              game.magnify_time_left == 0)
6140     {
6141       Feld[x][y] = EL_EMC_FAKE_GRASS;
6142       TEST_DrawLevelField(x, y);
6143     }
6144     else if (IS_GATE_GRAY(element) &&
6145              game.magnify_time_left > 0)
6146     {
6147       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6148                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6149                     IS_EM_GATE_GRAY(element) ?
6150                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6151                     IS_EMC_GATE_GRAY(element) ?
6152                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6153                     IS_DC_GATE_GRAY(element) ?
6154                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6155                     element);
6156       TEST_DrawLevelField(x, y);
6157     }
6158     else if (IS_GATE_GRAY_ACTIVE(element) &&
6159              game.magnify_time_left == 0)
6160     {
6161       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6162                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6163                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6164                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6165                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6166                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6167                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6168                     EL_DC_GATE_WHITE_GRAY :
6169                     element);
6170       TEST_DrawLevelField(x, y);
6171     }
6172   }
6173 }
6174
6175 static void ToggleLightSwitch(int x, int y)
6176 {
6177   int element = Feld[x][y];
6178
6179   game.light_time_left =
6180     (element == EL_LIGHT_SWITCH ?
6181      level.time_light * FRAMES_PER_SECOND : 0);
6182
6183   RedrawAllLightSwitchesAndInvisibleElements();
6184 }
6185
6186 static void ActivateTimegateSwitch(int x, int y)
6187 {
6188   int xx, yy;
6189
6190   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6191
6192   SCAN_PLAYFIELD(xx, yy)
6193   {
6194     int element = Feld[xx][yy];
6195
6196     if (element == EL_TIMEGATE_CLOSED ||
6197         element == EL_TIMEGATE_CLOSING)
6198     {
6199       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6200       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6201     }
6202
6203     /*
6204     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6205     {
6206       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6207       TEST_DrawLevelField(xx, yy);
6208     }
6209     */
6210
6211   }
6212
6213   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6214                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6215 }
6216
6217 void Impact(int x, int y)
6218 {
6219   boolean last_line = (y == lev_fieldy - 1);
6220   boolean object_hit = FALSE;
6221   boolean impact = (last_line || object_hit);
6222   int element = Feld[x][y];
6223   int smashed = EL_STEELWALL;
6224
6225   if (!last_line)       /* check if element below was hit */
6226   {
6227     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6228       return;
6229
6230     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6231                                          MovDir[x][y + 1] != MV_DOWN ||
6232                                          MovPos[x][y + 1] <= TILEY / 2));
6233
6234     /* do not smash moving elements that left the smashed field in time */
6235     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6236         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6237       object_hit = FALSE;
6238
6239 #if USE_QUICKSAND_IMPACT_BUGFIX
6240     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6241     {
6242       RemoveMovingField(x, y + 1);
6243       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6244       Feld[x][y + 2] = EL_ROCK;
6245       TEST_DrawLevelField(x, y + 2);
6246
6247       object_hit = TRUE;
6248     }
6249
6250     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6251     {
6252       RemoveMovingField(x, y + 1);
6253       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6254       Feld[x][y + 2] = EL_ROCK;
6255       TEST_DrawLevelField(x, y + 2);
6256
6257       object_hit = TRUE;
6258     }
6259 #endif
6260
6261     if (object_hit)
6262       smashed = MovingOrBlocked2Element(x, y + 1);
6263
6264     impact = (last_line || object_hit);
6265   }
6266
6267   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6268   {
6269     SplashAcid(x, y + 1);
6270     return;
6271   }
6272
6273   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6274   /* only reset graphic animation if graphic really changes after impact */
6275   if (impact &&
6276       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6277   {
6278     ResetGfxAnimation(x, y);
6279     TEST_DrawLevelField(x, y);
6280   }
6281
6282   if (impact && CAN_EXPLODE_IMPACT(element))
6283   {
6284     Bang(x, y);
6285     return;
6286   }
6287   else if (impact && element == EL_PEARL &&
6288            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6289   {
6290     ResetGfxAnimation(x, y);
6291
6292     Feld[x][y] = EL_PEARL_BREAKING;
6293     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6294     return;
6295   }
6296   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6297   {
6298     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6299
6300     return;
6301   }
6302
6303   if (impact && element == EL_AMOEBA_DROP)
6304   {
6305     if (object_hit && IS_PLAYER(x, y + 1))
6306       KillPlayerUnlessEnemyProtected(x, y + 1);
6307     else if (object_hit && smashed == EL_PENGUIN)
6308       Bang(x, y + 1);
6309     else
6310     {
6311       Feld[x][y] = EL_AMOEBA_GROWING;
6312       Store[x][y] = EL_AMOEBA_WET;
6313
6314       ResetRandomAnimationValue(x, y);
6315     }
6316     return;
6317   }
6318
6319   if (object_hit)               /* check which object was hit */
6320   {
6321     if ((CAN_PASS_MAGIC_WALL(element) && 
6322          (smashed == EL_MAGIC_WALL ||
6323           smashed == EL_BD_MAGIC_WALL)) ||
6324         (CAN_PASS_DC_MAGIC_WALL(element) &&
6325          smashed == EL_DC_MAGIC_WALL))
6326     {
6327       int xx, yy;
6328       int activated_magic_wall =
6329         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6330          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6331          EL_DC_MAGIC_WALL_ACTIVE);
6332
6333       /* activate magic wall / mill */
6334       SCAN_PLAYFIELD(xx, yy)
6335       {
6336         if (Feld[xx][yy] == smashed)
6337           Feld[xx][yy] = activated_magic_wall;
6338       }
6339
6340       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6341       game.magic_wall_active = TRUE;
6342
6343       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6344                             SND_MAGIC_WALL_ACTIVATING :
6345                             smashed == EL_BD_MAGIC_WALL ?
6346                             SND_BD_MAGIC_WALL_ACTIVATING :
6347                             SND_DC_MAGIC_WALL_ACTIVATING));
6348     }
6349
6350     if (IS_PLAYER(x, y + 1))
6351     {
6352       if (CAN_SMASH_PLAYER(element))
6353       {
6354         KillPlayerUnlessEnemyProtected(x, y + 1);
6355         return;
6356       }
6357     }
6358     else if (smashed == EL_PENGUIN)
6359     {
6360       if (CAN_SMASH_PLAYER(element))
6361       {
6362         Bang(x, y + 1);
6363         return;
6364       }
6365     }
6366     else if (element == EL_BD_DIAMOND)
6367     {
6368       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6369       {
6370         Bang(x, y + 1);
6371         return;
6372       }
6373     }
6374     else if (((element == EL_SP_INFOTRON ||
6375                element == EL_SP_ZONK) &&
6376               (smashed == EL_SP_SNIKSNAK ||
6377                smashed == EL_SP_ELECTRON ||
6378                smashed == EL_SP_DISK_ORANGE)) ||
6379              (element == EL_SP_INFOTRON &&
6380               smashed == EL_SP_DISK_YELLOW))
6381     {
6382       Bang(x, y + 1);
6383       return;
6384     }
6385     else if (CAN_SMASH_EVERYTHING(element))
6386     {
6387       if (IS_CLASSIC_ENEMY(smashed) ||
6388           CAN_EXPLODE_SMASHED(smashed))
6389       {
6390         Bang(x, y + 1);
6391         return;
6392       }
6393       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6394       {
6395         if (smashed == EL_LAMP ||
6396             smashed == EL_LAMP_ACTIVE)
6397         {
6398           Bang(x, y + 1);
6399           return;
6400         }
6401         else if (smashed == EL_NUT)
6402         {
6403           Feld[x][y + 1] = EL_NUT_BREAKING;
6404           PlayLevelSound(x, y, SND_NUT_BREAKING);
6405           RaiseScoreElement(EL_NUT);
6406           return;
6407         }
6408         else if (smashed == EL_PEARL)
6409         {
6410           ResetGfxAnimation(x, y);
6411
6412           Feld[x][y + 1] = EL_PEARL_BREAKING;
6413           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6414           return;
6415         }
6416         else if (smashed == EL_DIAMOND)
6417         {
6418           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6419           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6420           return;
6421         }
6422         else if (IS_BELT_SWITCH(smashed))
6423         {
6424           ToggleBeltSwitch(x, y + 1);
6425         }
6426         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6427                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6428                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6429                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6430         {
6431           ToggleSwitchgateSwitch(x, y + 1);
6432         }
6433         else if (smashed == EL_LIGHT_SWITCH ||
6434                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6435         {
6436           ToggleLightSwitch(x, y + 1);
6437         }
6438         else
6439         {
6440           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6441
6442           CheckElementChangeBySide(x, y + 1, smashed, element,
6443                                    CE_SWITCHED, CH_SIDE_TOP);
6444           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6445                                             CH_SIDE_TOP);
6446         }
6447       }
6448       else
6449       {
6450         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6451       }
6452     }
6453   }
6454
6455   /* play sound of magic wall / mill */
6456   if (!last_line &&
6457       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6458        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6459        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6460   {
6461     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6462       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6463     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6464       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6465     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6466       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6467
6468     return;
6469   }
6470
6471   /* play sound of object that hits the ground */
6472   if (last_line || object_hit)
6473     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6474 }
6475
6476 inline static void TurnRoundExt(int x, int y)
6477 {
6478   static struct
6479   {
6480     int dx, dy;
6481   } move_xy[] =
6482   {
6483     {  0,  0 },
6484     { -1,  0 },
6485     { +1,  0 },
6486     {  0,  0 },
6487     {  0, -1 },
6488     {  0,  0 }, { 0, 0 }, { 0, 0 },
6489     {  0, +1 }
6490   };
6491   static struct
6492   {
6493     int left, right, back;
6494   } turn[] =
6495   {
6496     { 0,        0,              0        },
6497     { MV_DOWN,  MV_UP,          MV_RIGHT },
6498     { MV_UP,    MV_DOWN,        MV_LEFT  },
6499     { 0,        0,              0        },
6500     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6501     { 0,        0,              0        },
6502     { 0,        0,              0        },
6503     { 0,        0,              0        },
6504     { MV_RIGHT, MV_LEFT,        MV_UP    }
6505   };
6506
6507   int element = Feld[x][y];
6508   int move_pattern = element_info[element].move_pattern;
6509
6510   int old_move_dir = MovDir[x][y];
6511   int left_dir  = turn[old_move_dir].left;
6512   int right_dir = turn[old_move_dir].right;
6513   int back_dir  = turn[old_move_dir].back;
6514
6515   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6516   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6517   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6518   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6519
6520   int left_x  = x + left_dx,  left_y  = y + left_dy;
6521   int right_x = x + right_dx, right_y = y + right_dy;
6522   int move_x  = x + move_dx,  move_y  = y + move_dy;
6523
6524   int xx, yy;
6525
6526   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6527   {
6528     TestIfBadThingTouchesOtherBadThing(x, y);
6529
6530     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6531       MovDir[x][y] = right_dir;
6532     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6533       MovDir[x][y] = left_dir;
6534
6535     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6536       MovDelay[x][y] = 9;
6537     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6538       MovDelay[x][y] = 1;
6539   }
6540   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6541   {
6542     TestIfBadThingTouchesOtherBadThing(x, y);
6543
6544     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6545       MovDir[x][y] = left_dir;
6546     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6547       MovDir[x][y] = right_dir;
6548
6549     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6550       MovDelay[x][y] = 9;
6551     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6552       MovDelay[x][y] = 1;
6553   }
6554   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6555   {
6556     TestIfBadThingTouchesOtherBadThing(x, y);
6557
6558     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6559       MovDir[x][y] = left_dir;
6560     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6561       MovDir[x][y] = right_dir;
6562
6563     if (MovDir[x][y] != old_move_dir)
6564       MovDelay[x][y] = 9;
6565   }
6566   else if (element == EL_YAMYAM)
6567   {
6568     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6569     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6570
6571     if (can_turn_left && can_turn_right)
6572       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6573     else if (can_turn_left)
6574       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6575     else if (can_turn_right)
6576       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6577     else
6578       MovDir[x][y] = back_dir;
6579
6580     MovDelay[x][y] = 16 + 16 * RND(3);
6581   }
6582   else if (element == EL_DARK_YAMYAM)
6583   {
6584     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6585                                                          left_x, left_y);
6586     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6587                                                          right_x, right_y);
6588
6589     if (can_turn_left && can_turn_right)
6590       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6591     else if (can_turn_left)
6592       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6593     else if (can_turn_right)
6594       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6595     else
6596       MovDir[x][y] = back_dir;
6597
6598     MovDelay[x][y] = 16 + 16 * RND(3);
6599   }
6600   else if (element == EL_PACMAN)
6601   {
6602     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6603     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6604
6605     if (can_turn_left && can_turn_right)
6606       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6607     else if (can_turn_left)
6608       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6609     else if (can_turn_right)
6610       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6611     else
6612       MovDir[x][y] = back_dir;
6613
6614     MovDelay[x][y] = 6 + RND(40);
6615   }
6616   else if (element == EL_PIG)
6617   {
6618     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6619     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6620     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6621     boolean should_turn_left, should_turn_right, should_move_on;
6622     int rnd_value = 24;
6623     int rnd = RND(rnd_value);
6624
6625     should_turn_left = (can_turn_left &&
6626                         (!can_move_on ||
6627                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6628                                                    y + back_dy + left_dy)));
6629     should_turn_right = (can_turn_right &&
6630                          (!can_move_on ||
6631                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6632                                                     y + back_dy + right_dy)));
6633     should_move_on = (can_move_on &&
6634                       (!can_turn_left ||
6635                        !can_turn_right ||
6636                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6637                                                  y + move_dy + left_dy) ||
6638                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6639                                                  y + move_dy + right_dy)));
6640
6641     if (should_turn_left || should_turn_right || should_move_on)
6642     {
6643       if (should_turn_left && should_turn_right && should_move_on)
6644         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6645                         rnd < 2 * rnd_value / 3 ? right_dir :
6646                         old_move_dir);
6647       else if (should_turn_left && should_turn_right)
6648         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6649       else if (should_turn_left && should_move_on)
6650         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6651       else if (should_turn_right && should_move_on)
6652         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6653       else if (should_turn_left)
6654         MovDir[x][y] = left_dir;
6655       else if (should_turn_right)
6656         MovDir[x][y] = right_dir;
6657       else if (should_move_on)
6658         MovDir[x][y] = old_move_dir;
6659     }
6660     else if (can_move_on && rnd > rnd_value / 8)
6661       MovDir[x][y] = old_move_dir;
6662     else if (can_turn_left && can_turn_right)
6663       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6664     else if (can_turn_left && rnd > rnd_value / 8)
6665       MovDir[x][y] = left_dir;
6666     else if (can_turn_right && rnd > rnd_value/8)
6667       MovDir[x][y] = right_dir;
6668     else
6669       MovDir[x][y] = back_dir;
6670
6671     xx = x + move_xy[MovDir[x][y]].dx;
6672     yy = y + move_xy[MovDir[x][y]].dy;
6673
6674     if (!IN_LEV_FIELD(xx, yy) ||
6675         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6676       MovDir[x][y] = old_move_dir;
6677
6678     MovDelay[x][y] = 0;
6679   }
6680   else if (element == EL_DRAGON)
6681   {
6682     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6683     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6684     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6685     int rnd_value = 24;
6686     int rnd = RND(rnd_value);
6687
6688     if (can_move_on && rnd > rnd_value / 8)
6689       MovDir[x][y] = old_move_dir;
6690     else if (can_turn_left && can_turn_right)
6691       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6692     else if (can_turn_left && rnd > rnd_value / 8)
6693       MovDir[x][y] = left_dir;
6694     else if (can_turn_right && rnd > rnd_value / 8)
6695       MovDir[x][y] = right_dir;
6696     else
6697       MovDir[x][y] = back_dir;
6698
6699     xx = x + move_xy[MovDir[x][y]].dx;
6700     yy = y + move_xy[MovDir[x][y]].dy;
6701
6702     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6703       MovDir[x][y] = old_move_dir;
6704
6705     MovDelay[x][y] = 0;
6706   }
6707   else if (element == EL_MOLE)
6708   {
6709     boolean can_move_on =
6710       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6711                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6712                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6713     if (!can_move_on)
6714     {
6715       boolean can_turn_left =
6716         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6717                               IS_AMOEBOID(Feld[left_x][left_y])));
6718
6719       boolean can_turn_right =
6720         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6721                               IS_AMOEBOID(Feld[right_x][right_y])));
6722
6723       if (can_turn_left && can_turn_right)
6724         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6725       else if (can_turn_left)
6726         MovDir[x][y] = left_dir;
6727       else
6728         MovDir[x][y] = right_dir;
6729     }
6730
6731     if (MovDir[x][y] != old_move_dir)
6732       MovDelay[x][y] = 9;
6733   }
6734   else if (element == EL_BALLOON)
6735   {
6736     MovDir[x][y] = game.wind_direction;
6737     MovDelay[x][y] = 0;
6738   }
6739   else if (element == EL_SPRING)
6740   {
6741     if (MovDir[x][y] & MV_HORIZONTAL)
6742     {
6743       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6744           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6745       {
6746         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6747         ResetGfxAnimation(move_x, move_y);
6748         TEST_DrawLevelField(move_x, move_y);
6749
6750         MovDir[x][y] = back_dir;
6751       }
6752       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6753                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6754         MovDir[x][y] = MV_NONE;
6755     }
6756
6757     MovDelay[x][y] = 0;
6758   }
6759   else if (element == EL_ROBOT ||
6760            element == EL_SATELLITE ||
6761            element == EL_PENGUIN ||
6762            element == EL_EMC_ANDROID)
6763   {
6764     int attr_x = -1, attr_y = -1;
6765
6766     if (AllPlayersGone)
6767     {
6768       attr_x = ExitX;
6769       attr_y = ExitY;
6770     }
6771     else
6772     {
6773       int i;
6774
6775       for (i = 0; i < MAX_PLAYERS; i++)
6776       {
6777         struct PlayerInfo *player = &stored_player[i];
6778         int jx = player->jx, jy = player->jy;
6779
6780         if (!player->active)
6781           continue;
6782
6783         if (attr_x == -1 ||
6784             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6785         {
6786           attr_x = jx;
6787           attr_y = jy;
6788         }
6789       }
6790     }
6791
6792     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6793         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6794          game.engine_version < VERSION_IDENT(3,1,0,0)))
6795     {
6796       attr_x = ZX;
6797       attr_y = ZY;
6798     }
6799
6800     if (element == EL_PENGUIN)
6801     {
6802       int i;
6803       static int xy[4][2] =
6804       {
6805         { 0, -1 },
6806         { -1, 0 },
6807         { +1, 0 },
6808         { 0, +1 }
6809       };
6810
6811       for (i = 0; i < NUM_DIRECTIONS; i++)
6812       {
6813         int ex = x + xy[i][0];
6814         int ey = y + xy[i][1];
6815
6816         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6817                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6818                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6819                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6820         {
6821           attr_x = ex;
6822           attr_y = ey;
6823           break;
6824         }
6825       }
6826     }
6827
6828     MovDir[x][y] = MV_NONE;
6829     if (attr_x < x)
6830       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6831     else if (attr_x > x)
6832       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6833     if (attr_y < y)
6834       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6835     else if (attr_y > y)
6836       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6837
6838     if (element == EL_ROBOT)
6839     {
6840       int newx, newy;
6841
6842       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6843         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6844       Moving2Blocked(x, y, &newx, &newy);
6845
6846       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6847         MovDelay[x][y] = 8 + 8 * !RND(3);
6848       else
6849         MovDelay[x][y] = 16;
6850     }
6851     else if (element == EL_PENGUIN)
6852     {
6853       int newx, newy;
6854
6855       MovDelay[x][y] = 1;
6856
6857       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6858       {
6859         boolean first_horiz = RND(2);
6860         int new_move_dir = MovDir[x][y];
6861
6862         MovDir[x][y] =
6863           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6864         Moving2Blocked(x, y, &newx, &newy);
6865
6866         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6867           return;
6868
6869         MovDir[x][y] =
6870           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6871         Moving2Blocked(x, y, &newx, &newy);
6872
6873         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6874           return;
6875
6876         MovDir[x][y] = old_move_dir;
6877         return;
6878       }
6879     }
6880     else if (element == EL_SATELLITE)
6881     {
6882       int newx, newy;
6883
6884       MovDelay[x][y] = 1;
6885
6886       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6887       {
6888         boolean first_horiz = RND(2);
6889         int new_move_dir = MovDir[x][y];
6890
6891         MovDir[x][y] =
6892           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6893         Moving2Blocked(x, y, &newx, &newy);
6894
6895         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6896           return;
6897
6898         MovDir[x][y] =
6899           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6900         Moving2Blocked(x, y, &newx, &newy);
6901
6902         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6903           return;
6904
6905         MovDir[x][y] = old_move_dir;
6906         return;
6907       }
6908     }
6909     else if (element == EL_EMC_ANDROID)
6910     {
6911       static int check_pos[16] =
6912       {
6913         -1,             /*  0 => (invalid)          */
6914         7,              /*  1 => MV_LEFT            */
6915         3,              /*  2 => MV_RIGHT           */
6916         -1,             /*  3 => (invalid)          */
6917         1,              /*  4 =>            MV_UP   */
6918         0,              /*  5 => MV_LEFT  | MV_UP   */
6919         2,              /*  6 => MV_RIGHT | MV_UP   */
6920         -1,             /*  7 => (invalid)          */
6921         5,              /*  8 =>            MV_DOWN */
6922         6,              /*  9 => MV_LEFT  | MV_DOWN */
6923         4,              /* 10 => MV_RIGHT | MV_DOWN */
6924         -1,             /* 11 => (invalid)          */
6925         -1,             /* 12 => (invalid)          */
6926         -1,             /* 13 => (invalid)          */
6927         -1,             /* 14 => (invalid)          */
6928         -1,             /* 15 => (invalid)          */
6929       };
6930       static struct
6931       {
6932         int dx, dy;
6933         int dir;
6934       } check_xy[8] =
6935       {
6936         { -1, -1,       MV_LEFT  | MV_UP   },
6937         {  0, -1,                  MV_UP   },
6938         { +1, -1,       MV_RIGHT | MV_UP   },
6939         { +1,  0,       MV_RIGHT           },
6940         { +1, +1,       MV_RIGHT | MV_DOWN },
6941         {  0, +1,                  MV_DOWN },
6942         { -1, +1,       MV_LEFT  | MV_DOWN },
6943         { -1,  0,       MV_LEFT            },
6944       };
6945       int start_pos, check_order;
6946       boolean can_clone = FALSE;
6947       int i;
6948
6949       /* check if there is any free field around current position */
6950       for (i = 0; i < 8; i++)
6951       {
6952         int newx = x + check_xy[i].dx;
6953         int newy = y + check_xy[i].dy;
6954
6955         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6956         {
6957           can_clone = TRUE;
6958
6959           break;
6960         }
6961       }
6962
6963       if (can_clone)            /* randomly find an element to clone */
6964       {
6965         can_clone = FALSE;
6966
6967         start_pos = check_pos[RND(8)];
6968         check_order = (RND(2) ? -1 : +1);
6969
6970         for (i = 0; i < 8; i++)
6971         {
6972           int pos_raw = start_pos + i * check_order;
6973           int pos = (pos_raw + 8) % 8;
6974           int newx = x + check_xy[pos].dx;
6975           int newy = y + check_xy[pos].dy;
6976
6977           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6978           {
6979             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6980             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6981
6982             Store[x][y] = Feld[newx][newy];
6983
6984             can_clone = TRUE;
6985
6986             break;
6987           }
6988         }
6989       }
6990
6991       if (can_clone)            /* randomly find a direction to move */
6992       {
6993         can_clone = FALSE;
6994
6995         start_pos = check_pos[RND(8)];
6996         check_order = (RND(2) ? -1 : +1);
6997
6998         for (i = 0; i < 8; i++)
6999         {
7000           int pos_raw = start_pos + i * check_order;
7001           int pos = (pos_raw + 8) % 8;
7002           int newx = x + check_xy[pos].dx;
7003           int newy = y + check_xy[pos].dy;
7004           int new_move_dir = check_xy[pos].dir;
7005
7006           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7007           {
7008             MovDir[x][y] = new_move_dir;
7009             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7010
7011             can_clone = TRUE;
7012
7013             break;
7014           }
7015         }
7016       }
7017
7018       if (can_clone)            /* cloning and moving successful */
7019         return;
7020
7021       /* cannot clone -- try to move towards player */
7022
7023       start_pos = check_pos[MovDir[x][y] & 0x0f];
7024       check_order = (RND(2) ? -1 : +1);
7025
7026       for (i = 0; i < 3; i++)
7027       {
7028         /* first check start_pos, then previous/next or (next/previous) pos */
7029         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7030         int pos = (pos_raw + 8) % 8;
7031         int newx = x + check_xy[pos].dx;
7032         int newy = y + check_xy[pos].dy;
7033         int new_move_dir = check_xy[pos].dir;
7034
7035         if (IS_PLAYER(newx, newy))
7036           break;
7037
7038         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7039         {
7040           MovDir[x][y] = new_move_dir;
7041           MovDelay[x][y] = level.android_move_time * 8 + 1;
7042
7043           break;
7044         }
7045       }
7046     }
7047   }
7048   else if (move_pattern == MV_TURNING_LEFT ||
7049            move_pattern == MV_TURNING_RIGHT ||
7050            move_pattern == MV_TURNING_LEFT_RIGHT ||
7051            move_pattern == MV_TURNING_RIGHT_LEFT ||
7052            move_pattern == MV_TURNING_RANDOM ||
7053            move_pattern == MV_ALL_DIRECTIONS)
7054   {
7055     boolean can_turn_left =
7056       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7057     boolean can_turn_right =
7058       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7059
7060     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7061       return;
7062
7063     if (move_pattern == MV_TURNING_LEFT)
7064       MovDir[x][y] = left_dir;
7065     else if (move_pattern == MV_TURNING_RIGHT)
7066       MovDir[x][y] = right_dir;
7067     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7068       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7069     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7070       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7071     else if (move_pattern == MV_TURNING_RANDOM)
7072       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7073                       can_turn_right && !can_turn_left ? right_dir :
7074                       RND(2) ? left_dir : right_dir);
7075     else if (can_turn_left && can_turn_right)
7076       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7077     else if (can_turn_left)
7078       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7079     else if (can_turn_right)
7080       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7081     else
7082       MovDir[x][y] = back_dir;
7083
7084     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7085   }
7086   else if (move_pattern == MV_HORIZONTAL ||
7087            move_pattern == MV_VERTICAL)
7088   {
7089     if (move_pattern & old_move_dir)
7090       MovDir[x][y] = back_dir;
7091     else if (move_pattern == MV_HORIZONTAL)
7092       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7093     else if (move_pattern == MV_VERTICAL)
7094       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7095
7096     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7097   }
7098   else if (move_pattern & MV_ANY_DIRECTION)
7099   {
7100     MovDir[x][y] = move_pattern;
7101     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7102   }
7103   else if (move_pattern & MV_WIND_DIRECTION)
7104   {
7105     MovDir[x][y] = game.wind_direction;
7106     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7107   }
7108   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7109   {
7110     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7111       MovDir[x][y] = left_dir;
7112     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7113       MovDir[x][y] = right_dir;
7114
7115     if (MovDir[x][y] != old_move_dir)
7116       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7117   }
7118   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7119   {
7120     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7121       MovDir[x][y] = right_dir;
7122     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7123       MovDir[x][y] = left_dir;
7124
7125     if (MovDir[x][y] != old_move_dir)
7126       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7127   }
7128   else if (move_pattern == MV_TOWARDS_PLAYER ||
7129            move_pattern == MV_AWAY_FROM_PLAYER)
7130   {
7131     int attr_x = -1, attr_y = -1;
7132     int newx, newy;
7133     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7134
7135     if (AllPlayersGone)
7136     {
7137       attr_x = ExitX;
7138       attr_y = ExitY;
7139     }
7140     else
7141     {
7142       int i;
7143
7144       for (i = 0; i < MAX_PLAYERS; i++)
7145       {
7146         struct PlayerInfo *player = &stored_player[i];
7147         int jx = player->jx, jy = player->jy;
7148
7149         if (!player->active)
7150           continue;
7151
7152         if (attr_x == -1 ||
7153             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7154         {
7155           attr_x = jx;
7156           attr_y = jy;
7157         }
7158       }
7159     }
7160
7161     MovDir[x][y] = MV_NONE;
7162     if (attr_x < x)
7163       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7164     else if (attr_x > x)
7165       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7166     if (attr_y < y)
7167       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7168     else if (attr_y > y)
7169       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7170
7171     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7172
7173     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7174     {
7175       boolean first_horiz = RND(2);
7176       int new_move_dir = MovDir[x][y];
7177
7178       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7179       {
7180         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7181         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7182
7183         return;
7184       }
7185
7186       MovDir[x][y] =
7187         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7188       Moving2Blocked(x, y, &newx, &newy);
7189
7190       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7191         return;
7192
7193       MovDir[x][y] =
7194         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7195       Moving2Blocked(x, y, &newx, &newy);
7196
7197       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7198         return;
7199
7200       MovDir[x][y] = old_move_dir;
7201     }
7202   }
7203   else if (move_pattern == MV_WHEN_PUSHED ||
7204            move_pattern == MV_WHEN_DROPPED)
7205   {
7206     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7207       MovDir[x][y] = MV_NONE;
7208
7209     MovDelay[x][y] = 0;
7210   }
7211   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7212   {
7213     static int test_xy[7][2] =
7214     {
7215       { 0, -1 },
7216       { -1, 0 },
7217       { +1, 0 },
7218       { 0, +1 },
7219       { 0, -1 },
7220       { -1, 0 },
7221       { +1, 0 },
7222     };
7223     static int test_dir[7] =
7224     {
7225       MV_UP,
7226       MV_LEFT,
7227       MV_RIGHT,
7228       MV_DOWN,
7229       MV_UP,
7230       MV_LEFT,
7231       MV_RIGHT,
7232     };
7233     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7234     int move_preference = -1000000;     /* start with very low preference */
7235     int new_move_dir = MV_NONE;
7236     int start_test = RND(4);
7237     int i;
7238
7239     for (i = 0; i < NUM_DIRECTIONS; i++)
7240     {
7241       int move_dir = test_dir[start_test + i];
7242       int move_dir_preference;
7243
7244       xx = x + test_xy[start_test + i][0];
7245       yy = y + test_xy[start_test + i][1];
7246
7247       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7248           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7249       {
7250         new_move_dir = move_dir;
7251
7252         break;
7253       }
7254
7255       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7256         continue;
7257
7258       move_dir_preference = -1 * RunnerVisit[xx][yy];
7259       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7260         move_dir_preference = PlayerVisit[xx][yy];
7261
7262       if (move_dir_preference > move_preference)
7263       {
7264         /* prefer field that has not been visited for the longest time */
7265         move_preference = move_dir_preference;
7266         new_move_dir = move_dir;
7267       }
7268       else if (move_dir_preference == move_preference &&
7269                move_dir == old_move_dir)
7270       {
7271         /* prefer last direction when all directions are preferred equally */
7272         move_preference = move_dir_preference;
7273         new_move_dir = move_dir;
7274       }
7275     }
7276
7277     MovDir[x][y] = new_move_dir;
7278     if (old_move_dir != new_move_dir)
7279       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7280   }
7281 }
7282
7283 static void TurnRound(int x, int y)
7284 {
7285   int direction = MovDir[x][y];
7286
7287   TurnRoundExt(x, y);
7288
7289   GfxDir[x][y] = MovDir[x][y];
7290
7291   if (direction != MovDir[x][y])
7292     GfxFrame[x][y] = 0;
7293
7294   if (MovDelay[x][y])
7295     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7296
7297   ResetGfxFrame(x, y);
7298 }
7299
7300 static boolean JustBeingPushed(int x, int y)
7301 {
7302   int i;
7303
7304   for (i = 0; i < MAX_PLAYERS; i++)
7305   {
7306     struct PlayerInfo *player = &stored_player[i];
7307
7308     if (player->active && player->is_pushing && player->MovPos)
7309     {
7310       int next_jx = player->jx + (player->jx - player->last_jx);
7311       int next_jy = player->jy + (player->jy - player->last_jy);
7312
7313       if (x == next_jx && y == next_jy)
7314         return TRUE;
7315     }
7316   }
7317
7318   return FALSE;
7319 }
7320
7321 void StartMoving(int x, int y)
7322 {
7323   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7324   int element = Feld[x][y];
7325
7326   if (Stop[x][y])
7327     return;
7328
7329   if (MovDelay[x][y] == 0)
7330     GfxAction[x][y] = ACTION_DEFAULT;
7331
7332   if (CAN_FALL(element) && y < lev_fieldy - 1)
7333   {
7334     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7335         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7336       if (JustBeingPushed(x, y))
7337         return;
7338
7339     if (element == EL_QUICKSAND_FULL)
7340     {
7341       if (IS_FREE(x, y + 1))
7342       {
7343         InitMovingField(x, y, MV_DOWN);
7344         started_moving = TRUE;
7345
7346         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7347 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7348         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7349           Store[x][y] = EL_ROCK;
7350 #else
7351         Store[x][y] = EL_ROCK;
7352 #endif
7353
7354         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7355       }
7356       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7357       {
7358         if (!MovDelay[x][y])
7359         {
7360           MovDelay[x][y] = TILEY + 1;
7361
7362           ResetGfxAnimation(x, y);
7363           ResetGfxAnimation(x, y + 1);
7364         }
7365
7366         if (MovDelay[x][y])
7367         {
7368           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7369           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7370
7371           MovDelay[x][y]--;
7372           if (MovDelay[x][y])
7373             return;
7374         }
7375
7376         Feld[x][y] = EL_QUICKSAND_EMPTY;
7377         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7378         Store[x][y + 1] = Store[x][y];
7379         Store[x][y] = 0;
7380
7381         PlayLevelSoundAction(x, y, ACTION_FILLING);
7382       }
7383       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7384       {
7385         if (!MovDelay[x][y])
7386         {
7387           MovDelay[x][y] = TILEY + 1;
7388
7389           ResetGfxAnimation(x, y);
7390           ResetGfxAnimation(x, y + 1);
7391         }
7392
7393         if (MovDelay[x][y])
7394         {
7395           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7396           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7397
7398           MovDelay[x][y]--;
7399           if (MovDelay[x][y])
7400             return;
7401         }
7402
7403         Feld[x][y] = EL_QUICKSAND_EMPTY;
7404         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7405         Store[x][y + 1] = Store[x][y];
7406         Store[x][y] = 0;
7407
7408         PlayLevelSoundAction(x, y, ACTION_FILLING);
7409       }
7410     }
7411     else if (element == EL_QUICKSAND_FAST_FULL)
7412     {
7413       if (IS_FREE(x, y + 1))
7414       {
7415         InitMovingField(x, y, MV_DOWN);
7416         started_moving = TRUE;
7417
7418         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7419 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7420         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7421           Store[x][y] = EL_ROCK;
7422 #else
7423         Store[x][y] = EL_ROCK;
7424 #endif
7425
7426         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7427       }
7428       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7429       {
7430         if (!MovDelay[x][y])
7431         {
7432           MovDelay[x][y] = TILEY + 1;
7433
7434           ResetGfxAnimation(x, y);
7435           ResetGfxAnimation(x, y + 1);
7436         }
7437
7438         if (MovDelay[x][y])
7439         {
7440           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7441           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7442
7443           MovDelay[x][y]--;
7444           if (MovDelay[x][y])
7445             return;
7446         }
7447
7448         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7449         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7450         Store[x][y + 1] = Store[x][y];
7451         Store[x][y] = 0;
7452
7453         PlayLevelSoundAction(x, y, ACTION_FILLING);
7454       }
7455       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7456       {
7457         if (!MovDelay[x][y])
7458         {
7459           MovDelay[x][y] = TILEY + 1;
7460
7461           ResetGfxAnimation(x, y);
7462           ResetGfxAnimation(x, y + 1);
7463         }
7464
7465         if (MovDelay[x][y])
7466         {
7467           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7468           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7469
7470           MovDelay[x][y]--;
7471           if (MovDelay[x][y])
7472             return;
7473         }
7474
7475         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7476         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7477         Store[x][y + 1] = Store[x][y];
7478         Store[x][y] = 0;
7479
7480         PlayLevelSoundAction(x, y, ACTION_FILLING);
7481       }
7482     }
7483     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7484              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7485     {
7486       InitMovingField(x, y, MV_DOWN);
7487       started_moving = TRUE;
7488
7489       Feld[x][y] = EL_QUICKSAND_FILLING;
7490       Store[x][y] = element;
7491
7492       PlayLevelSoundAction(x, y, ACTION_FILLING);
7493     }
7494     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7495              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7496     {
7497       InitMovingField(x, y, MV_DOWN);
7498       started_moving = TRUE;
7499
7500       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7501       Store[x][y] = element;
7502
7503       PlayLevelSoundAction(x, y, ACTION_FILLING);
7504     }
7505     else if (element == EL_MAGIC_WALL_FULL)
7506     {
7507       if (IS_FREE(x, y + 1))
7508       {
7509         InitMovingField(x, y, MV_DOWN);
7510         started_moving = TRUE;
7511
7512         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7513         Store[x][y] = EL_CHANGED(Store[x][y]);
7514       }
7515       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7516       {
7517         if (!MovDelay[x][y])
7518           MovDelay[x][y] = TILEY / 4 + 1;
7519
7520         if (MovDelay[x][y])
7521         {
7522           MovDelay[x][y]--;
7523           if (MovDelay[x][y])
7524             return;
7525         }
7526
7527         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7528         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7529         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7530         Store[x][y] = 0;
7531       }
7532     }
7533     else if (element == EL_BD_MAGIC_WALL_FULL)
7534     {
7535       if (IS_FREE(x, y + 1))
7536       {
7537         InitMovingField(x, y, MV_DOWN);
7538         started_moving = TRUE;
7539
7540         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7541         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7542       }
7543       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7544       {
7545         if (!MovDelay[x][y])
7546           MovDelay[x][y] = TILEY / 4 + 1;
7547
7548         if (MovDelay[x][y])
7549         {
7550           MovDelay[x][y]--;
7551           if (MovDelay[x][y])
7552             return;
7553         }
7554
7555         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7556         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7557         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7558         Store[x][y] = 0;
7559       }
7560     }
7561     else if (element == EL_DC_MAGIC_WALL_FULL)
7562     {
7563       if (IS_FREE(x, y + 1))
7564       {
7565         InitMovingField(x, y, MV_DOWN);
7566         started_moving = TRUE;
7567
7568         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7569         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7570       }
7571       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7572       {
7573         if (!MovDelay[x][y])
7574           MovDelay[x][y] = TILEY / 4 + 1;
7575
7576         if (MovDelay[x][y])
7577         {
7578           MovDelay[x][y]--;
7579           if (MovDelay[x][y])
7580             return;
7581         }
7582
7583         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7584         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7585         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7586         Store[x][y] = 0;
7587       }
7588     }
7589     else if ((CAN_PASS_MAGIC_WALL(element) &&
7590               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7591                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7592              (CAN_PASS_DC_MAGIC_WALL(element) &&
7593               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7594
7595     {
7596       InitMovingField(x, y, MV_DOWN);
7597       started_moving = TRUE;
7598
7599       Feld[x][y] =
7600         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7601          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7602          EL_DC_MAGIC_WALL_FILLING);
7603       Store[x][y] = element;
7604     }
7605     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7606     {
7607       SplashAcid(x, y + 1);
7608
7609       InitMovingField(x, y, MV_DOWN);
7610       started_moving = TRUE;
7611
7612       Store[x][y] = EL_ACID;
7613     }
7614     else if (
7615              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7616               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7617              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7618               CAN_FALL(element) && WasJustFalling[x][y] &&
7619               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7620
7621              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7622               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7623               (Feld[x][y + 1] == EL_BLOCKED)))
7624     {
7625       /* this is needed for a special case not covered by calling "Impact()"
7626          from "ContinueMoving()": if an element moves to a tile directly below
7627          another element which was just falling on that tile (which was empty
7628          in the previous frame), the falling element above would just stop
7629          instead of smashing the element below (in previous version, the above
7630          element was just checked for "moving" instead of "falling", resulting
7631          in incorrect smashes caused by horizontal movement of the above
7632          element; also, the case of the player being the element to smash was
7633          simply not covered here... :-/ ) */
7634
7635       CheckCollision[x][y] = 0;
7636       CheckImpact[x][y] = 0;
7637
7638       Impact(x, y);
7639     }
7640     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7641     {
7642       if (MovDir[x][y] == MV_NONE)
7643       {
7644         InitMovingField(x, y, MV_DOWN);
7645         started_moving = TRUE;
7646       }
7647     }
7648     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7649     {
7650       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7651         MovDir[x][y] = MV_DOWN;
7652
7653       InitMovingField(x, y, MV_DOWN);
7654       started_moving = TRUE;
7655     }
7656     else if (element == EL_AMOEBA_DROP)
7657     {
7658       Feld[x][y] = EL_AMOEBA_GROWING;
7659       Store[x][y] = EL_AMOEBA_WET;
7660     }
7661     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7662               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7663              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7664              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7665     {
7666       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7667                                 (IS_FREE(x - 1, y + 1) ||
7668                                  Feld[x - 1][y + 1] == EL_ACID));
7669       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7670                                 (IS_FREE(x + 1, y + 1) ||
7671                                  Feld[x + 1][y + 1] == EL_ACID));
7672       boolean can_fall_any  = (can_fall_left || can_fall_right);
7673       boolean can_fall_both = (can_fall_left && can_fall_right);
7674       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7675
7676       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7677       {
7678         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7679           can_fall_right = FALSE;
7680         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7681           can_fall_left = FALSE;
7682         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7683           can_fall_right = FALSE;
7684         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7685           can_fall_left = FALSE;
7686
7687         can_fall_any  = (can_fall_left || can_fall_right);
7688         can_fall_both = FALSE;
7689       }
7690
7691       if (can_fall_both)
7692       {
7693         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7694           can_fall_right = FALSE;       /* slip down on left side */
7695         else
7696           can_fall_left = !(can_fall_right = RND(2));
7697
7698         can_fall_both = FALSE;
7699       }
7700
7701       if (can_fall_any)
7702       {
7703         /* if not determined otherwise, prefer left side for slipping down */
7704         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7705         started_moving = TRUE;
7706       }
7707     }
7708     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7709     {
7710       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7711       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7712       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7713       int belt_dir = game.belt_dir[belt_nr];
7714
7715       if ((belt_dir == MV_LEFT  && left_is_free) ||
7716           (belt_dir == MV_RIGHT && right_is_free))
7717       {
7718         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7719
7720         InitMovingField(x, y, belt_dir);
7721         started_moving = TRUE;
7722
7723         Pushed[x][y] = TRUE;
7724         Pushed[nextx][y] = TRUE;
7725
7726         GfxAction[x][y] = ACTION_DEFAULT;
7727       }
7728       else
7729       {
7730         MovDir[x][y] = 0;       /* if element was moving, stop it */
7731       }
7732     }
7733   }
7734
7735   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7736   if (CAN_MOVE(element) && !started_moving)
7737   {
7738     int move_pattern = element_info[element].move_pattern;
7739     int newx, newy;
7740
7741     Moving2Blocked(x, y, &newx, &newy);
7742
7743     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7744       return;
7745
7746     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7747         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7748     {
7749       WasJustMoving[x][y] = 0;
7750       CheckCollision[x][y] = 0;
7751
7752       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7753
7754       if (Feld[x][y] != element)        /* element has changed */
7755         return;
7756     }
7757
7758     if (!MovDelay[x][y])        /* start new movement phase */
7759     {
7760       /* all objects that can change their move direction after each step
7761          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7762
7763       if (element != EL_YAMYAM &&
7764           element != EL_DARK_YAMYAM &&
7765           element != EL_PACMAN &&
7766           !(move_pattern & MV_ANY_DIRECTION) &&
7767           move_pattern != MV_TURNING_LEFT &&
7768           move_pattern != MV_TURNING_RIGHT &&
7769           move_pattern != MV_TURNING_LEFT_RIGHT &&
7770           move_pattern != MV_TURNING_RIGHT_LEFT &&
7771           move_pattern != MV_TURNING_RANDOM)
7772       {
7773         TurnRound(x, y);
7774
7775         if (MovDelay[x][y] && (element == EL_BUG ||
7776                                element == EL_SPACESHIP ||
7777                                element == EL_SP_SNIKSNAK ||
7778                                element == EL_SP_ELECTRON ||
7779                                element == EL_MOLE))
7780           TEST_DrawLevelField(x, y);
7781       }
7782     }
7783
7784     if (MovDelay[x][y])         /* wait some time before next movement */
7785     {
7786       MovDelay[x][y]--;
7787
7788       if (element == EL_ROBOT ||
7789           element == EL_YAMYAM ||
7790           element == EL_DARK_YAMYAM)
7791       {
7792         DrawLevelElementAnimationIfNeeded(x, y, element);
7793         PlayLevelSoundAction(x, y, ACTION_WAITING);
7794       }
7795       else if (element == EL_SP_ELECTRON)
7796         DrawLevelElementAnimationIfNeeded(x, y, element);
7797       else if (element == EL_DRAGON)
7798       {
7799         int i;
7800         int dir = MovDir[x][y];
7801         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7802         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7803         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7804                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7805                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7806                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7807         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7808
7809         GfxAction[x][y] = ACTION_ATTACKING;
7810
7811         if (IS_PLAYER(x, y))
7812           DrawPlayerField(x, y);
7813         else
7814           TEST_DrawLevelField(x, y);
7815
7816         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7817
7818         for (i = 1; i <= 3; i++)
7819         {
7820           int xx = x + i * dx;
7821           int yy = y + i * dy;
7822           int sx = SCREENX(xx);
7823           int sy = SCREENY(yy);
7824           int flame_graphic = graphic + (i - 1);
7825
7826           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7827             break;
7828
7829           if (MovDelay[x][y])
7830           {
7831             int flamed = MovingOrBlocked2Element(xx, yy);
7832
7833             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7834               Bang(xx, yy);
7835             else
7836               RemoveMovingField(xx, yy);
7837
7838             ChangeDelay[xx][yy] = 0;
7839
7840             Feld[xx][yy] = EL_FLAMES;
7841
7842             if (IN_SCR_FIELD(sx, sy))
7843             {
7844               TEST_DrawLevelFieldCrumbled(xx, yy);
7845               DrawGraphic(sx, sy, flame_graphic, frame);
7846             }
7847           }
7848           else
7849           {
7850             if (Feld[xx][yy] == EL_FLAMES)
7851               Feld[xx][yy] = EL_EMPTY;
7852             TEST_DrawLevelField(xx, yy);
7853           }
7854         }
7855       }
7856
7857       if (MovDelay[x][y])       /* element still has to wait some time */
7858       {
7859         PlayLevelSoundAction(x, y, ACTION_WAITING);
7860
7861         return;
7862       }
7863     }
7864
7865     /* now make next step */
7866
7867     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7868
7869     if (DONT_COLLIDE_WITH(element) &&
7870         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7871         !PLAYER_ENEMY_PROTECTED(newx, newy))
7872     {
7873       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7874
7875       return;
7876     }
7877
7878     else if (CAN_MOVE_INTO_ACID(element) &&
7879              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7880              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7881              (MovDir[x][y] == MV_DOWN ||
7882               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7883     {
7884       SplashAcid(newx, newy);
7885       Store[x][y] = EL_ACID;
7886     }
7887     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7888     {
7889       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7890           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7891           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7892           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7893       {
7894         RemoveField(x, y);
7895         TEST_DrawLevelField(x, y);
7896
7897         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7898         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7899           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7900
7901         local_player->friends_still_needed--;
7902         if (!local_player->friends_still_needed &&
7903             !local_player->GameOver && AllPlayersGone)
7904           PlayerWins(local_player);
7905
7906         return;
7907       }
7908       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7909       {
7910         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7911           TEST_DrawLevelField(newx, newy);
7912         else
7913           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7914       }
7915       else if (!IS_FREE(newx, newy))
7916       {
7917         GfxAction[x][y] = ACTION_WAITING;
7918
7919         if (IS_PLAYER(x, y))
7920           DrawPlayerField(x, y);
7921         else
7922           TEST_DrawLevelField(x, y);
7923
7924         return;
7925       }
7926     }
7927     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7928     {
7929       if (IS_FOOD_PIG(Feld[newx][newy]))
7930       {
7931         if (IS_MOVING(newx, newy))
7932           RemoveMovingField(newx, newy);
7933         else
7934         {
7935           Feld[newx][newy] = EL_EMPTY;
7936           TEST_DrawLevelField(newx, newy);
7937         }
7938
7939         PlayLevelSound(x, y, SND_PIG_DIGGING);
7940       }
7941       else if (!IS_FREE(newx, newy))
7942       {
7943         if (IS_PLAYER(x, y))
7944           DrawPlayerField(x, y);
7945         else
7946           TEST_DrawLevelField(x, y);
7947
7948         return;
7949       }
7950     }
7951     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7952     {
7953       if (Store[x][y] != EL_EMPTY)
7954       {
7955         boolean can_clone = FALSE;
7956         int xx, yy;
7957
7958         /* check if element to clone is still there */
7959         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7960         {
7961           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7962           {
7963             can_clone = TRUE;
7964
7965             break;
7966           }
7967         }
7968
7969         /* cannot clone or target field not free anymore -- do not clone */
7970         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7971           Store[x][y] = EL_EMPTY;
7972       }
7973
7974       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7975       {
7976         if (IS_MV_DIAGONAL(MovDir[x][y]))
7977         {
7978           int diagonal_move_dir = MovDir[x][y];
7979           int stored = Store[x][y];
7980           int change_delay = 8;
7981           int graphic;
7982
7983           /* android is moving diagonally */
7984
7985           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7986
7987           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7988           GfxElement[x][y] = EL_EMC_ANDROID;
7989           GfxAction[x][y] = ACTION_SHRINKING;
7990           GfxDir[x][y] = diagonal_move_dir;
7991           ChangeDelay[x][y] = change_delay;
7992
7993           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7994                                    GfxDir[x][y]);
7995
7996           DrawLevelGraphicAnimation(x, y, graphic);
7997           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7998
7999           if (Feld[newx][newy] == EL_ACID)
8000           {
8001             SplashAcid(newx, newy);
8002
8003             return;
8004           }
8005
8006           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8007
8008           Store[newx][newy] = EL_EMC_ANDROID;
8009           GfxElement[newx][newy] = EL_EMC_ANDROID;
8010           GfxAction[newx][newy] = ACTION_GROWING;
8011           GfxDir[newx][newy] = diagonal_move_dir;
8012           ChangeDelay[newx][newy] = change_delay;
8013
8014           graphic = el_act_dir2img(GfxElement[newx][newy],
8015                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8016
8017           DrawLevelGraphicAnimation(newx, newy, graphic);
8018           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8019
8020           return;
8021         }
8022         else
8023         {
8024           Feld[newx][newy] = EL_EMPTY;
8025           TEST_DrawLevelField(newx, newy);
8026
8027           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8028         }
8029       }
8030       else if (!IS_FREE(newx, newy))
8031       {
8032         return;
8033       }
8034     }
8035     else if (IS_CUSTOM_ELEMENT(element) &&
8036              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8037     {
8038       if (!DigFieldByCE(newx, newy, element))
8039         return;
8040
8041       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8042       {
8043         RunnerVisit[x][y] = FrameCounter;
8044         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8045       }
8046     }
8047     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8048     {
8049       if (!IS_FREE(newx, newy))
8050       {
8051         if (IS_PLAYER(x, y))
8052           DrawPlayerField(x, y);
8053         else
8054           TEST_DrawLevelField(x, y);
8055
8056         return;
8057       }
8058       else
8059       {
8060         boolean wanna_flame = !RND(10);
8061         int dx = newx - x, dy = newy - y;
8062         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8063         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8064         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8065                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8066         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8067                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8068
8069         if ((wanna_flame ||
8070              IS_CLASSIC_ENEMY(element1) ||
8071              IS_CLASSIC_ENEMY(element2)) &&
8072             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8073             element1 != EL_FLAMES && element2 != EL_FLAMES)
8074         {
8075           ResetGfxAnimation(x, y);
8076           GfxAction[x][y] = ACTION_ATTACKING;
8077
8078           if (IS_PLAYER(x, y))
8079             DrawPlayerField(x, y);
8080           else
8081             TEST_DrawLevelField(x, y);
8082
8083           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8084
8085           MovDelay[x][y] = 50;
8086
8087           Feld[newx][newy] = EL_FLAMES;
8088           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8089             Feld[newx1][newy1] = EL_FLAMES;
8090           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8091             Feld[newx2][newy2] = EL_FLAMES;
8092
8093           return;
8094         }
8095       }
8096     }
8097     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8098              Feld[newx][newy] == EL_DIAMOND)
8099     {
8100       if (IS_MOVING(newx, newy))
8101         RemoveMovingField(newx, newy);
8102       else
8103       {
8104         Feld[newx][newy] = EL_EMPTY;
8105         TEST_DrawLevelField(newx, newy);
8106       }
8107
8108       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8109     }
8110     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8111              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8112     {
8113       if (AmoebaNr[newx][newy])
8114       {
8115         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8116         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8117             Feld[newx][newy] == EL_BD_AMOEBA)
8118           AmoebaCnt[AmoebaNr[newx][newy]]--;
8119       }
8120
8121       if (IS_MOVING(newx, newy))
8122       {
8123         RemoveMovingField(newx, newy);
8124       }
8125       else
8126       {
8127         Feld[newx][newy] = EL_EMPTY;
8128         TEST_DrawLevelField(newx, newy);
8129       }
8130
8131       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8132     }
8133     else if ((element == EL_PACMAN || element == EL_MOLE)
8134              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8135     {
8136       if (AmoebaNr[newx][newy])
8137       {
8138         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8139         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8140             Feld[newx][newy] == EL_BD_AMOEBA)
8141           AmoebaCnt[AmoebaNr[newx][newy]]--;
8142       }
8143
8144       if (element == EL_MOLE)
8145       {
8146         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8147         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8148
8149         ResetGfxAnimation(x, y);
8150         GfxAction[x][y] = ACTION_DIGGING;
8151         TEST_DrawLevelField(x, y);
8152
8153         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8154
8155         return;                         /* wait for shrinking amoeba */
8156       }
8157       else      /* element == EL_PACMAN */
8158       {
8159         Feld[newx][newy] = EL_EMPTY;
8160         TEST_DrawLevelField(newx, newy);
8161         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8162       }
8163     }
8164     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8165              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8166               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8167     {
8168       /* wait for shrinking amoeba to completely disappear */
8169       return;
8170     }
8171     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8172     {
8173       /* object was running against a wall */
8174
8175       TurnRound(x, y);
8176
8177       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8178         DrawLevelElementAnimation(x, y, element);
8179
8180       if (DONT_TOUCH(element))
8181         TestIfBadThingTouchesPlayer(x, y);
8182
8183       return;
8184     }
8185
8186     InitMovingField(x, y, MovDir[x][y]);
8187
8188     PlayLevelSoundAction(x, y, ACTION_MOVING);
8189   }
8190
8191   if (MovDir[x][y])
8192     ContinueMoving(x, y);
8193 }
8194
8195 void ContinueMoving(int x, int y)
8196 {
8197   int element = Feld[x][y];
8198   struct ElementInfo *ei = &element_info[element];
8199   int direction = MovDir[x][y];
8200   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8201   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8202   int newx = x + dx, newy = y + dy;
8203   int stored = Store[x][y];
8204   int stored_new = Store[newx][newy];
8205   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8206   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8207   boolean last_line = (newy == lev_fieldy - 1);
8208
8209   MovPos[x][y] += getElementMoveStepsize(x, y);
8210
8211   if (pushed_by_player) /* special case: moving object pushed by player */
8212     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8213
8214   if (ABS(MovPos[x][y]) < TILEX)
8215   {
8216     TEST_DrawLevelField(x, y);
8217
8218     return;     /* element is still moving */
8219   }
8220
8221   /* element reached destination field */
8222
8223   Feld[x][y] = EL_EMPTY;
8224   Feld[newx][newy] = element;
8225   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8226
8227   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8228   {
8229     element = Feld[newx][newy] = EL_ACID;
8230   }
8231   else if (element == EL_MOLE)
8232   {
8233     Feld[x][y] = EL_SAND;
8234
8235     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8236   }
8237   else if (element == EL_QUICKSAND_FILLING)
8238   {
8239     element = Feld[newx][newy] = get_next_element(element);
8240     Store[newx][newy] = Store[x][y];
8241   }
8242   else if (element == EL_QUICKSAND_EMPTYING)
8243   {
8244     Feld[x][y] = get_next_element(element);
8245     element = Feld[newx][newy] = Store[x][y];
8246   }
8247   else if (element == EL_QUICKSAND_FAST_FILLING)
8248   {
8249     element = Feld[newx][newy] = get_next_element(element);
8250     Store[newx][newy] = Store[x][y];
8251   }
8252   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8253   {
8254     Feld[x][y] = get_next_element(element);
8255     element = Feld[newx][newy] = Store[x][y];
8256   }
8257   else if (element == EL_MAGIC_WALL_FILLING)
8258   {
8259     element = Feld[newx][newy] = get_next_element(element);
8260     if (!game.magic_wall_active)
8261       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8262     Store[newx][newy] = Store[x][y];
8263   }
8264   else if (element == EL_MAGIC_WALL_EMPTYING)
8265   {
8266     Feld[x][y] = get_next_element(element);
8267     if (!game.magic_wall_active)
8268       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8269     element = Feld[newx][newy] = Store[x][y];
8270
8271     InitField(newx, newy, FALSE);
8272   }
8273   else if (element == EL_BD_MAGIC_WALL_FILLING)
8274   {
8275     element = Feld[newx][newy] = get_next_element(element);
8276     if (!game.magic_wall_active)
8277       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8278     Store[newx][newy] = Store[x][y];
8279   }
8280   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8281   {
8282     Feld[x][y] = get_next_element(element);
8283     if (!game.magic_wall_active)
8284       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8285     element = Feld[newx][newy] = Store[x][y];
8286
8287     InitField(newx, newy, FALSE);
8288   }
8289   else if (element == EL_DC_MAGIC_WALL_FILLING)
8290   {
8291     element = Feld[newx][newy] = get_next_element(element);
8292     if (!game.magic_wall_active)
8293       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8294     Store[newx][newy] = Store[x][y];
8295   }
8296   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8297   {
8298     Feld[x][y] = get_next_element(element);
8299     if (!game.magic_wall_active)
8300       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8301     element = Feld[newx][newy] = Store[x][y];
8302
8303     InitField(newx, newy, FALSE);
8304   }
8305   else if (element == EL_AMOEBA_DROPPING)
8306   {
8307     Feld[x][y] = get_next_element(element);
8308     element = Feld[newx][newy] = Store[x][y];
8309   }
8310   else if (element == EL_SOKOBAN_OBJECT)
8311   {
8312     if (Back[x][y])
8313       Feld[x][y] = Back[x][y];
8314
8315     if (Back[newx][newy])
8316       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8317
8318     Back[x][y] = Back[newx][newy] = 0;
8319   }
8320
8321   Store[x][y] = EL_EMPTY;
8322   MovPos[x][y] = 0;
8323   MovDir[x][y] = 0;
8324   MovDelay[x][y] = 0;
8325
8326   MovDelay[newx][newy] = 0;
8327
8328   if (CAN_CHANGE_OR_HAS_ACTION(element))
8329   {
8330     /* copy element change control values to new field */
8331     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8332     ChangePage[newx][newy]  = ChangePage[x][y];
8333     ChangeCount[newx][newy] = ChangeCount[x][y];
8334     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8335   }
8336
8337   CustomValue[newx][newy] = CustomValue[x][y];
8338
8339   ChangeDelay[x][y] = 0;
8340   ChangePage[x][y] = -1;
8341   ChangeCount[x][y] = 0;
8342   ChangeEvent[x][y] = -1;
8343
8344   CustomValue[x][y] = 0;
8345
8346   /* copy animation control values to new field */
8347   GfxFrame[newx][newy]  = GfxFrame[x][y];
8348   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8349   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8350   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8351
8352   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8353
8354   /* some elements can leave other elements behind after moving */
8355   if (ei->move_leave_element != EL_EMPTY &&
8356       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8357       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8358   {
8359     int move_leave_element = ei->move_leave_element;
8360
8361     /* this makes it possible to leave the removed element again */
8362     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8363       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8364
8365     Feld[x][y] = move_leave_element;
8366
8367     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8368       MovDir[x][y] = direction;
8369
8370     InitField(x, y, FALSE);
8371
8372     if (GFX_CRUMBLED(Feld[x][y]))
8373       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8374
8375     if (ELEM_IS_PLAYER(move_leave_element))
8376       RelocatePlayer(x, y, move_leave_element);
8377   }
8378
8379   /* do this after checking for left-behind element */
8380   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8381
8382   if (!CAN_MOVE(element) ||
8383       (CAN_FALL(element) && direction == MV_DOWN &&
8384        (element == EL_SPRING ||
8385         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8386         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8387     GfxDir[x][y] = MovDir[newx][newy] = 0;
8388
8389   TEST_DrawLevelField(x, y);
8390   TEST_DrawLevelField(newx, newy);
8391
8392   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8393
8394   /* prevent pushed element from moving on in pushed direction */
8395   if (pushed_by_player && CAN_MOVE(element) &&
8396       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8397       !(element_info[element].move_pattern & direction))
8398     TurnRound(newx, newy);
8399
8400   /* prevent elements on conveyor belt from moving on in last direction */
8401   if (pushed_by_conveyor && CAN_FALL(element) &&
8402       direction & MV_HORIZONTAL)
8403     MovDir[newx][newy] = 0;
8404
8405   if (!pushed_by_player)
8406   {
8407     int nextx = newx + dx, nexty = newy + dy;
8408     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8409
8410     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8411
8412     if (CAN_FALL(element) && direction == MV_DOWN)
8413       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8414
8415     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8416       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8417
8418     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8419       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8420   }
8421
8422   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8423   {
8424     TestIfBadThingTouchesPlayer(newx, newy);
8425     TestIfBadThingTouchesFriend(newx, newy);
8426
8427     if (!IS_CUSTOM_ELEMENT(element))
8428       TestIfBadThingTouchesOtherBadThing(newx, newy);
8429   }
8430   else if (element == EL_PENGUIN)
8431     TestIfFriendTouchesBadThing(newx, newy);
8432
8433   if (DONT_GET_HIT_BY(element))
8434   {
8435     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8436   }
8437
8438   /* give the player one last chance (one more frame) to move away */
8439   if (CAN_FALL(element) && direction == MV_DOWN &&
8440       (last_line || (!IS_FREE(x, newy + 1) &&
8441                      (!IS_PLAYER(x, newy + 1) ||
8442                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8443     Impact(x, newy);
8444
8445   if (pushed_by_player && !game.use_change_when_pushing_bug)
8446   {
8447     int push_side = MV_DIR_OPPOSITE(direction);
8448     struct PlayerInfo *player = PLAYERINFO(x, y);
8449
8450     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8451                                player->index_bit, push_side);
8452     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8453                                         player->index_bit, push_side);
8454   }
8455
8456   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8457     MovDelay[newx][newy] = 1;
8458
8459   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8460
8461   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8462   TestIfElementHitsCustomElement(newx, newy, direction);
8463   TestIfPlayerTouchesCustomElement(newx, newy);
8464   TestIfElementTouchesCustomElement(newx, newy);
8465
8466   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8467       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8468     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8469                              MV_DIR_OPPOSITE(direction));
8470 }
8471
8472 int AmoebeNachbarNr(int ax, int ay)
8473 {
8474   int i;
8475   int element = Feld[ax][ay];
8476   int group_nr = 0;
8477   static int xy[4][2] =
8478   {
8479     { 0, -1 },
8480     { -1, 0 },
8481     { +1, 0 },
8482     { 0, +1 }
8483   };
8484
8485   for (i = 0; i < NUM_DIRECTIONS; i++)
8486   {
8487     int x = ax + xy[i][0];
8488     int y = ay + xy[i][1];
8489
8490     if (!IN_LEV_FIELD(x, y))
8491       continue;
8492
8493     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8494       group_nr = AmoebaNr[x][y];
8495   }
8496
8497   return group_nr;
8498 }
8499
8500 void AmoebenVereinigen(int ax, int ay)
8501 {
8502   int i, x, y, xx, yy;
8503   int new_group_nr = AmoebaNr[ax][ay];
8504   static int xy[4][2] =
8505   {
8506     { 0, -1 },
8507     { -1, 0 },
8508     { +1, 0 },
8509     { 0, +1 }
8510   };
8511
8512   if (new_group_nr == 0)
8513     return;
8514
8515   for (i = 0; i < NUM_DIRECTIONS; i++)
8516   {
8517     x = ax + xy[i][0];
8518     y = ay + xy[i][1];
8519
8520     if (!IN_LEV_FIELD(x, y))
8521       continue;
8522
8523     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8524          Feld[x][y] == EL_BD_AMOEBA ||
8525          Feld[x][y] == EL_AMOEBA_DEAD) &&
8526         AmoebaNr[x][y] != new_group_nr)
8527     {
8528       int old_group_nr = AmoebaNr[x][y];
8529
8530       if (old_group_nr == 0)
8531         return;
8532
8533       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8534       AmoebaCnt[old_group_nr] = 0;
8535       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8536       AmoebaCnt2[old_group_nr] = 0;
8537
8538       SCAN_PLAYFIELD(xx, yy)
8539       {
8540         if (AmoebaNr[xx][yy] == old_group_nr)
8541           AmoebaNr[xx][yy] = new_group_nr;
8542       }
8543     }
8544   }
8545 }
8546
8547 void AmoebeUmwandeln(int ax, int ay)
8548 {
8549   int i, x, y;
8550
8551   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8552   {
8553     int group_nr = AmoebaNr[ax][ay];
8554
8555 #ifdef DEBUG
8556     if (group_nr == 0)
8557     {
8558       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8559       printf("AmoebeUmwandeln(): This should never happen!\n");
8560       return;
8561     }
8562 #endif
8563
8564     SCAN_PLAYFIELD(x, y)
8565     {
8566       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8567       {
8568         AmoebaNr[x][y] = 0;
8569         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8570       }
8571     }
8572
8573     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8574                             SND_AMOEBA_TURNING_TO_GEM :
8575                             SND_AMOEBA_TURNING_TO_ROCK));
8576     Bang(ax, ay);
8577   }
8578   else
8579   {
8580     static int xy[4][2] =
8581     {
8582       { 0, -1 },
8583       { -1, 0 },
8584       { +1, 0 },
8585       { 0, +1 }
8586     };
8587
8588     for (i = 0; i < NUM_DIRECTIONS; i++)
8589     {
8590       x = ax + xy[i][0];
8591       y = ay + xy[i][1];
8592
8593       if (!IN_LEV_FIELD(x, y))
8594         continue;
8595
8596       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8597       {
8598         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8599                               SND_AMOEBA_TURNING_TO_GEM :
8600                               SND_AMOEBA_TURNING_TO_ROCK));
8601         Bang(x, y);
8602       }
8603     }
8604   }
8605 }
8606
8607 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8608 {
8609   int x, y;
8610   int group_nr = AmoebaNr[ax][ay];
8611   boolean done = FALSE;
8612
8613 #ifdef DEBUG
8614   if (group_nr == 0)
8615   {
8616     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8617     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8618     return;
8619   }
8620 #endif
8621
8622   SCAN_PLAYFIELD(x, y)
8623   {
8624     if (AmoebaNr[x][y] == group_nr &&
8625         (Feld[x][y] == EL_AMOEBA_DEAD ||
8626          Feld[x][y] == EL_BD_AMOEBA ||
8627          Feld[x][y] == EL_AMOEBA_GROWING))
8628     {
8629       AmoebaNr[x][y] = 0;
8630       Feld[x][y] = new_element;
8631       InitField(x, y, FALSE);
8632       TEST_DrawLevelField(x, y);
8633       done = TRUE;
8634     }
8635   }
8636
8637   if (done)
8638     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8639                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8640                             SND_BD_AMOEBA_TURNING_TO_GEM));
8641 }
8642
8643 void AmoebeWaechst(int x, int y)
8644 {
8645   static unsigned int sound_delay = 0;
8646   static unsigned int sound_delay_value = 0;
8647
8648   if (!MovDelay[x][y])          /* start new growing cycle */
8649   {
8650     MovDelay[x][y] = 7;
8651
8652     if (DelayReached(&sound_delay, sound_delay_value))
8653     {
8654       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8655       sound_delay_value = 30;
8656     }
8657   }
8658
8659   if (MovDelay[x][y])           /* wait some time before growing bigger */
8660   {
8661     MovDelay[x][y]--;
8662     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8663     {
8664       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8665                                            6 - MovDelay[x][y]);
8666
8667       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8668     }
8669
8670     if (!MovDelay[x][y])
8671     {
8672       Feld[x][y] = Store[x][y];
8673       Store[x][y] = 0;
8674       TEST_DrawLevelField(x, y);
8675     }
8676   }
8677 }
8678
8679 void AmoebaDisappearing(int x, int y)
8680 {
8681   static unsigned int sound_delay = 0;
8682   static unsigned int sound_delay_value = 0;
8683
8684   if (!MovDelay[x][y])          /* start new shrinking cycle */
8685   {
8686     MovDelay[x][y] = 7;
8687
8688     if (DelayReached(&sound_delay, sound_delay_value))
8689       sound_delay_value = 30;
8690   }
8691
8692   if (MovDelay[x][y])           /* wait some time before shrinking */
8693   {
8694     MovDelay[x][y]--;
8695     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8696     {
8697       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8698                                            6 - MovDelay[x][y]);
8699
8700       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8701     }
8702
8703     if (!MovDelay[x][y])
8704     {
8705       Feld[x][y] = EL_EMPTY;
8706       TEST_DrawLevelField(x, y);
8707
8708       /* don't let mole enter this field in this cycle;
8709          (give priority to objects falling to this field from above) */
8710       Stop[x][y] = TRUE;
8711     }
8712   }
8713 }
8714
8715 void AmoebeAbleger(int ax, int ay)
8716 {
8717   int i;
8718   int element = Feld[ax][ay];
8719   int graphic = el2img(element);
8720   int newax = ax, neway = ay;
8721   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8722   static int xy[4][2] =
8723   {
8724     { 0, -1 },
8725     { -1, 0 },
8726     { +1, 0 },
8727     { 0, +1 }
8728   };
8729
8730   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8731   {
8732     Feld[ax][ay] = EL_AMOEBA_DEAD;
8733     TEST_DrawLevelField(ax, ay);
8734     return;
8735   }
8736
8737   if (IS_ANIMATED(graphic))
8738     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8739
8740   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8741     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8742
8743   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8744   {
8745     MovDelay[ax][ay]--;
8746     if (MovDelay[ax][ay])
8747       return;
8748   }
8749
8750   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8751   {
8752     int start = RND(4);
8753     int x = ax + xy[start][0];
8754     int y = ay + xy[start][1];
8755
8756     if (!IN_LEV_FIELD(x, y))
8757       return;
8758
8759     if (IS_FREE(x, y) ||
8760         CAN_GROW_INTO(Feld[x][y]) ||
8761         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8762         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8763     {
8764       newax = x;
8765       neway = y;
8766     }
8767
8768     if (newax == ax && neway == ay)
8769       return;
8770   }
8771   else                          /* normal or "filled" (BD style) amoeba */
8772   {
8773     int start = RND(4);
8774     boolean waiting_for_player = FALSE;
8775
8776     for (i = 0; i < NUM_DIRECTIONS; i++)
8777     {
8778       int j = (start + i) % 4;
8779       int x = ax + xy[j][0];
8780       int y = ay + xy[j][1];
8781
8782       if (!IN_LEV_FIELD(x, y))
8783         continue;
8784
8785       if (IS_FREE(x, y) ||
8786           CAN_GROW_INTO(Feld[x][y]) ||
8787           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8788           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8789       {
8790         newax = x;
8791         neway = y;
8792         break;
8793       }
8794       else if (IS_PLAYER(x, y))
8795         waiting_for_player = TRUE;
8796     }
8797
8798     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8799     {
8800       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8801       {
8802         Feld[ax][ay] = EL_AMOEBA_DEAD;
8803         TEST_DrawLevelField(ax, ay);
8804         AmoebaCnt[AmoebaNr[ax][ay]]--;
8805
8806         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8807         {
8808           if (element == EL_AMOEBA_FULL)
8809             AmoebeUmwandeln(ax, ay);
8810           else if (element == EL_BD_AMOEBA)
8811             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8812         }
8813       }
8814       return;
8815     }
8816     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8817     {
8818       /* amoeba gets larger by growing in some direction */
8819
8820       int new_group_nr = AmoebaNr[ax][ay];
8821
8822 #ifdef DEBUG
8823   if (new_group_nr == 0)
8824   {
8825     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8826     printf("AmoebeAbleger(): This should never happen!\n");
8827     return;
8828   }
8829 #endif
8830
8831       AmoebaNr[newax][neway] = new_group_nr;
8832       AmoebaCnt[new_group_nr]++;
8833       AmoebaCnt2[new_group_nr]++;
8834
8835       /* if amoeba touches other amoeba(s) after growing, unify them */
8836       AmoebenVereinigen(newax, neway);
8837
8838       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8839       {
8840         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8841         return;
8842       }
8843     }
8844   }
8845
8846   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8847       (neway == lev_fieldy - 1 && newax != ax))
8848   {
8849     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8850     Store[newax][neway] = element;
8851   }
8852   else if (neway == ay || element == EL_EMC_DRIPPER)
8853   {
8854     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8855
8856     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8857   }
8858   else
8859   {
8860     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8861     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8862     Store[ax][ay] = EL_AMOEBA_DROP;
8863     ContinueMoving(ax, ay);
8864     return;
8865   }
8866
8867   TEST_DrawLevelField(newax, neway);
8868 }
8869
8870 void Life(int ax, int ay)
8871 {
8872   int x1, y1, x2, y2;
8873   int life_time = 40;
8874   int element = Feld[ax][ay];
8875   int graphic = el2img(element);
8876   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8877                          level.biomaze);
8878   boolean changed = FALSE;
8879
8880   if (IS_ANIMATED(graphic))
8881     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8882
8883   if (Stop[ax][ay])
8884     return;
8885
8886   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8887     MovDelay[ax][ay] = life_time;
8888
8889   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8890   {
8891     MovDelay[ax][ay]--;
8892     if (MovDelay[ax][ay])
8893       return;
8894   }
8895
8896   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8897   {
8898     int xx = ax+x1, yy = ay+y1;
8899     int nachbarn = 0;
8900
8901     if (!IN_LEV_FIELD(xx, yy))
8902       continue;
8903
8904     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8905     {
8906       int x = xx+x2, y = yy+y2;
8907
8908       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8909         continue;
8910
8911       if (((Feld[x][y] == element ||
8912             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8913            !Stop[x][y]) ||
8914           (IS_FREE(x, y) && Stop[x][y]))
8915         nachbarn++;
8916     }
8917
8918     if (xx == ax && yy == ay)           /* field in the middle */
8919     {
8920       if (nachbarn < life_parameter[0] ||
8921           nachbarn > life_parameter[1])
8922       {
8923         Feld[xx][yy] = EL_EMPTY;
8924         if (!Stop[xx][yy])
8925           TEST_DrawLevelField(xx, yy);
8926         Stop[xx][yy] = TRUE;
8927         changed = TRUE;
8928       }
8929     }
8930     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8931     {                                   /* free border field */
8932       if (nachbarn >= life_parameter[2] &&
8933           nachbarn <= life_parameter[3])
8934       {
8935         Feld[xx][yy] = element;
8936         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8937         if (!Stop[xx][yy])
8938           TEST_DrawLevelField(xx, yy);
8939         Stop[xx][yy] = TRUE;
8940         changed = TRUE;
8941       }
8942     }
8943   }
8944
8945   if (changed)
8946     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8947                    SND_GAME_OF_LIFE_GROWING);
8948 }
8949
8950 static void InitRobotWheel(int x, int y)
8951 {
8952   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8953 }
8954
8955 static void RunRobotWheel(int x, int y)
8956 {
8957   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8958 }
8959
8960 static void StopRobotWheel(int x, int y)
8961 {
8962   if (ZX == x && ZY == y)
8963   {
8964     ZX = ZY = -1;
8965
8966     game.robot_wheel_active = FALSE;
8967   }
8968 }
8969
8970 static void InitTimegateWheel(int x, int y)
8971 {
8972   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8973 }
8974
8975 static void RunTimegateWheel(int x, int y)
8976 {
8977   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8978 }
8979
8980 static void InitMagicBallDelay(int x, int y)
8981 {
8982   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8983 }
8984
8985 static void ActivateMagicBall(int bx, int by)
8986 {
8987   int x, y;
8988
8989   if (level.ball_random)
8990   {
8991     int pos_border = RND(8);    /* select one of the eight border elements */
8992     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8993     int xx = pos_content % 3;
8994     int yy = pos_content / 3;
8995
8996     x = bx - 1 + xx;
8997     y = by - 1 + yy;
8998
8999     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9000       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9001   }
9002   else
9003   {
9004     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9005     {
9006       int xx = x - bx + 1;
9007       int yy = y - by + 1;
9008
9009       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9010         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9011     }
9012   }
9013
9014   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9015 }
9016
9017 void CheckExit(int x, int y)
9018 {
9019   if (local_player->gems_still_needed > 0 ||
9020       local_player->sokobanfields_still_needed > 0 ||
9021       local_player->lights_still_needed > 0)
9022   {
9023     int element = Feld[x][y];
9024     int graphic = el2img(element);
9025
9026     if (IS_ANIMATED(graphic))
9027       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9028
9029     return;
9030   }
9031
9032   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9033     return;
9034
9035   Feld[x][y] = EL_EXIT_OPENING;
9036
9037   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9038 }
9039
9040 void CheckExitEM(int x, int y)
9041 {
9042   if (local_player->gems_still_needed > 0 ||
9043       local_player->sokobanfields_still_needed > 0 ||
9044       local_player->lights_still_needed > 0)
9045   {
9046     int element = Feld[x][y];
9047     int graphic = el2img(element);
9048
9049     if (IS_ANIMATED(graphic))
9050       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9051
9052     return;
9053   }
9054
9055   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9056     return;
9057
9058   Feld[x][y] = EL_EM_EXIT_OPENING;
9059
9060   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9061 }
9062
9063 void CheckExitSteel(int x, int y)
9064 {
9065   if (local_player->gems_still_needed > 0 ||
9066       local_player->sokobanfields_still_needed > 0 ||
9067       local_player->lights_still_needed > 0)
9068   {
9069     int element = Feld[x][y];
9070     int graphic = el2img(element);
9071
9072     if (IS_ANIMATED(graphic))
9073       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9074
9075     return;
9076   }
9077
9078   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9079     return;
9080
9081   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9082
9083   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9084 }
9085
9086 void CheckExitSteelEM(int x, int y)
9087 {
9088   if (local_player->gems_still_needed > 0 ||
9089       local_player->sokobanfields_still_needed > 0 ||
9090       local_player->lights_still_needed > 0)
9091   {
9092     int element = Feld[x][y];
9093     int graphic = el2img(element);
9094
9095     if (IS_ANIMATED(graphic))
9096       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9097
9098     return;
9099   }
9100
9101   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9102     return;
9103
9104   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9105
9106   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9107 }
9108
9109 void CheckExitSP(int x, int y)
9110 {
9111   if (local_player->gems_still_needed > 0)
9112   {
9113     int element = Feld[x][y];
9114     int graphic = el2img(element);
9115
9116     if (IS_ANIMATED(graphic))
9117       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9118
9119     return;
9120   }
9121
9122   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9123     return;
9124
9125   Feld[x][y] = EL_SP_EXIT_OPENING;
9126
9127   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9128 }
9129
9130 static void CloseAllOpenTimegates()
9131 {
9132   int x, y;
9133
9134   SCAN_PLAYFIELD(x, y)
9135   {
9136     int element = Feld[x][y];
9137
9138     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9139     {
9140       Feld[x][y] = EL_TIMEGATE_CLOSING;
9141
9142       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9143     }
9144   }
9145 }
9146
9147 void DrawTwinkleOnField(int x, int y)
9148 {
9149   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9150     return;
9151
9152   if (Feld[x][y] == EL_BD_DIAMOND)
9153     return;
9154
9155   if (MovDelay[x][y] == 0)      /* next animation frame */
9156     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9157
9158   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9159   {
9160     MovDelay[x][y]--;
9161
9162     DrawLevelElementAnimation(x, y, Feld[x][y]);
9163
9164     if (MovDelay[x][y] != 0)
9165     {
9166       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9167                                            10 - MovDelay[x][y]);
9168
9169       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9170     }
9171   }
9172 }
9173
9174 void MauerWaechst(int x, int y)
9175 {
9176   int delay = 6;
9177
9178   if (!MovDelay[x][y])          /* next animation frame */
9179     MovDelay[x][y] = 3 * delay;
9180
9181   if (MovDelay[x][y])           /* wait some time before next frame */
9182   {
9183     MovDelay[x][y]--;
9184
9185     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9186     {
9187       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9188       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9189
9190       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9191     }
9192
9193     if (!MovDelay[x][y])
9194     {
9195       if (MovDir[x][y] == MV_LEFT)
9196       {
9197         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9198           TEST_DrawLevelField(x - 1, y);
9199       }
9200       else if (MovDir[x][y] == MV_RIGHT)
9201       {
9202         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9203           TEST_DrawLevelField(x + 1, y);
9204       }
9205       else if (MovDir[x][y] == MV_UP)
9206       {
9207         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9208           TEST_DrawLevelField(x, y - 1);
9209       }
9210       else
9211       {
9212         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9213           TEST_DrawLevelField(x, y + 1);
9214       }
9215
9216       Feld[x][y] = Store[x][y];
9217       Store[x][y] = 0;
9218       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9219       TEST_DrawLevelField(x, y);
9220     }
9221   }
9222 }
9223
9224 void MauerAbleger(int ax, int ay)
9225 {
9226   int element = Feld[ax][ay];
9227   int graphic = el2img(element);
9228   boolean oben_frei = FALSE, unten_frei = FALSE;
9229   boolean links_frei = FALSE, rechts_frei = FALSE;
9230   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9231   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9232   boolean new_wall = FALSE;
9233
9234   if (IS_ANIMATED(graphic))
9235     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9236
9237   if (!MovDelay[ax][ay])        /* start building new wall */
9238     MovDelay[ax][ay] = 6;
9239
9240   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9241   {
9242     MovDelay[ax][ay]--;
9243     if (MovDelay[ax][ay])
9244       return;
9245   }
9246
9247   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9248     oben_frei = TRUE;
9249   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9250     unten_frei = TRUE;
9251   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9252     links_frei = TRUE;
9253   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9254     rechts_frei = TRUE;
9255
9256   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9257       element == EL_EXPANDABLE_WALL_ANY)
9258   {
9259     if (oben_frei)
9260     {
9261       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9262       Store[ax][ay-1] = element;
9263       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9264       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9265         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9266                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9267       new_wall = TRUE;
9268     }
9269     if (unten_frei)
9270     {
9271       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9272       Store[ax][ay+1] = element;
9273       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9274       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9275         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9276                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9277       new_wall = TRUE;
9278     }
9279   }
9280
9281   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9282       element == EL_EXPANDABLE_WALL_ANY ||
9283       element == EL_EXPANDABLE_WALL ||
9284       element == EL_BD_EXPANDABLE_WALL)
9285   {
9286     if (links_frei)
9287     {
9288       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9289       Store[ax-1][ay] = element;
9290       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9291       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9292         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9293                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9294       new_wall = TRUE;
9295     }
9296
9297     if (rechts_frei)
9298     {
9299       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9300       Store[ax+1][ay] = element;
9301       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9302       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9303         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9304                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9305       new_wall = TRUE;
9306     }
9307   }
9308
9309   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9310     TEST_DrawLevelField(ax, ay);
9311
9312   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9313     oben_massiv = TRUE;
9314   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9315     unten_massiv = TRUE;
9316   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9317     links_massiv = TRUE;
9318   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9319     rechts_massiv = TRUE;
9320
9321   if (((oben_massiv && unten_massiv) ||
9322        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9323        element == EL_EXPANDABLE_WALL) &&
9324       ((links_massiv && rechts_massiv) ||
9325        element == EL_EXPANDABLE_WALL_VERTICAL))
9326     Feld[ax][ay] = EL_WALL;
9327
9328   if (new_wall)
9329     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9330 }
9331
9332 void MauerAblegerStahl(int ax, int ay)
9333 {
9334   int element = Feld[ax][ay];
9335   int graphic = el2img(element);
9336   boolean oben_frei = FALSE, unten_frei = FALSE;
9337   boolean links_frei = FALSE, rechts_frei = FALSE;
9338   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9339   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9340   boolean new_wall = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (!MovDelay[ax][ay])        /* start building new wall */
9346     MovDelay[ax][ay] = 6;
9347
9348   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9349   {
9350     MovDelay[ax][ay]--;
9351     if (MovDelay[ax][ay])
9352       return;
9353   }
9354
9355   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9356     oben_frei = TRUE;
9357   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9358     unten_frei = TRUE;
9359   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9360     links_frei = TRUE;
9361   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9362     rechts_frei = TRUE;
9363
9364   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9365       element == EL_EXPANDABLE_STEELWALL_ANY)
9366   {
9367     if (oben_frei)
9368     {
9369       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9370       Store[ax][ay-1] = element;
9371       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9372       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9373         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9374                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9375       new_wall = TRUE;
9376     }
9377     if (unten_frei)
9378     {
9379       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9380       Store[ax][ay+1] = element;
9381       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9382       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9383         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9384                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9385       new_wall = TRUE;
9386     }
9387   }
9388
9389   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9390       element == EL_EXPANDABLE_STEELWALL_ANY)
9391   {
9392     if (links_frei)
9393     {
9394       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9395       Store[ax-1][ay] = element;
9396       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9397       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9398         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9399                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9400       new_wall = TRUE;
9401     }
9402
9403     if (rechts_frei)
9404     {
9405       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9406       Store[ax+1][ay] = element;
9407       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9408       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9409         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9410                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9411       new_wall = TRUE;
9412     }
9413   }
9414
9415   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9416     oben_massiv = TRUE;
9417   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9418     unten_massiv = TRUE;
9419   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9420     links_massiv = TRUE;
9421   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9422     rechts_massiv = TRUE;
9423
9424   if (((oben_massiv && unten_massiv) ||
9425        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9426       ((links_massiv && rechts_massiv) ||
9427        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9428     Feld[ax][ay] = EL_STEELWALL;
9429
9430   if (new_wall)
9431     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9432 }
9433
9434 void CheckForDragon(int x, int y)
9435 {
9436   int i, j;
9437   boolean dragon_found = FALSE;
9438   static int xy[4][2] =
9439   {
9440     { 0, -1 },
9441     { -1, 0 },
9442     { +1, 0 },
9443     { 0, +1 }
9444   };
9445
9446   for (i = 0; i < NUM_DIRECTIONS; i++)
9447   {
9448     for (j = 0; j < 4; j++)
9449     {
9450       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9451
9452       if (IN_LEV_FIELD(xx, yy) &&
9453           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9454       {
9455         if (Feld[xx][yy] == EL_DRAGON)
9456           dragon_found = TRUE;
9457       }
9458       else
9459         break;
9460     }
9461   }
9462
9463   if (!dragon_found)
9464   {
9465     for (i = 0; i < NUM_DIRECTIONS; i++)
9466     {
9467       for (j = 0; j < 3; j++)
9468       {
9469         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9470   
9471         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9472         {
9473           Feld[xx][yy] = EL_EMPTY;
9474           TEST_DrawLevelField(xx, yy);
9475         }
9476         else
9477           break;
9478       }
9479     }
9480   }
9481 }
9482
9483 static void InitBuggyBase(int x, int y)
9484 {
9485   int element = Feld[x][y];
9486   int activating_delay = FRAMES_PER_SECOND / 4;
9487
9488   ChangeDelay[x][y] =
9489     (element == EL_SP_BUGGY_BASE ?
9490      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9491      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9492      activating_delay :
9493      element == EL_SP_BUGGY_BASE_ACTIVE ?
9494      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9495 }
9496
9497 static void WarnBuggyBase(int x, int y)
9498 {
9499   int i;
9500   static int xy[4][2] =
9501   {
9502     { 0, -1 },
9503     { -1, 0 },
9504     { +1, 0 },
9505     { 0, +1 }
9506   };
9507
9508   for (i = 0; i < NUM_DIRECTIONS; i++)
9509   {
9510     int xx = x + xy[i][0];
9511     int yy = y + xy[i][1];
9512
9513     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9514     {
9515       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9516
9517       break;
9518     }
9519   }
9520 }
9521
9522 static void InitTrap(int x, int y)
9523 {
9524   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9525 }
9526
9527 static void ActivateTrap(int x, int y)
9528 {
9529   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9530 }
9531
9532 static void ChangeActiveTrap(int x, int y)
9533 {
9534   int graphic = IMG_TRAP_ACTIVE;
9535
9536   /* if new animation frame was drawn, correct crumbled sand border */
9537   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9538     TEST_DrawLevelFieldCrumbled(x, y);
9539 }
9540
9541 static int getSpecialActionElement(int element, int number, int base_element)
9542 {
9543   return (element != EL_EMPTY ? element :
9544           number != -1 ? base_element + number - 1 :
9545           EL_EMPTY);
9546 }
9547
9548 static int getModifiedActionNumber(int value_old, int operator, int operand,
9549                                    int value_min, int value_max)
9550 {
9551   int value_new = (operator == CA_MODE_SET      ? operand :
9552                    operator == CA_MODE_ADD      ? value_old + operand :
9553                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9554                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9555                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9556                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9557                    value_old);
9558
9559   return (value_new < value_min ? value_min :
9560           value_new > value_max ? value_max :
9561           value_new);
9562 }
9563
9564 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9565 {
9566   struct ElementInfo *ei = &element_info[element];
9567   struct ElementChangeInfo *change = &ei->change_page[page];
9568   int target_element = change->target_element;
9569   int action_type = change->action_type;
9570   int action_mode = change->action_mode;
9571   int action_arg = change->action_arg;
9572   int action_element = change->action_element;
9573   int i;
9574
9575   if (!change->has_action)
9576     return;
9577
9578   /* ---------- determine action paramater values -------------------------- */
9579
9580   int level_time_value =
9581     (level.time > 0 ? TimeLeft :
9582      TimePlayed);
9583
9584   int action_arg_element_raw =
9585     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9586      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9587      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9588      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9589      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9590      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9591      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9592      EL_EMPTY);
9593   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9594
9595   int action_arg_direction =
9596     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9597      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9598      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9599      change->actual_trigger_side :
9600      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9601      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9602      MV_NONE);
9603
9604   int action_arg_number_min =
9605     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9606      CA_ARG_MIN);
9607
9608   int action_arg_number_max =
9609     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9610      action_type == CA_SET_LEVEL_GEMS ? 999 :
9611      action_type == CA_SET_LEVEL_TIME ? 9999 :
9612      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9613      action_type == CA_SET_CE_VALUE ? 9999 :
9614      action_type == CA_SET_CE_SCORE ? 9999 :
9615      CA_ARG_MAX);
9616
9617   int action_arg_number_reset =
9618     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9619      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9620      action_type == CA_SET_LEVEL_TIME ? level.time :
9621      action_type == CA_SET_LEVEL_SCORE ? 0 :
9622      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9623      action_type == CA_SET_CE_SCORE ? 0 :
9624      0);
9625
9626   int action_arg_number =
9627     (action_arg <= CA_ARG_MAX ? action_arg :
9628      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9629      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9630      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9631      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9632      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9633      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9634      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9635      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9636      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9637      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9638      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9639      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9640      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9641      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9642      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9643      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9644      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9645      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9646      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9647      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9648      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9649      -1);
9650
9651   int action_arg_number_old =
9652     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9653      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9654      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9655      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9656      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9657      0);
9658
9659   int action_arg_number_new =
9660     getModifiedActionNumber(action_arg_number_old,
9661                             action_mode, action_arg_number,
9662                             action_arg_number_min, action_arg_number_max);
9663
9664   int trigger_player_bits =
9665     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9666      change->actual_trigger_player_bits : change->trigger_player);
9667
9668   int action_arg_player_bits =
9669     (action_arg >= CA_ARG_PLAYER_1 &&
9670      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9671      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9672      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9673      PLAYER_BITS_ANY);
9674
9675   /* ---------- execute action  -------------------------------------------- */
9676
9677   switch (action_type)
9678   {
9679     case CA_NO_ACTION:
9680     {
9681       return;
9682     }
9683
9684     /* ---------- level actions  ------------------------------------------- */
9685
9686     case CA_RESTART_LEVEL:
9687     {
9688       game.restart_level = TRUE;
9689
9690       break;
9691     }
9692
9693     case CA_SHOW_ENVELOPE:
9694     {
9695       int element = getSpecialActionElement(action_arg_element,
9696                                             action_arg_number, EL_ENVELOPE_1);
9697
9698       if (IS_ENVELOPE(element))
9699         local_player->show_envelope = element;
9700
9701       break;
9702     }
9703
9704     case CA_SET_LEVEL_TIME:
9705     {
9706       if (level.time > 0)       /* only modify limited time value */
9707       {
9708         TimeLeft = action_arg_number_new;
9709
9710         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9711
9712         DisplayGameControlValues();
9713
9714         if (!TimeLeft && setup.time_limit)
9715           for (i = 0; i < MAX_PLAYERS; i++)
9716             KillPlayer(&stored_player[i]);
9717       }
9718
9719       break;
9720     }
9721
9722     case CA_SET_LEVEL_SCORE:
9723     {
9724       local_player->score = action_arg_number_new;
9725
9726       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9727
9728       DisplayGameControlValues();
9729
9730       break;
9731     }
9732
9733     case CA_SET_LEVEL_GEMS:
9734     {
9735       local_player->gems_still_needed = action_arg_number_new;
9736
9737       game.snapshot.collected_item = TRUE;
9738
9739       game_panel_controls[GAME_PANEL_GEMS].value =
9740         local_player->gems_still_needed;
9741
9742       DisplayGameControlValues();
9743
9744       break;
9745     }
9746
9747     case CA_SET_LEVEL_WIND:
9748     {
9749       game.wind_direction = action_arg_direction;
9750
9751       break;
9752     }
9753
9754     case CA_SET_LEVEL_RANDOM_SEED:
9755     {
9756       /* ensure that setting a new random seed while playing is predictable */
9757       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9758
9759       break;
9760     }
9761
9762     /* ---------- player actions  ------------------------------------------ */
9763
9764     case CA_MOVE_PLAYER:
9765     {
9766       /* automatically move to the next field in specified direction */
9767       for (i = 0; i < MAX_PLAYERS; i++)
9768         if (trigger_player_bits & (1 << i))
9769           stored_player[i].programmed_action = action_arg_direction;
9770
9771       break;
9772     }
9773
9774     case CA_EXIT_PLAYER:
9775     {
9776       for (i = 0; i < MAX_PLAYERS; i++)
9777         if (action_arg_player_bits & (1 << i))
9778           PlayerWins(&stored_player[i]);
9779
9780       break;
9781     }
9782
9783     case CA_KILL_PLAYER:
9784     {
9785       for (i = 0; i < MAX_PLAYERS; i++)
9786         if (action_arg_player_bits & (1 << i))
9787           KillPlayer(&stored_player[i]);
9788
9789       break;
9790     }
9791
9792     case CA_SET_PLAYER_KEYS:
9793     {
9794       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9795       int element = getSpecialActionElement(action_arg_element,
9796                                             action_arg_number, EL_KEY_1);
9797
9798       if (IS_KEY(element))
9799       {
9800         for (i = 0; i < MAX_PLAYERS; i++)
9801         {
9802           if (trigger_player_bits & (1 << i))
9803           {
9804             stored_player[i].key[KEY_NR(element)] = key_state;
9805
9806             DrawGameDoorValues();
9807           }
9808         }
9809       }
9810
9811       break;
9812     }
9813
9814     case CA_SET_PLAYER_SPEED:
9815     {
9816       for (i = 0; i < MAX_PLAYERS; i++)
9817       {
9818         if (trigger_player_bits & (1 << i))
9819         {
9820           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9821
9822           if (action_arg == CA_ARG_SPEED_FASTER &&
9823               stored_player[i].cannot_move)
9824           {
9825             action_arg_number = STEPSIZE_VERY_SLOW;
9826           }
9827           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9828                    action_arg == CA_ARG_SPEED_FASTER)
9829           {
9830             action_arg_number = 2;
9831             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9832                            CA_MODE_MULTIPLY);
9833           }
9834           else if (action_arg == CA_ARG_NUMBER_RESET)
9835           {
9836             action_arg_number = level.initial_player_stepsize[i];
9837           }
9838
9839           move_stepsize =
9840             getModifiedActionNumber(move_stepsize,
9841                                     action_mode,
9842                                     action_arg_number,
9843                                     action_arg_number_min,
9844                                     action_arg_number_max);
9845
9846           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9847         }
9848       }
9849
9850       break;
9851     }
9852
9853     case CA_SET_PLAYER_SHIELD:
9854     {
9855       for (i = 0; i < MAX_PLAYERS; i++)
9856       {
9857         if (trigger_player_bits & (1 << i))
9858         {
9859           if (action_arg == CA_ARG_SHIELD_OFF)
9860           {
9861             stored_player[i].shield_normal_time_left = 0;
9862             stored_player[i].shield_deadly_time_left = 0;
9863           }
9864           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9865           {
9866             stored_player[i].shield_normal_time_left = 999999;
9867           }
9868           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9869           {
9870             stored_player[i].shield_normal_time_left = 999999;
9871             stored_player[i].shield_deadly_time_left = 999999;
9872           }
9873         }
9874       }
9875
9876       break;
9877     }
9878
9879     case CA_SET_PLAYER_GRAVITY:
9880     {
9881       for (i = 0; i < MAX_PLAYERS; i++)
9882       {
9883         if (trigger_player_bits & (1 << i))
9884         {
9885           stored_player[i].gravity =
9886             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9887              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9888              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9889              stored_player[i].gravity);
9890         }
9891       }
9892
9893       break;
9894     }
9895
9896     case CA_SET_PLAYER_ARTWORK:
9897     {
9898       for (i = 0; i < MAX_PLAYERS; i++)
9899       {
9900         if (trigger_player_bits & (1 << i))
9901         {
9902           int artwork_element = action_arg_element;
9903
9904           if (action_arg == CA_ARG_ELEMENT_RESET)
9905             artwork_element =
9906               (level.use_artwork_element[i] ? level.artwork_element[i] :
9907                stored_player[i].element_nr);
9908
9909           if (stored_player[i].artwork_element != artwork_element)
9910             stored_player[i].Frame = 0;
9911
9912           stored_player[i].artwork_element = artwork_element;
9913
9914           SetPlayerWaiting(&stored_player[i], FALSE);
9915
9916           /* set number of special actions for bored and sleeping animation */
9917           stored_player[i].num_special_action_bored =
9918             get_num_special_action(artwork_element,
9919                                    ACTION_BORING_1, ACTION_BORING_LAST);
9920           stored_player[i].num_special_action_sleeping =
9921             get_num_special_action(artwork_element,
9922                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9923         }
9924       }
9925
9926       break;
9927     }
9928
9929     case CA_SET_PLAYER_INVENTORY:
9930     {
9931       for (i = 0; i < MAX_PLAYERS; i++)
9932       {
9933         struct PlayerInfo *player = &stored_player[i];
9934         int j, k;
9935
9936         if (trigger_player_bits & (1 << i))
9937         {
9938           int inventory_element = action_arg_element;
9939
9940           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9941               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9942               action_arg == CA_ARG_ELEMENT_ACTION)
9943           {
9944             int element = inventory_element;
9945             int collect_count = element_info[element].collect_count_initial;
9946
9947             if (!IS_CUSTOM_ELEMENT(element))
9948               collect_count = 1;
9949
9950             if (collect_count == 0)
9951               player->inventory_infinite_element = element;
9952             else
9953               for (k = 0; k < collect_count; k++)
9954                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9955                   player->inventory_element[player->inventory_size++] =
9956                     element;
9957           }
9958           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9959                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9960                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9961           {
9962             if (player->inventory_infinite_element != EL_UNDEFINED &&
9963                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9964                                      action_arg_element_raw))
9965               player->inventory_infinite_element = EL_UNDEFINED;
9966
9967             for (k = 0, j = 0; j < player->inventory_size; j++)
9968             {
9969               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9970                                         action_arg_element_raw))
9971                 player->inventory_element[k++] = player->inventory_element[j];
9972             }
9973
9974             player->inventory_size = k;
9975           }
9976           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9977           {
9978             if (player->inventory_size > 0)
9979             {
9980               for (j = 0; j < player->inventory_size - 1; j++)
9981                 player->inventory_element[j] = player->inventory_element[j + 1];
9982
9983               player->inventory_size--;
9984             }
9985           }
9986           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9987           {
9988             if (player->inventory_size > 0)
9989               player->inventory_size--;
9990           }
9991           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9992           {
9993             player->inventory_infinite_element = EL_UNDEFINED;
9994             player->inventory_size = 0;
9995           }
9996           else if (action_arg == CA_ARG_INVENTORY_RESET)
9997           {
9998             player->inventory_infinite_element = EL_UNDEFINED;
9999             player->inventory_size = 0;
10000
10001             if (level.use_initial_inventory[i])
10002             {
10003               for (j = 0; j < level.initial_inventory_size[i]; j++)
10004               {
10005                 int element = level.initial_inventory_content[i][j];
10006                 int collect_count = element_info[element].collect_count_initial;
10007
10008                 if (!IS_CUSTOM_ELEMENT(element))
10009                   collect_count = 1;
10010
10011                 if (collect_count == 0)
10012                   player->inventory_infinite_element = element;
10013                 else
10014                   for (k = 0; k < collect_count; k++)
10015                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10016                       player->inventory_element[player->inventory_size++] =
10017                         element;
10018               }
10019             }
10020           }
10021         }
10022       }
10023
10024       break;
10025     }
10026
10027     /* ---------- CE actions  ---------------------------------------------- */
10028
10029     case CA_SET_CE_VALUE:
10030     {
10031       int last_ce_value = CustomValue[x][y];
10032
10033       CustomValue[x][y] = action_arg_number_new;
10034
10035       if (CustomValue[x][y] != last_ce_value)
10036       {
10037         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10038         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10039
10040         if (CustomValue[x][y] == 0)
10041         {
10042           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10043           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10044         }
10045       }
10046
10047       break;
10048     }
10049
10050     case CA_SET_CE_SCORE:
10051     {
10052       int last_ce_score = ei->collect_score;
10053
10054       ei->collect_score = action_arg_number_new;
10055
10056       if (ei->collect_score != last_ce_score)
10057       {
10058         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10059         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10060
10061         if (ei->collect_score == 0)
10062         {
10063           int xx, yy;
10064
10065           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10066           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10067
10068           /*
10069             This is a very special case that seems to be a mixture between
10070             CheckElementChange() and CheckTriggeredElementChange(): while
10071             the first one only affects single elements that are triggered
10072             directly, the second one affects multiple elements in the playfield
10073             that are triggered indirectly by another element. This is a third
10074             case: Changing the CE score always affects multiple identical CEs,
10075             so every affected CE must be checked, not only the single CE for
10076             which the CE score was changed in the first place (as every instance
10077             of that CE shares the same CE score, and therefore also can change)!
10078           */
10079           SCAN_PLAYFIELD(xx, yy)
10080           {
10081             if (Feld[xx][yy] == element)
10082               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10083                                  CE_SCORE_GETS_ZERO);
10084           }
10085         }
10086       }
10087
10088       break;
10089     }
10090
10091     case CA_SET_CE_ARTWORK:
10092     {
10093       int artwork_element = action_arg_element;
10094       boolean reset_frame = FALSE;
10095       int xx, yy;
10096
10097       if (action_arg == CA_ARG_ELEMENT_RESET)
10098         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10099                            element);
10100
10101       if (ei->gfx_element != artwork_element)
10102         reset_frame = TRUE;
10103
10104       ei->gfx_element = artwork_element;
10105
10106       SCAN_PLAYFIELD(xx, yy)
10107       {
10108         if (Feld[xx][yy] == element)
10109         {
10110           if (reset_frame)
10111           {
10112             ResetGfxAnimation(xx, yy);
10113             ResetRandomAnimationValue(xx, yy);
10114           }
10115
10116           TEST_DrawLevelField(xx, yy);
10117         }
10118       }
10119
10120       break;
10121     }
10122
10123     /* ---------- engine actions  ------------------------------------------ */
10124
10125     case CA_SET_ENGINE_SCAN_MODE:
10126     {
10127       InitPlayfieldScanMode(action_arg);
10128
10129       break;
10130     }
10131
10132     default:
10133       break;
10134   }
10135 }
10136
10137 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10138 {
10139   int old_element = Feld[x][y];
10140   int new_element = GetElementFromGroupElement(element);
10141   int previous_move_direction = MovDir[x][y];
10142   int last_ce_value = CustomValue[x][y];
10143   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10144   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10145   boolean add_player_onto_element = (new_element_is_player &&
10146                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10147                                      IS_WALKABLE(old_element));
10148
10149   if (!add_player_onto_element)
10150   {
10151     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10152       RemoveMovingField(x, y);
10153     else
10154       RemoveField(x, y);
10155
10156     Feld[x][y] = new_element;
10157
10158     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10159       MovDir[x][y] = previous_move_direction;
10160
10161     if (element_info[new_element].use_last_ce_value)
10162       CustomValue[x][y] = last_ce_value;
10163
10164     InitField_WithBug1(x, y, FALSE);
10165
10166     new_element = Feld[x][y];   /* element may have changed */
10167
10168     ResetGfxAnimation(x, y);
10169     ResetRandomAnimationValue(x, y);
10170
10171     TEST_DrawLevelField(x, y);
10172
10173     if (GFX_CRUMBLED(new_element))
10174       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10175   }
10176
10177   /* check if element under the player changes from accessible to unaccessible
10178      (needed for special case of dropping element which then changes) */
10179   /* (must be checked after creating new element for walkable group elements) */
10180   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10181       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10182   {
10183     Bang(x, y);
10184
10185     return;
10186   }
10187
10188   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10189   if (new_element_is_player)
10190     RelocatePlayer(x, y, new_element);
10191
10192   if (is_change)
10193     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10194
10195   TestIfBadThingTouchesPlayer(x, y);
10196   TestIfPlayerTouchesCustomElement(x, y);
10197   TestIfElementTouchesCustomElement(x, y);
10198 }
10199
10200 static void CreateField(int x, int y, int element)
10201 {
10202   CreateFieldExt(x, y, element, FALSE);
10203 }
10204
10205 static void CreateElementFromChange(int x, int y, int element)
10206 {
10207   element = GET_VALID_RUNTIME_ELEMENT(element);
10208
10209   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10210   {
10211     int old_element = Feld[x][y];
10212
10213     /* prevent changed element from moving in same engine frame
10214        unless both old and new element can either fall or move */
10215     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10216         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10217       Stop[x][y] = TRUE;
10218   }
10219
10220   CreateFieldExt(x, y, element, TRUE);
10221 }
10222
10223 static boolean ChangeElement(int x, int y, int element, int page)
10224 {
10225   struct ElementInfo *ei = &element_info[element];
10226   struct ElementChangeInfo *change = &ei->change_page[page];
10227   int ce_value = CustomValue[x][y];
10228   int ce_score = ei->collect_score;
10229   int target_element;
10230   int old_element = Feld[x][y];
10231
10232   /* always use default change event to prevent running into a loop */
10233   if (ChangeEvent[x][y] == -1)
10234     ChangeEvent[x][y] = CE_DELAY;
10235
10236   if (ChangeEvent[x][y] == CE_DELAY)
10237   {
10238     /* reset actual trigger element, trigger player and action element */
10239     change->actual_trigger_element = EL_EMPTY;
10240     change->actual_trigger_player = EL_EMPTY;
10241     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10242     change->actual_trigger_side = CH_SIDE_NONE;
10243     change->actual_trigger_ce_value = 0;
10244     change->actual_trigger_ce_score = 0;
10245   }
10246
10247   /* do not change elements more than a specified maximum number of changes */
10248   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10249     return FALSE;
10250
10251   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10252
10253   if (change->explode)
10254   {
10255     Bang(x, y);
10256
10257     return TRUE;
10258   }
10259
10260   if (change->use_target_content)
10261   {
10262     boolean complete_replace = TRUE;
10263     boolean can_replace[3][3];
10264     int xx, yy;
10265
10266     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10267     {
10268       boolean is_empty;
10269       boolean is_walkable;
10270       boolean is_diggable;
10271       boolean is_collectible;
10272       boolean is_removable;
10273       boolean is_destructible;
10274       int ex = x + xx - 1;
10275       int ey = y + yy - 1;
10276       int content_element = change->target_content.e[xx][yy];
10277       int e;
10278
10279       can_replace[xx][yy] = TRUE;
10280
10281       if (ex == x && ey == y)   /* do not check changing element itself */
10282         continue;
10283
10284       if (content_element == EL_EMPTY_SPACE)
10285       {
10286         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10287
10288         continue;
10289       }
10290
10291       if (!IN_LEV_FIELD(ex, ey))
10292       {
10293         can_replace[xx][yy] = FALSE;
10294         complete_replace = FALSE;
10295
10296         continue;
10297       }
10298
10299       e = Feld[ex][ey];
10300
10301       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10302         e = MovingOrBlocked2Element(ex, ey);
10303
10304       is_empty = (IS_FREE(ex, ey) ||
10305                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10306
10307       is_walkable     = (is_empty || IS_WALKABLE(e));
10308       is_diggable     = (is_empty || IS_DIGGABLE(e));
10309       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10310       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10311       is_removable    = (is_diggable || is_collectible);
10312
10313       can_replace[xx][yy] =
10314         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10315           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10316           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10317           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10318           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10319           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10320          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10321
10322       if (!can_replace[xx][yy])
10323         complete_replace = FALSE;
10324     }
10325
10326     if (!change->only_if_complete || complete_replace)
10327     {
10328       boolean something_has_changed = FALSE;
10329
10330       if (change->only_if_complete && change->use_random_replace &&
10331           RND(100) < change->random_percentage)
10332         return FALSE;
10333
10334       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10335       {
10336         int ex = x + xx - 1;
10337         int ey = y + yy - 1;
10338         int content_element;
10339
10340         if (can_replace[xx][yy] && (!change->use_random_replace ||
10341                                     RND(100) < change->random_percentage))
10342         {
10343           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10344             RemoveMovingField(ex, ey);
10345
10346           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10347
10348           content_element = change->target_content.e[xx][yy];
10349           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10350                                               ce_value, ce_score);
10351
10352           CreateElementFromChange(ex, ey, target_element);
10353
10354           something_has_changed = TRUE;
10355
10356           /* for symmetry reasons, freeze newly created border elements */
10357           if (ex != x || ey != y)
10358             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10359         }
10360       }
10361
10362       if (something_has_changed)
10363       {
10364         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10365         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10366       }
10367     }
10368   }
10369   else
10370   {
10371     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10372                                         ce_value, ce_score);
10373
10374     if (element == EL_DIAGONAL_GROWING ||
10375         element == EL_DIAGONAL_SHRINKING)
10376     {
10377       target_element = Store[x][y];
10378
10379       Store[x][y] = EL_EMPTY;
10380     }
10381
10382     CreateElementFromChange(x, y, target_element);
10383
10384     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10385     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10386   }
10387
10388   /* this uses direct change before indirect change */
10389   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10390
10391   return TRUE;
10392 }
10393
10394 static void HandleElementChange(int x, int y, int page)
10395 {
10396   int element = MovingOrBlocked2Element(x, y);
10397   struct ElementInfo *ei = &element_info[element];
10398   struct ElementChangeInfo *change = &ei->change_page[page];
10399   boolean handle_action_before_change = FALSE;
10400
10401 #ifdef DEBUG
10402   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10403       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10404   {
10405     printf("\n\n");
10406     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10407            x, y, element, element_info[element].token_name);
10408     printf("HandleElementChange(): This should never happen!\n");
10409     printf("\n\n");
10410   }
10411 #endif
10412
10413   /* this can happen with classic bombs on walkable, changing elements */
10414   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10415   {
10416     return;
10417   }
10418
10419   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10420   {
10421     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10422
10423     if (change->can_change)
10424     {
10425       /* !!! not clear why graphic animation should be reset at all here !!! */
10426       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10427       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10428
10429       /*
10430         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10431
10432         When using an animation frame delay of 1 (this only happens with
10433         "sp_zonk.moving.left/right" in the classic graphics), the default
10434         (non-moving) animation shows wrong animation frames (while the
10435         moving animation, like "sp_zonk.moving.left/right", is correct,
10436         so this graphical bug never shows up with the classic graphics).
10437         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10438         be drawn instead of the correct frames 0,1,2,3. This is caused by
10439         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10440         an element change: First when the change delay ("ChangeDelay[][]")
10441         counter has reached zero after decrementing, then a second time in
10442         the next frame (after "GfxFrame[][]" was already incremented) when
10443         "ChangeDelay[][]" is reset to the initial delay value again.
10444
10445         This causes frame 0 to be drawn twice, while the last frame won't
10446         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10447
10448         As some animations may already be cleverly designed around this bug
10449         (at least the "Snake Bite" snake tail animation does this), it cannot
10450         simply be fixed here without breaking such existing animations.
10451         Unfortunately, it cannot easily be detected if a graphics set was
10452         designed "before" or "after" the bug was fixed. As a workaround,
10453         a new graphics set option "game.graphics_engine_version" was added
10454         to be able to specify the game's major release version for which the
10455         graphics set was designed, which can then be used to decide if the
10456         bugfix should be used (version 4 and above) or not (version 3 or
10457         below, or if no version was specified at all, as with old sets).
10458
10459         (The wrong/fixed animation frames can be tested with the test level set
10460         "test_gfxframe" and level "000", which contains a specially prepared
10461         custom element at level position (x/y) == (11/9) which uses the zonk
10462         animation mentioned above. Using "game.graphics_engine_version: 4"
10463         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10464         This can also be seen from the debug output for this test element.)
10465       */
10466
10467       /* when a custom element is about to change (for example by change delay),
10468          do not reset graphic animation when the custom element is moving */
10469       if (game.graphics_engine_version < 4 &&
10470           !IS_MOVING(x, y))
10471       {
10472         ResetGfxAnimation(x, y);
10473         ResetRandomAnimationValue(x, y);
10474       }
10475
10476       if (change->pre_change_function)
10477         change->pre_change_function(x, y);
10478     }
10479   }
10480
10481   ChangeDelay[x][y]--;
10482
10483   if (ChangeDelay[x][y] != 0)           /* continue element change */
10484   {
10485     if (change->can_change)
10486     {
10487       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10488
10489       if (IS_ANIMATED(graphic))
10490         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10491
10492       if (change->change_function)
10493         change->change_function(x, y);
10494     }
10495   }
10496   else                                  /* finish element change */
10497   {
10498     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10499     {
10500       page = ChangePage[x][y];
10501       ChangePage[x][y] = -1;
10502
10503       change = &ei->change_page[page];
10504     }
10505
10506     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10507     {
10508       ChangeDelay[x][y] = 1;            /* try change after next move step */
10509       ChangePage[x][y] = page;          /* remember page to use for change */
10510
10511       return;
10512     }
10513
10514     /* special case: set new level random seed before changing element */
10515     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10516       handle_action_before_change = TRUE;
10517
10518     if (change->has_action && handle_action_before_change)
10519       ExecuteCustomElementAction(x, y, element, page);
10520
10521     if (change->can_change)
10522     {
10523       if (ChangeElement(x, y, element, page))
10524       {
10525         if (change->post_change_function)
10526           change->post_change_function(x, y);
10527       }
10528     }
10529
10530     if (change->has_action && !handle_action_before_change)
10531       ExecuteCustomElementAction(x, y, element, page);
10532   }
10533 }
10534
10535 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10536                                               int trigger_element,
10537                                               int trigger_event,
10538                                               int trigger_player,
10539                                               int trigger_side,
10540                                               int trigger_page)
10541 {
10542   boolean change_done_any = FALSE;
10543   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10544   int i;
10545
10546   if (!(trigger_events[trigger_element][trigger_event]))
10547     return FALSE;
10548
10549   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10550
10551   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10552   {
10553     int element = EL_CUSTOM_START + i;
10554     boolean change_done = FALSE;
10555     int p;
10556
10557     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10558         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10559       continue;
10560
10561     for (p = 0; p < element_info[element].num_change_pages; p++)
10562     {
10563       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10564
10565       if (change->can_change_or_has_action &&
10566           change->has_event[trigger_event] &&
10567           change->trigger_side & trigger_side &&
10568           change->trigger_player & trigger_player &&
10569           change->trigger_page & trigger_page_bits &&
10570           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10571       {
10572         change->actual_trigger_element = trigger_element;
10573         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10574         change->actual_trigger_player_bits = trigger_player;
10575         change->actual_trigger_side = trigger_side;
10576         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10577         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10578
10579         if ((change->can_change && !change_done) || change->has_action)
10580         {
10581           int x, y;
10582
10583           SCAN_PLAYFIELD(x, y)
10584           {
10585             if (Feld[x][y] == element)
10586             {
10587               if (change->can_change && !change_done)
10588               {
10589                 /* if element already changed in this frame, not only prevent
10590                    another element change (checked in ChangeElement()), but
10591                    also prevent additional element actions for this element */
10592
10593                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10594                     !level.use_action_after_change_bug)
10595                   continue;
10596
10597                 ChangeDelay[x][y] = 1;
10598                 ChangeEvent[x][y] = trigger_event;
10599
10600                 HandleElementChange(x, y, p);
10601               }
10602               else if (change->has_action)
10603               {
10604                 /* if element already changed in this frame, not only prevent
10605                    another element change (checked in ChangeElement()), but
10606                    also prevent additional element actions for this element */
10607
10608                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10609                     !level.use_action_after_change_bug)
10610                   continue;
10611
10612                 ExecuteCustomElementAction(x, y, element, p);
10613                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10614               }
10615             }
10616           }
10617
10618           if (change->can_change)
10619           {
10620             change_done = TRUE;
10621             change_done_any = TRUE;
10622           }
10623         }
10624       }
10625     }
10626   }
10627
10628   RECURSION_LOOP_DETECTION_END();
10629
10630   return change_done_any;
10631 }
10632
10633 static boolean CheckElementChangeExt(int x, int y,
10634                                      int element,
10635                                      int trigger_element,
10636                                      int trigger_event,
10637                                      int trigger_player,
10638                                      int trigger_side)
10639 {
10640   boolean change_done = FALSE;
10641   int p;
10642
10643   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10644       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10645     return FALSE;
10646
10647   if (Feld[x][y] == EL_BLOCKED)
10648   {
10649     Blocked2Moving(x, y, &x, &y);
10650     element = Feld[x][y];
10651   }
10652
10653   /* check if element has already changed or is about to change after moving */
10654   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10655        Feld[x][y] != element) ||
10656
10657       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10658        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10659         ChangePage[x][y] != -1)))
10660     return FALSE;
10661
10662   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10663
10664   for (p = 0; p < element_info[element].num_change_pages; p++)
10665   {
10666     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10667
10668     /* check trigger element for all events where the element that is checked
10669        for changing interacts with a directly adjacent element -- this is
10670        different to element changes that affect other elements to change on the
10671        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10672     boolean check_trigger_element =
10673       (trigger_event == CE_TOUCHING_X ||
10674        trigger_event == CE_HITTING_X ||
10675        trigger_event == CE_HIT_BY_X ||
10676        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10677
10678     if (change->can_change_or_has_action &&
10679         change->has_event[trigger_event] &&
10680         change->trigger_side & trigger_side &&
10681         change->trigger_player & trigger_player &&
10682         (!check_trigger_element ||
10683          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10684     {
10685       change->actual_trigger_element = trigger_element;
10686       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10687       change->actual_trigger_player_bits = trigger_player;
10688       change->actual_trigger_side = trigger_side;
10689       change->actual_trigger_ce_value = CustomValue[x][y];
10690       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10691
10692       /* special case: trigger element not at (x,y) position for some events */
10693       if (check_trigger_element)
10694       {
10695         static struct
10696         {
10697           int dx, dy;
10698         } move_xy[] =
10699           {
10700             {  0,  0 },
10701             { -1,  0 },
10702             { +1,  0 },
10703             {  0,  0 },
10704             {  0, -1 },
10705             {  0,  0 }, { 0, 0 }, { 0, 0 },
10706             {  0, +1 }
10707           };
10708
10709         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10710         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10711
10712         change->actual_trigger_ce_value = CustomValue[xx][yy];
10713         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10714       }
10715
10716       if (change->can_change && !change_done)
10717       {
10718         ChangeDelay[x][y] = 1;
10719         ChangeEvent[x][y] = trigger_event;
10720
10721         HandleElementChange(x, y, p);
10722
10723         change_done = TRUE;
10724       }
10725       else if (change->has_action)
10726       {
10727         ExecuteCustomElementAction(x, y, element, p);
10728         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10729       }
10730     }
10731   }
10732
10733   RECURSION_LOOP_DETECTION_END();
10734
10735   return change_done;
10736 }
10737
10738 static void PlayPlayerSound(struct PlayerInfo *player)
10739 {
10740   int jx = player->jx, jy = player->jy;
10741   int sound_element = player->artwork_element;
10742   int last_action = player->last_action_waiting;
10743   int action = player->action_waiting;
10744
10745   if (player->is_waiting)
10746   {
10747     if (action != last_action)
10748       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10749     else
10750       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10751   }
10752   else
10753   {
10754     if (action != last_action)
10755       StopSound(element_info[sound_element].sound[last_action]);
10756
10757     if (last_action == ACTION_SLEEPING)
10758       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10759   }
10760 }
10761
10762 static void PlayAllPlayersSound()
10763 {
10764   int i;
10765
10766   for (i = 0; i < MAX_PLAYERS; i++)
10767     if (stored_player[i].active)
10768       PlayPlayerSound(&stored_player[i]);
10769 }
10770
10771 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10772 {
10773   boolean last_waiting = player->is_waiting;
10774   int move_dir = player->MovDir;
10775
10776   player->dir_waiting = move_dir;
10777   player->last_action_waiting = player->action_waiting;
10778
10779   if (is_waiting)
10780   {
10781     if (!last_waiting)          /* not waiting -> waiting */
10782     {
10783       player->is_waiting = TRUE;
10784
10785       player->frame_counter_bored =
10786         FrameCounter +
10787         game.player_boring_delay_fixed +
10788         GetSimpleRandom(game.player_boring_delay_random);
10789       player->frame_counter_sleeping =
10790         FrameCounter +
10791         game.player_sleeping_delay_fixed +
10792         GetSimpleRandom(game.player_sleeping_delay_random);
10793
10794       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10795     }
10796
10797     if (game.player_sleeping_delay_fixed +
10798         game.player_sleeping_delay_random > 0 &&
10799         player->anim_delay_counter == 0 &&
10800         player->post_delay_counter == 0 &&
10801         FrameCounter >= player->frame_counter_sleeping)
10802       player->is_sleeping = TRUE;
10803     else if (game.player_boring_delay_fixed +
10804              game.player_boring_delay_random > 0 &&
10805              FrameCounter >= player->frame_counter_bored)
10806       player->is_bored = TRUE;
10807
10808     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10809                               player->is_bored ? ACTION_BORING :
10810                               ACTION_WAITING);
10811
10812     if (player->is_sleeping && player->use_murphy)
10813     {
10814       /* special case for sleeping Murphy when leaning against non-free tile */
10815
10816       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_LEFT;
10820       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10821                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10822                 !IS_MOVING(player->jx + 1, player->jy)))
10823         move_dir = MV_RIGHT;
10824       else
10825         player->is_sleeping = FALSE;
10826
10827       player->dir_waiting = move_dir;
10828     }
10829
10830     if (player->is_sleeping)
10831     {
10832       if (player->num_special_action_sleeping > 0)
10833       {
10834         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10835         {
10836           int last_special_action = player->special_action_sleeping;
10837           int num_special_action = player->num_special_action_sleeping;
10838           int special_action =
10839             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10840              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10841              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10842              last_special_action + 1 : ACTION_SLEEPING);
10843           int special_graphic =
10844             el_act_dir2img(player->artwork_element, special_action, move_dir);
10845
10846           player->anim_delay_counter =
10847             graphic_info[special_graphic].anim_delay_fixed +
10848             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10849           player->post_delay_counter =
10850             graphic_info[special_graphic].post_delay_fixed +
10851             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10852
10853           player->special_action_sleeping = special_action;
10854         }
10855
10856         if (player->anim_delay_counter > 0)
10857         {
10858           player->action_waiting = player->special_action_sleeping;
10859           player->anim_delay_counter--;
10860         }
10861         else if (player->post_delay_counter > 0)
10862         {
10863           player->post_delay_counter--;
10864         }
10865       }
10866     }
10867     else if (player->is_bored)
10868     {
10869       if (player->num_special_action_bored > 0)
10870       {
10871         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10872         {
10873           int special_action =
10874             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10875           int special_graphic =
10876             el_act_dir2img(player->artwork_element, special_action, move_dir);
10877
10878           player->anim_delay_counter =
10879             graphic_info[special_graphic].anim_delay_fixed +
10880             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10881           player->post_delay_counter =
10882             graphic_info[special_graphic].post_delay_fixed +
10883             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10884
10885           player->special_action_bored = special_action;
10886         }
10887
10888         if (player->anim_delay_counter > 0)
10889         {
10890           player->action_waiting = player->special_action_bored;
10891           player->anim_delay_counter--;
10892         }
10893         else if (player->post_delay_counter > 0)
10894         {
10895           player->post_delay_counter--;
10896         }
10897       }
10898     }
10899   }
10900   else if (last_waiting)        /* waiting -> not waiting */
10901   {
10902     player->is_waiting = FALSE;
10903     player->is_bored = FALSE;
10904     player->is_sleeping = FALSE;
10905
10906     player->frame_counter_bored = -1;
10907     player->frame_counter_sleeping = -1;
10908
10909     player->anim_delay_counter = 0;
10910     player->post_delay_counter = 0;
10911
10912     player->dir_waiting = player->MovDir;
10913     player->action_waiting = ACTION_DEFAULT;
10914
10915     player->special_action_bored = ACTION_DEFAULT;
10916     player->special_action_sleeping = ACTION_DEFAULT;
10917   }
10918 }
10919
10920 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10921 {
10922   if ((!player->is_moving  && player->was_moving) ||
10923       (player->MovPos == 0 && player->was_moving) ||
10924       (player->is_snapping && !player->was_snapping) ||
10925       (player->is_dropping && !player->was_dropping))
10926   {
10927     if (!CheckSaveEngineSnapshotToList())
10928       return;
10929
10930     player->was_moving = FALSE;
10931     player->was_snapping = TRUE;
10932     player->was_dropping = TRUE;
10933   }
10934   else
10935   {
10936     if (player->is_moving)
10937       player->was_moving = TRUE;
10938
10939     if (!player->is_snapping)
10940       player->was_snapping = FALSE;
10941
10942     if (!player->is_dropping)
10943       player->was_dropping = FALSE;
10944   }
10945 }
10946
10947 static void CheckSingleStepMode(struct PlayerInfo *player)
10948 {
10949   if (tape.single_step && tape.recording && !tape.pausing)
10950   {
10951     /* as it is called "single step mode", just return to pause mode when the
10952        player stopped moving after one tile (or never starts moving at all) */
10953     if (!player->is_moving &&
10954         !player->is_pushing &&
10955         !player->is_dropping_pressed)
10956     {
10957       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10958       SnapField(player, 0, 0);                  /* stop snapping */
10959     }
10960   }
10961
10962   CheckSaveEngineSnapshot(player);
10963 }
10964
10965 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10966 {
10967   int left      = player_action & JOY_LEFT;
10968   int right     = player_action & JOY_RIGHT;
10969   int up        = player_action & JOY_UP;
10970   int down      = player_action & JOY_DOWN;
10971   int button1   = player_action & JOY_BUTTON_1;
10972   int button2   = player_action & JOY_BUTTON_2;
10973   int dx        = (left ? -1 : right ? 1 : 0);
10974   int dy        = (up   ? -1 : down  ? 1 : 0);
10975
10976   if (!player->active || tape.pausing)
10977     return 0;
10978
10979   if (player_action)
10980   {
10981     if (button1)
10982       SnapField(player, dx, dy);
10983     else
10984     {
10985       if (button2)
10986         DropElement(player);
10987
10988       MovePlayer(player, dx, dy);
10989     }
10990
10991     CheckSingleStepMode(player);
10992
10993     SetPlayerWaiting(player, FALSE);
10994
10995     return player_action;
10996   }
10997   else
10998   {
10999     /* no actions for this player (no input at player's configured device) */
11000
11001     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11002     SnapField(player, 0, 0);
11003     CheckGravityMovementWhenNotMoving(player);
11004
11005     if (player->MovPos == 0)
11006       SetPlayerWaiting(player, TRUE);
11007
11008     if (player->MovPos == 0)    /* needed for tape.playing */
11009       player->is_moving = FALSE;
11010
11011     player->is_dropping = FALSE;
11012     player->is_dropping_pressed = FALSE;
11013     player->drop_pressed_delay = 0;
11014
11015     CheckSingleStepMode(player);
11016
11017     return 0;
11018   }
11019 }
11020
11021 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11022                                          byte *tape_action)
11023 {
11024   if (!tape.use_mouse)
11025     return;
11026
11027   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11028   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11029   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11030 }
11031
11032 static void SetTapeActionFromMouseAction(byte *tape_action,
11033                                          struct MouseActionInfo *mouse_action)
11034 {
11035   if (!tape.use_mouse)
11036     return;
11037
11038   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11039   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11040   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11041 }
11042
11043 static void CheckLevelTime()
11044 {
11045   int i;
11046
11047   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11048   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11049   {
11050     if (level.native_em_level->lev->home == 0)  /* all players at home */
11051     {
11052       PlayerWins(local_player);
11053
11054       AllPlayersGone = TRUE;
11055
11056       level.native_em_level->lev->home = -1;
11057     }
11058
11059     if (level.native_em_level->ply[0]->alive == 0 &&
11060         level.native_em_level->ply[1]->alive == 0 &&
11061         level.native_em_level->ply[2]->alive == 0 &&
11062         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11063       AllPlayersGone = TRUE;
11064   }
11065   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11066   {
11067     if (game_sp.LevelSolved &&
11068         !game_sp.GameOver)                              /* game won */
11069     {
11070       PlayerWins(local_player);
11071
11072       game_sp.GameOver = TRUE;
11073
11074       AllPlayersGone = TRUE;
11075     }
11076
11077     if (game_sp.GameOver)                               /* game lost */
11078       AllPlayersGone = TRUE;
11079   }
11080   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11081   {
11082     if (game_mm.level_solved &&
11083         !game_mm.game_over)                             /* game won */
11084     {
11085       PlayerWins(local_player);
11086
11087       game_mm.game_over = TRUE;
11088
11089       AllPlayersGone = TRUE;
11090     }
11091
11092     if (game_mm.game_over)                              /* game lost */
11093       AllPlayersGone = TRUE;
11094   }
11095
11096   if (TimeFrames >= FRAMES_PER_SECOND)
11097   {
11098     TimeFrames = 0;
11099     TapeTime++;
11100
11101     for (i = 0; i < MAX_PLAYERS; i++)
11102     {
11103       struct PlayerInfo *player = &stored_player[i];
11104
11105       if (SHIELD_ON(player))
11106       {
11107         player->shield_normal_time_left--;
11108
11109         if (player->shield_deadly_time_left > 0)
11110           player->shield_deadly_time_left--;
11111       }
11112     }
11113
11114     if (!local_player->LevelSolved && !level.use_step_counter)
11115     {
11116       TimePlayed++;
11117
11118       if (TimeLeft > 0)
11119       {
11120         TimeLeft--;
11121
11122         if (TimeLeft <= 10 && setup.time_limit)
11123           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11124
11125         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11126            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11127
11128         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11129
11130         if (!TimeLeft && setup.time_limit)
11131         {
11132           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11133             level.native_em_level->lev->killed_out_of_time = TRUE;
11134           else
11135             for (i = 0; i < MAX_PLAYERS; i++)
11136               KillPlayer(&stored_player[i]);
11137         }
11138       }
11139       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11140       {
11141         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11142       }
11143
11144       level.native_em_level->lev->time =
11145         (game.no_time_limit ? TimePlayed : TimeLeft);
11146     }
11147
11148     if (tape.recording || tape.playing)
11149       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11150   }
11151
11152   if (tape.recording || tape.playing)
11153     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11154
11155   UpdateAndDisplayGameControlValues();
11156 }
11157
11158 void AdvanceFrameAndPlayerCounters(int player_nr)
11159 {
11160   int i;
11161
11162   /* advance frame counters (global frame counter and time frame counter) */
11163   FrameCounter++;
11164   TimeFrames++;
11165
11166   /* advance player counters (counters for move delay, move animation etc.) */
11167   for (i = 0; i < MAX_PLAYERS; i++)
11168   {
11169     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11170     int move_delay_value = stored_player[i].move_delay_value;
11171     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11172
11173     if (!advance_player_counters)       /* not all players may be affected */
11174       continue;
11175
11176     if (move_frames == 0)       /* less than one move per game frame */
11177     {
11178       int stepsize = TILEX / move_delay_value;
11179       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11180       int count = (stored_player[i].is_moving ?
11181                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11182
11183       if (count % delay == 0)
11184         move_frames = 1;
11185     }
11186
11187     stored_player[i].Frame += move_frames;
11188
11189     if (stored_player[i].MovPos != 0)
11190       stored_player[i].StepFrame += move_frames;
11191
11192     if (stored_player[i].move_delay > 0)
11193       stored_player[i].move_delay--;
11194
11195     /* due to bugs in previous versions, counter must count up, not down */
11196     if (stored_player[i].push_delay != -1)
11197       stored_player[i].push_delay++;
11198
11199     if (stored_player[i].drop_delay > 0)
11200       stored_player[i].drop_delay--;
11201
11202     if (stored_player[i].is_dropping_pressed)
11203       stored_player[i].drop_pressed_delay++;
11204   }
11205 }
11206
11207 void StartGameActions(boolean init_network_game, boolean record_tape,
11208                       int random_seed)
11209 {
11210   unsigned int new_random_seed = InitRND(random_seed);
11211
11212   if (record_tape)
11213     TapeStartRecording(new_random_seed);
11214
11215   if (init_network_game)
11216   {
11217     SendToServer_StartPlaying();
11218
11219     return;
11220   }
11221
11222   InitGame();
11223 }
11224
11225 void GameActionsExt()
11226 {
11227 #if 0
11228   static unsigned int game_frame_delay = 0;
11229 #endif
11230   unsigned int game_frame_delay_value;
11231   byte *recorded_player_action;
11232   byte summarized_player_action = 0;
11233   byte tape_action[MAX_PLAYERS];
11234   int i;
11235
11236   /* detect endless loops, caused by custom element programming */
11237   if (recursion_loop_detected && recursion_loop_depth == 0)
11238   {
11239     char *message = getStringCat3("Internal Error! Element ",
11240                                   EL_NAME(recursion_loop_element),
11241                                   " caused endless loop! Quit the game?");
11242
11243     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11244           EL_NAME(recursion_loop_element));
11245
11246     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11247
11248     recursion_loop_detected = FALSE;    /* if game should be continued */
11249
11250     free(message);
11251
11252     return;
11253   }
11254
11255   if (game.restart_level)
11256     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11257
11258   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11259   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11260   {
11261     if (level.native_em_level->lev->home == 0)  /* all players at home */
11262     {
11263       PlayerWins(local_player);
11264
11265       AllPlayersGone = TRUE;
11266
11267       level.native_em_level->lev->home = -1;
11268     }
11269
11270     if (level.native_em_level->ply[0]->alive == 0 &&
11271         level.native_em_level->ply[1]->alive == 0 &&
11272         level.native_em_level->ply[2]->alive == 0 &&
11273         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11274       AllPlayersGone = TRUE;
11275   }
11276   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11277   {
11278     if (game_sp.LevelSolved &&
11279         !game_sp.GameOver)                              /* game won */
11280     {
11281       PlayerWins(local_player);
11282
11283       game_sp.GameOver = TRUE;
11284
11285       AllPlayersGone = TRUE;
11286     }
11287
11288     if (game_sp.GameOver)                               /* game lost */
11289       AllPlayersGone = TRUE;
11290   }
11291   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11292   {
11293     if (game_mm.level_solved &&
11294         !game_mm.game_over)                             /* game won */
11295     {
11296       PlayerWins(local_player);
11297
11298       game_mm.game_over = TRUE;
11299
11300       AllPlayersGone = TRUE;
11301     }
11302
11303     if (game_mm.game_over)                              /* game lost */
11304       AllPlayersGone = TRUE;
11305   }
11306
11307   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11308     GameWon();
11309
11310   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11311     TapeStop();
11312
11313   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11314     return;
11315
11316   game_frame_delay_value =
11317     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11318
11319   if (tape.playing && tape.warp_forward && !tape.pausing)
11320     game_frame_delay_value = 0;
11321
11322   SetVideoFrameDelay(game_frame_delay_value);
11323
11324 #if 0
11325 #if 0
11326   /* ---------- main game synchronization point ---------- */
11327
11328   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11329
11330   printf("::: skip == %d\n", skip);
11331
11332 #else
11333   /* ---------- main game synchronization point ---------- */
11334
11335   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11336 #endif
11337 #endif
11338
11339   if (network_playing && !network_player_action_received)
11340   {
11341     /* try to get network player actions in time */
11342
11343     /* last chance to get network player actions without main loop delay */
11344     HandleNetworking();
11345
11346     /* game was quit by network peer */
11347     if (game_status != GAME_MODE_PLAYING)
11348       return;
11349
11350     if (!network_player_action_received)
11351       return;           /* failed to get network player actions in time */
11352
11353     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11354   }
11355
11356   if (tape.pausing)
11357     return;
11358
11359   /* at this point we know that we really continue executing the game */
11360
11361   network_player_action_received = FALSE;
11362
11363   /* when playing tape, read previously recorded player input from tape data */
11364   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11365
11366   local_player->effective_mouse_action = local_player->mouse_action;
11367
11368   if (recorded_player_action != NULL)
11369     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11370                                  recorded_player_action);
11371
11372   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11373   if (tape.pausing)
11374     return;
11375
11376   if (tape.set_centered_player)
11377   {
11378     game.centered_player_nr_next = tape.centered_player_nr_next;
11379     game.set_centered_player = TRUE;
11380   }
11381
11382   for (i = 0; i < MAX_PLAYERS; i++)
11383   {
11384     summarized_player_action |= stored_player[i].action;
11385
11386     if (!network_playing && (game.team_mode || tape.playing))
11387       stored_player[i].effective_action = stored_player[i].action;
11388   }
11389
11390   if (network_playing)
11391     SendToServer_MovePlayer(summarized_player_action);
11392
11393   // summarize all actions at local players mapped input device position
11394   // (this allows using different input devices in single player mode)
11395   if (!network.enabled && !game.team_mode)
11396     stored_player[map_player_action[local_player->index_nr]].effective_action =
11397       summarized_player_action;
11398
11399   if (tape.recording &&
11400       setup.team_mode &&
11401       setup.input_on_focus &&
11402       game.centered_player_nr != -1)
11403   {
11404     for (i = 0; i < MAX_PLAYERS; i++)
11405       stored_player[i].effective_action =
11406         (i == game.centered_player_nr ? summarized_player_action : 0);
11407   }
11408
11409   if (recorded_player_action != NULL)
11410     for (i = 0; i < MAX_PLAYERS; i++)
11411       stored_player[i].effective_action = recorded_player_action[i];
11412
11413   for (i = 0; i < MAX_PLAYERS; i++)
11414   {
11415     tape_action[i] = stored_player[i].effective_action;
11416
11417     /* (this may happen in the RND game engine if a player was not present on
11418        the playfield on level start, but appeared later from a custom element */
11419     if (setup.team_mode &&
11420         tape.recording &&
11421         tape_action[i] &&
11422         !tape.player_participates[i])
11423       tape.player_participates[i] = TRUE;
11424   }
11425
11426   SetTapeActionFromMouseAction(tape_action,
11427                                &local_player->effective_mouse_action);
11428
11429   /* only record actions from input devices, but not programmed actions */
11430   if (tape.recording)
11431     TapeRecordAction(tape_action);
11432
11433 #if USE_NEW_PLAYER_ASSIGNMENTS
11434   // !!! also map player actions in single player mode !!!
11435   // if (game.team_mode)
11436   if (1)
11437   {
11438     byte mapped_action[MAX_PLAYERS];
11439
11440 #if DEBUG_PLAYER_ACTIONS
11441     printf(":::");
11442     for (i = 0; i < MAX_PLAYERS; i++)
11443       printf(" %d, ", stored_player[i].effective_action);
11444 #endif
11445
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11448
11449     for (i = 0; i < MAX_PLAYERS; i++)
11450       stored_player[i].effective_action = mapped_action[i];
11451
11452 #if DEBUG_PLAYER_ACTIONS
11453     printf(" =>");
11454     for (i = 0; i < MAX_PLAYERS; i++)
11455       printf(" %d, ", stored_player[i].effective_action);
11456     printf("\n");
11457 #endif
11458   }
11459 #if DEBUG_PLAYER_ACTIONS
11460   else
11461   {
11462     printf(":::");
11463     for (i = 0; i < MAX_PLAYERS; i++)
11464       printf(" %d, ", stored_player[i].effective_action);
11465     printf("\n");
11466   }
11467 #endif
11468 #endif
11469
11470   for (i = 0; i < MAX_PLAYERS; i++)
11471   {
11472     // allow engine snapshot in case of changed movement attempt
11473     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11474         (stored_player[i].effective_action & KEY_MOTION))
11475       game.snapshot.changed_action = TRUE;
11476
11477     // allow engine snapshot in case of snapping/dropping attempt
11478     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11479         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11480       game.snapshot.changed_action = TRUE;
11481
11482     game.snapshot.last_action[i] = stored_player[i].effective_action;
11483   }
11484
11485   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11486   {
11487     GameActions_EM_Main();
11488   }
11489   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11490   {
11491     GameActions_SP_Main();
11492   }
11493   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11494   {
11495     GameActions_MM_Main();
11496   }
11497   else
11498   {
11499     GameActions_RND_Main();
11500   }
11501
11502   BlitScreenToBitmap(backbuffer);
11503
11504   CheckLevelTime();
11505
11506   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11507
11508   if (global.show_frames_per_second)
11509   {
11510     static unsigned int fps_counter = 0;
11511     static int fps_frames = 0;
11512     unsigned int fps_delay_ms = Counter() - fps_counter;
11513
11514     fps_frames++;
11515
11516     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11517     {
11518       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11519
11520       fps_frames = 0;
11521       fps_counter = Counter();
11522
11523       /* always draw FPS to screen after FPS value was updated */
11524       redraw_mask |= REDRAW_FPS;
11525     }
11526
11527     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11528     if (GetDrawDeactivationMask() == REDRAW_NONE)
11529       redraw_mask |= REDRAW_FPS;
11530   }
11531 }
11532
11533 static void GameActions_CheckSaveEngineSnapshot()
11534 {
11535   if (!game.snapshot.save_snapshot)
11536     return;
11537
11538   // clear flag for saving snapshot _before_ saving snapshot
11539   game.snapshot.save_snapshot = FALSE;
11540
11541   SaveEngineSnapshotToList();
11542 }
11543
11544 void GameActions()
11545 {
11546   GameActionsExt();
11547
11548   GameActions_CheckSaveEngineSnapshot();
11549 }
11550
11551 void GameActions_EM_Main()
11552 {
11553   byte effective_action[MAX_PLAYERS];
11554   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11555   int i;
11556
11557   for (i = 0; i < MAX_PLAYERS; i++)
11558     effective_action[i] = stored_player[i].effective_action;
11559
11560   GameActions_EM(effective_action, warp_mode);
11561 }
11562
11563 void GameActions_SP_Main()
11564 {
11565   byte effective_action[MAX_PLAYERS];
11566   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11567   int i;
11568
11569   for (i = 0; i < MAX_PLAYERS; i++)
11570     effective_action[i] = stored_player[i].effective_action;
11571
11572   GameActions_SP(effective_action, warp_mode);
11573
11574   for (i = 0; i < MAX_PLAYERS; i++)
11575   {
11576     if (stored_player[i].force_dropping)
11577       stored_player[i].action |= KEY_BUTTON_DROP;
11578
11579     stored_player[i].force_dropping = FALSE;
11580   }
11581 }
11582
11583 void GameActions_MM_Main()
11584 {
11585   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11586
11587   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11588 }
11589
11590 void GameActions_RND_Main()
11591 {
11592   GameActions_RND();
11593 }
11594
11595 void GameActions_RND()
11596 {
11597   int magic_wall_x = 0, magic_wall_y = 0;
11598   int i, x, y, element, graphic, last_gfx_frame;
11599
11600   InitPlayfieldScanModeVars();
11601
11602   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11603   {
11604     SCAN_PLAYFIELD(x, y)
11605     {
11606       ChangeCount[x][y] = 0;
11607       ChangeEvent[x][y] = -1;
11608     }
11609   }
11610
11611   if (game.set_centered_player)
11612   {
11613     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11614
11615     /* switching to "all players" only possible if all players fit to screen */
11616     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11617     {
11618       game.centered_player_nr_next = game.centered_player_nr;
11619       game.set_centered_player = FALSE;
11620     }
11621
11622     /* do not switch focus to non-existing (or non-active) player */
11623     if (game.centered_player_nr_next >= 0 &&
11624         !stored_player[game.centered_player_nr_next].active)
11625     {
11626       game.centered_player_nr_next = game.centered_player_nr;
11627       game.set_centered_player = FALSE;
11628     }
11629   }
11630
11631   if (game.set_centered_player &&
11632       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11633   {
11634     int sx, sy;
11635
11636     if (game.centered_player_nr_next == -1)
11637     {
11638       setScreenCenteredToAllPlayers(&sx, &sy);
11639     }
11640     else
11641     {
11642       sx = stored_player[game.centered_player_nr_next].jx;
11643       sy = stored_player[game.centered_player_nr_next].jy;
11644     }
11645
11646     game.centered_player_nr = game.centered_player_nr_next;
11647     game.set_centered_player = FALSE;
11648
11649     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11650     DrawGameDoorValues();
11651   }
11652
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654   {
11655     int actual_player_action = stored_player[i].effective_action;
11656
11657 #if 1
11658     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11659        - rnd_equinox_tetrachloride 048
11660        - rnd_equinox_tetrachloride_ii 096
11661        - rnd_emanuel_schmieg 002
11662        - doctor_sloan_ww 001, 020
11663     */
11664     if (stored_player[i].MovPos == 0)
11665       CheckGravityMovement(&stored_player[i]);
11666 #endif
11667
11668     /* overwrite programmed action with tape action */
11669     if (stored_player[i].programmed_action)
11670       actual_player_action = stored_player[i].programmed_action;
11671
11672     PlayerActions(&stored_player[i], actual_player_action);
11673
11674     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11675   }
11676
11677   ScrollScreen(NULL, SCROLL_GO_ON);
11678
11679   /* for backwards compatibility, the following code emulates a fixed bug that
11680      occured when pushing elements (causing elements that just made their last
11681      pushing step to already (if possible) make their first falling step in the
11682      same game frame, which is bad); this code is also needed to use the famous
11683      "spring push bug" which is used in older levels and might be wanted to be
11684      used also in newer levels, but in this case the buggy pushing code is only
11685      affecting the "spring" element and no other elements */
11686
11687   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11688   {
11689     for (i = 0; i < MAX_PLAYERS; i++)
11690     {
11691       struct PlayerInfo *player = &stored_player[i];
11692       int x = player->jx;
11693       int y = player->jy;
11694
11695       if (player->active && player->is_pushing && player->is_moving &&
11696           IS_MOVING(x, y) &&
11697           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11698            Feld[x][y] == EL_SPRING))
11699       {
11700         ContinueMoving(x, y);
11701
11702         /* continue moving after pushing (this is actually a bug) */
11703         if (!IS_MOVING(x, y))
11704           Stop[x][y] = FALSE;
11705       }
11706     }
11707   }
11708
11709   SCAN_PLAYFIELD(x, y)
11710   {
11711     ChangeCount[x][y] = 0;
11712     ChangeEvent[x][y] = -1;
11713
11714     /* this must be handled before main playfield loop */
11715     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11716     {
11717       MovDelay[x][y]--;
11718       if (MovDelay[x][y] <= 0)
11719         RemoveField(x, y);
11720     }
11721
11722     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11723     {
11724       MovDelay[x][y]--;
11725       if (MovDelay[x][y] <= 0)
11726       {
11727         RemoveField(x, y);
11728         TEST_DrawLevelField(x, y);
11729
11730         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11731       }
11732     }
11733
11734 #if DEBUG
11735     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11736     {
11737       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11738       printf("GameActions(): This should never happen!\n");
11739
11740       ChangePage[x][y] = -1;
11741     }
11742 #endif
11743
11744     Stop[x][y] = FALSE;
11745     if (WasJustMoving[x][y] > 0)
11746       WasJustMoving[x][y]--;
11747     if (WasJustFalling[x][y] > 0)
11748       WasJustFalling[x][y]--;
11749     if (CheckCollision[x][y] > 0)
11750       CheckCollision[x][y]--;
11751     if (CheckImpact[x][y] > 0)
11752       CheckImpact[x][y]--;
11753
11754     GfxFrame[x][y]++;
11755
11756     /* reset finished pushing action (not done in ContinueMoving() to allow
11757        continuous pushing animation for elements with zero push delay) */
11758     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11759     {
11760       ResetGfxAnimation(x, y);
11761       TEST_DrawLevelField(x, y);
11762     }
11763
11764 #if DEBUG
11765     if (IS_BLOCKED(x, y))
11766     {
11767       int oldx, oldy;
11768
11769       Blocked2Moving(x, y, &oldx, &oldy);
11770       if (!IS_MOVING(oldx, oldy))
11771       {
11772         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11773         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11774         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11775         printf("GameActions(): This should never happen!\n");
11776       }
11777     }
11778 #endif
11779   }
11780
11781   SCAN_PLAYFIELD(x, y)
11782   {
11783     element = Feld[x][y];
11784     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11785     last_gfx_frame = GfxFrame[x][y];
11786
11787     ResetGfxFrame(x, y);
11788
11789     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11790       DrawLevelGraphicAnimation(x, y, graphic);
11791
11792     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11793         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11794       ResetRandomAnimationValue(x, y);
11795
11796     SetRandomAnimationValue(x, y);
11797
11798     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11799
11800     if (IS_INACTIVE(element))
11801     {
11802       if (IS_ANIMATED(graphic))
11803         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11804
11805       continue;
11806     }
11807
11808     /* this may take place after moving, so 'element' may have changed */
11809     if (IS_CHANGING(x, y) &&
11810         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11811     {
11812       int page = element_info[element].event_page_nr[CE_DELAY];
11813
11814       HandleElementChange(x, y, page);
11815
11816       element = Feld[x][y];
11817       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11818     }
11819
11820     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11821     {
11822       StartMoving(x, y);
11823
11824       element = Feld[x][y];
11825       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11826
11827       if (IS_ANIMATED(graphic) &&
11828           !IS_MOVING(x, y) &&
11829           !Stop[x][y])
11830         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11831
11832       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11833         TEST_DrawTwinkleOnField(x, y);
11834     }
11835     else if (element == EL_ACID)
11836     {
11837       if (!Stop[x][y])
11838         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11839     }
11840     else if ((element == EL_EXIT_OPEN ||
11841               element == EL_EM_EXIT_OPEN ||
11842               element == EL_SP_EXIT_OPEN ||
11843               element == EL_STEEL_EXIT_OPEN ||
11844               element == EL_EM_STEEL_EXIT_OPEN ||
11845               element == EL_SP_TERMINAL ||
11846               element == EL_SP_TERMINAL_ACTIVE ||
11847               element == EL_EXTRA_TIME ||
11848               element == EL_SHIELD_NORMAL ||
11849               element == EL_SHIELD_DEADLY) &&
11850              IS_ANIMATED(graphic))
11851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11852     else if (IS_MOVING(x, y))
11853       ContinueMoving(x, y);
11854     else if (IS_ACTIVE_BOMB(element))
11855       CheckDynamite(x, y);
11856     else if (element == EL_AMOEBA_GROWING)
11857       AmoebeWaechst(x, y);
11858     else if (element == EL_AMOEBA_SHRINKING)
11859       AmoebaDisappearing(x, y);
11860
11861 #if !USE_NEW_AMOEBA_CODE
11862     else if (IS_AMOEBALIVE(element))
11863       AmoebeAbleger(x, y);
11864 #endif
11865
11866     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11867       Life(x, y);
11868     else if (element == EL_EXIT_CLOSED)
11869       CheckExit(x, y);
11870     else if (element == EL_EM_EXIT_CLOSED)
11871       CheckExitEM(x, y);
11872     else if (element == EL_STEEL_EXIT_CLOSED)
11873       CheckExitSteel(x, y);
11874     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11875       CheckExitSteelEM(x, y);
11876     else if (element == EL_SP_EXIT_CLOSED)
11877       CheckExitSP(x, y);
11878     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11879              element == EL_EXPANDABLE_STEELWALL_GROWING)
11880       MauerWaechst(x, y);
11881     else if (element == EL_EXPANDABLE_WALL ||
11882              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11883              element == EL_EXPANDABLE_WALL_VERTICAL ||
11884              element == EL_EXPANDABLE_WALL_ANY ||
11885              element == EL_BD_EXPANDABLE_WALL)
11886       MauerAbleger(x, y);
11887     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11888              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11889              element == EL_EXPANDABLE_STEELWALL_ANY)
11890       MauerAblegerStahl(x, y);
11891     else if (element == EL_FLAMES)
11892       CheckForDragon(x, y);
11893     else if (element == EL_EXPLOSION)
11894       ; /* drawing of correct explosion animation is handled separately */
11895     else if (element == EL_ELEMENT_SNAPPING ||
11896              element == EL_DIAGONAL_SHRINKING ||
11897              element == EL_DIAGONAL_GROWING)
11898     {
11899       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11900
11901       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11902     }
11903     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11904       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11905
11906     if (IS_BELT_ACTIVE(element))
11907       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11908
11909     if (game.magic_wall_active)
11910     {
11911       int jx = local_player->jx, jy = local_player->jy;
11912
11913       /* play the element sound at the position nearest to the player */
11914       if ((element == EL_MAGIC_WALL_FULL ||
11915            element == EL_MAGIC_WALL_ACTIVE ||
11916            element == EL_MAGIC_WALL_EMPTYING ||
11917            element == EL_BD_MAGIC_WALL_FULL ||
11918            element == EL_BD_MAGIC_WALL_ACTIVE ||
11919            element == EL_BD_MAGIC_WALL_EMPTYING ||
11920            element == EL_DC_MAGIC_WALL_FULL ||
11921            element == EL_DC_MAGIC_WALL_ACTIVE ||
11922            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11923           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11924       {
11925         magic_wall_x = x;
11926         magic_wall_y = y;
11927       }
11928     }
11929   }
11930
11931 #if USE_NEW_AMOEBA_CODE
11932   /* new experimental amoeba growth stuff */
11933   if (!(FrameCounter % 8))
11934   {
11935     static unsigned int random = 1684108901;
11936
11937     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11938     {
11939       x = RND(lev_fieldx);
11940       y = RND(lev_fieldy);
11941       element = Feld[x][y];
11942
11943       if (!IS_PLAYER(x,y) &&
11944           (element == EL_EMPTY ||
11945            CAN_GROW_INTO(element) ||
11946            element == EL_QUICKSAND_EMPTY ||
11947            element == EL_QUICKSAND_FAST_EMPTY ||
11948            element == EL_ACID_SPLASH_LEFT ||
11949            element == EL_ACID_SPLASH_RIGHT))
11950       {
11951         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11952             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11953             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11954             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11955           Feld[x][y] = EL_AMOEBA_DROP;
11956       }
11957
11958       random = random * 129 + 1;
11959     }
11960   }
11961 #endif
11962
11963   game.explosions_delayed = FALSE;
11964
11965   SCAN_PLAYFIELD(x, y)
11966   {
11967     element = Feld[x][y];
11968
11969     if (ExplodeField[x][y])
11970       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11971     else if (element == EL_EXPLOSION)
11972       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11973
11974     ExplodeField[x][y] = EX_TYPE_NONE;
11975   }
11976
11977   game.explosions_delayed = TRUE;
11978
11979   if (game.magic_wall_active)
11980   {
11981     if (!(game.magic_wall_time_left % 4))
11982     {
11983       int element = Feld[magic_wall_x][magic_wall_y];
11984
11985       if (element == EL_BD_MAGIC_WALL_FULL ||
11986           element == EL_BD_MAGIC_WALL_ACTIVE ||
11987           element == EL_BD_MAGIC_WALL_EMPTYING)
11988         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11989       else if (element == EL_DC_MAGIC_WALL_FULL ||
11990                element == EL_DC_MAGIC_WALL_ACTIVE ||
11991                element == EL_DC_MAGIC_WALL_EMPTYING)
11992         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11993       else
11994         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11995     }
11996
11997     if (game.magic_wall_time_left > 0)
11998     {
11999       game.magic_wall_time_left--;
12000
12001       if (!game.magic_wall_time_left)
12002       {
12003         SCAN_PLAYFIELD(x, y)
12004         {
12005           element = Feld[x][y];
12006
12007           if (element == EL_MAGIC_WALL_ACTIVE ||
12008               element == EL_MAGIC_WALL_FULL)
12009           {
12010             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12011             TEST_DrawLevelField(x, y);
12012           }
12013           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12014                    element == EL_BD_MAGIC_WALL_FULL)
12015           {
12016             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12017             TEST_DrawLevelField(x, y);
12018           }
12019           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12020                    element == EL_DC_MAGIC_WALL_FULL)
12021           {
12022             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12023             TEST_DrawLevelField(x, y);
12024           }
12025         }
12026
12027         game.magic_wall_active = FALSE;
12028       }
12029     }
12030   }
12031
12032   if (game.light_time_left > 0)
12033   {
12034     game.light_time_left--;
12035
12036     if (game.light_time_left == 0)
12037       RedrawAllLightSwitchesAndInvisibleElements();
12038   }
12039
12040   if (game.timegate_time_left > 0)
12041   {
12042     game.timegate_time_left--;
12043
12044     if (game.timegate_time_left == 0)
12045       CloseAllOpenTimegates();
12046   }
12047
12048   if (game.lenses_time_left > 0)
12049   {
12050     game.lenses_time_left--;
12051
12052     if (game.lenses_time_left == 0)
12053       RedrawAllInvisibleElementsForLenses();
12054   }
12055
12056   if (game.magnify_time_left > 0)
12057   {
12058     game.magnify_time_left--;
12059
12060     if (game.magnify_time_left == 0)
12061       RedrawAllInvisibleElementsForMagnifier();
12062   }
12063
12064   for (i = 0; i < MAX_PLAYERS; i++)
12065   {
12066     struct PlayerInfo *player = &stored_player[i];
12067
12068     if (SHIELD_ON(player))
12069     {
12070       if (player->shield_deadly_time_left)
12071         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12072       else if (player->shield_normal_time_left)
12073         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12074     }
12075   }
12076
12077 #if USE_DELAYED_GFX_REDRAW
12078   SCAN_PLAYFIELD(x, y)
12079   {
12080     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12081     {
12082       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12083          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12084
12085       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12086         DrawLevelField(x, y);
12087
12088       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12089         DrawLevelFieldCrumbled(x, y);
12090
12091       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12092         DrawLevelFieldCrumbledNeighbours(x, y);
12093
12094       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12095         DrawTwinkleOnField(x, y);
12096     }
12097
12098     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12099   }
12100 #endif
12101
12102   DrawAllPlayers();
12103   PlayAllPlayersSound();
12104
12105   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12106   {
12107     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12108
12109     local_player->show_envelope = 0;
12110   }
12111
12112   /* use random number generator in every frame to make it less predictable */
12113   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12114     RND(1);
12115 }
12116
12117 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12118 {
12119   int min_x = x, min_y = y, max_x = x, max_y = y;
12120   int i;
12121
12122   for (i = 0; i < MAX_PLAYERS; i++)
12123   {
12124     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12125
12126     if (!stored_player[i].active || &stored_player[i] == player)
12127       continue;
12128
12129     min_x = MIN(min_x, jx);
12130     min_y = MIN(min_y, jy);
12131     max_x = MAX(max_x, jx);
12132     max_y = MAX(max_y, jy);
12133   }
12134
12135   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12136 }
12137
12138 static boolean AllPlayersInVisibleScreen()
12139 {
12140   int i;
12141
12142   for (i = 0; i < MAX_PLAYERS; i++)
12143   {
12144     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12145
12146     if (!stored_player[i].active)
12147       continue;
12148
12149     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12150       return FALSE;
12151   }
12152
12153   return TRUE;
12154 }
12155
12156 void ScrollLevel(int dx, int dy)
12157 {
12158   int scroll_offset = 2 * TILEX_VAR;
12159   int x, y;
12160
12161   BlitBitmap(drawto_field, drawto_field,
12162              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12163              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12164              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12165              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12166              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12167              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12168
12169   if (dx != 0)
12170   {
12171     x = (dx == 1 ? BX1 : BX2);
12172     for (y = BY1; y <= BY2; y++)
12173       DrawScreenField(x, y);
12174   }
12175
12176   if (dy != 0)
12177   {
12178     y = (dy == 1 ? BY1 : BY2);
12179     for (x = BX1; x <= BX2; x++)
12180       DrawScreenField(x, y);
12181   }
12182
12183   redraw_mask |= REDRAW_FIELD;
12184 }
12185
12186 static boolean canFallDown(struct PlayerInfo *player)
12187 {
12188   int jx = player->jx, jy = player->jy;
12189
12190   return (IN_LEV_FIELD(jx, jy + 1) &&
12191           (IS_FREE(jx, jy + 1) ||
12192            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12193           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12194           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12195 }
12196
12197 static boolean canPassField(int x, int y, int move_dir)
12198 {
12199   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12200   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12201   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12202   int nextx = x + dx;
12203   int nexty = y + dy;
12204   int element = Feld[x][y];
12205
12206   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12207           !CAN_MOVE(element) &&
12208           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12209           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12210           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12211 }
12212
12213 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12214 {
12215   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12216   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12217   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12218   int newx = x + dx;
12219   int newy = y + dy;
12220
12221   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12222           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12223           (IS_DIGGABLE(Feld[newx][newy]) ||
12224            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12225            canPassField(newx, newy, move_dir)));
12226 }
12227
12228 static void CheckGravityMovement(struct PlayerInfo *player)
12229 {
12230   if (player->gravity && !player->programmed_action)
12231   {
12232     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12233     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12234     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12235     int jx = player->jx, jy = player->jy;
12236     boolean player_is_moving_to_valid_field =
12237       (!player_is_snapping &&
12238        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12239         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12240     boolean player_can_fall_down = canFallDown(player);
12241
12242     if (player_can_fall_down &&
12243         !player_is_moving_to_valid_field)
12244       player->programmed_action = MV_DOWN;
12245   }
12246 }
12247
12248 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12249 {
12250   return CheckGravityMovement(player);
12251
12252   if (player->gravity && !player->programmed_action)
12253   {
12254     int jx = player->jx, jy = player->jy;
12255     boolean field_under_player_is_free =
12256       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12257     boolean player_is_standing_on_valid_field =
12258       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12259        (IS_WALKABLE(Feld[jx][jy]) &&
12260         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12261
12262     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12263       player->programmed_action = MV_DOWN;
12264   }
12265 }
12266
12267 /*
12268   MovePlayerOneStep()
12269   -----------------------------------------------------------------------------
12270   dx, dy:               direction (non-diagonal) to try to move the player to
12271   real_dx, real_dy:     direction as read from input device (can be diagonal)
12272 */
12273
12274 boolean MovePlayerOneStep(struct PlayerInfo *player,
12275                           int dx, int dy, int real_dx, int real_dy)
12276 {
12277   int jx = player->jx, jy = player->jy;
12278   int new_jx = jx + dx, new_jy = jy + dy;
12279   int can_move;
12280   boolean player_can_move = !player->cannot_move;
12281
12282   if (!player->active || (!dx && !dy))
12283     return MP_NO_ACTION;
12284
12285   player->MovDir = (dx < 0 ? MV_LEFT :
12286                     dx > 0 ? MV_RIGHT :
12287                     dy < 0 ? MV_UP :
12288                     dy > 0 ? MV_DOWN :  MV_NONE);
12289
12290   if (!IN_LEV_FIELD(new_jx, new_jy))
12291     return MP_NO_ACTION;
12292
12293   if (!player_can_move)
12294   {
12295     if (player->MovPos == 0)
12296     {
12297       player->is_moving = FALSE;
12298       player->is_digging = FALSE;
12299       player->is_collecting = FALSE;
12300       player->is_snapping = FALSE;
12301       player->is_pushing = FALSE;
12302     }
12303   }
12304
12305   if (!network.enabled && game.centered_player_nr == -1 &&
12306       !AllPlayersInSight(player, new_jx, new_jy))
12307     return MP_NO_ACTION;
12308
12309   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12310   if (can_move != MP_MOVING)
12311     return can_move;
12312
12313   /* check if DigField() has caused relocation of the player */
12314   if (player->jx != jx || player->jy != jy)
12315     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12316
12317   StorePlayer[jx][jy] = 0;
12318   player->last_jx = jx;
12319   player->last_jy = jy;
12320   player->jx = new_jx;
12321   player->jy = new_jy;
12322   StorePlayer[new_jx][new_jy] = player->element_nr;
12323
12324   if (player->move_delay_value_next != -1)
12325   {
12326     player->move_delay_value = player->move_delay_value_next;
12327     player->move_delay_value_next = -1;
12328   }
12329
12330   player->MovPos =
12331     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12332
12333   player->step_counter++;
12334
12335   PlayerVisit[jx][jy] = FrameCounter;
12336
12337   player->is_moving = TRUE;
12338
12339 #if 1
12340   /* should better be called in MovePlayer(), but this breaks some tapes */
12341   ScrollPlayer(player, SCROLL_INIT);
12342 #endif
12343
12344   return MP_MOVING;
12345 }
12346
12347 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12348 {
12349   int jx = player->jx, jy = player->jy;
12350   int old_jx = jx, old_jy = jy;
12351   int moved = MP_NO_ACTION;
12352
12353   if (!player->active)
12354     return FALSE;
12355
12356   if (!dx && !dy)
12357   {
12358     if (player->MovPos == 0)
12359     {
12360       player->is_moving = FALSE;
12361       player->is_digging = FALSE;
12362       player->is_collecting = FALSE;
12363       player->is_snapping = FALSE;
12364       player->is_pushing = FALSE;
12365     }
12366
12367     return FALSE;
12368   }
12369
12370   if (player->move_delay > 0)
12371     return FALSE;
12372
12373   player->move_delay = -1;              /* set to "uninitialized" value */
12374
12375   /* store if player is automatically moved to next field */
12376   player->is_auto_moving = (player->programmed_action != MV_NONE);
12377
12378   /* remove the last programmed player action */
12379   player->programmed_action = 0;
12380
12381   if (player->MovPos)
12382   {
12383     /* should only happen if pre-1.2 tape recordings are played */
12384     /* this is only for backward compatibility */
12385
12386     int original_move_delay_value = player->move_delay_value;
12387
12388 #if DEBUG
12389     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12390            tape.counter);
12391 #endif
12392
12393     /* scroll remaining steps with finest movement resolution */
12394     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12395
12396     while (player->MovPos)
12397     {
12398       ScrollPlayer(player, SCROLL_GO_ON);
12399       ScrollScreen(NULL, SCROLL_GO_ON);
12400
12401       AdvanceFrameAndPlayerCounters(player->index_nr);
12402
12403       DrawAllPlayers();
12404       BackToFront_WithFrameDelay(0);
12405     }
12406
12407     player->move_delay_value = original_move_delay_value;
12408   }
12409
12410   player->is_active = FALSE;
12411
12412   if (player->last_move_dir & MV_HORIZONTAL)
12413   {
12414     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12415       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12416   }
12417   else
12418   {
12419     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12420       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12421   }
12422
12423   if (!moved && !player->is_active)
12424   {
12425     player->is_moving = FALSE;
12426     player->is_digging = FALSE;
12427     player->is_collecting = FALSE;
12428     player->is_snapping = FALSE;
12429     player->is_pushing = FALSE;
12430   }
12431
12432   jx = player->jx;
12433   jy = player->jy;
12434
12435   if (moved & MP_MOVING && !ScreenMovPos &&
12436       (player->index_nr == game.centered_player_nr ||
12437        game.centered_player_nr == -1))
12438   {
12439     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12440     int offset = game.scroll_delay_value;
12441
12442     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12443     {
12444       /* actual player has left the screen -- scroll in that direction */
12445       if (jx != old_jx)         /* player has moved horizontally */
12446         scroll_x += (jx - old_jx);
12447       else                      /* player has moved vertically */
12448         scroll_y += (jy - old_jy);
12449     }
12450     else
12451     {
12452       if (jx != old_jx)         /* player has moved horizontally */
12453       {
12454         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12455             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12456           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12457
12458         /* don't scroll over playfield boundaries */
12459         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12460           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12461
12462         /* don't scroll more than one field at a time */
12463         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12464
12465         /* don't scroll against the player's moving direction */
12466         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12467             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12468           scroll_x = old_scroll_x;
12469       }
12470       else                      /* player has moved vertically */
12471       {
12472         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12473             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12474           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12475
12476         /* don't scroll over playfield boundaries */
12477         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12478           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12479
12480         /* don't scroll more than one field at a time */
12481         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12482
12483         /* don't scroll against the player's moving direction */
12484         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12485             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12486           scroll_y = old_scroll_y;
12487       }
12488     }
12489
12490     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12491     {
12492       if (!network.enabled && game.centered_player_nr == -1 &&
12493           !AllPlayersInVisibleScreen())
12494       {
12495         scroll_x = old_scroll_x;
12496         scroll_y = old_scroll_y;
12497       }
12498       else
12499       {
12500         ScrollScreen(player, SCROLL_INIT);
12501         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12502       }
12503     }
12504   }
12505
12506   player->StepFrame = 0;
12507
12508   if (moved & MP_MOVING)
12509   {
12510     if (old_jx != jx && old_jy == jy)
12511       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12512     else if (old_jx == jx && old_jy != jy)
12513       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12514
12515     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12516
12517     player->last_move_dir = player->MovDir;
12518     player->is_moving = TRUE;
12519     player->is_snapping = FALSE;
12520     player->is_switching = FALSE;
12521     player->is_dropping = FALSE;
12522     player->is_dropping_pressed = FALSE;
12523     player->drop_pressed_delay = 0;
12524
12525 #if 0
12526     /* should better be called here than above, but this breaks some tapes */
12527     ScrollPlayer(player, SCROLL_INIT);
12528 #endif
12529   }
12530   else
12531   {
12532     CheckGravityMovementWhenNotMoving(player);
12533
12534     player->is_moving = FALSE;
12535
12536     /* at this point, the player is allowed to move, but cannot move right now
12537        (e.g. because of something blocking the way) -- ensure that the player
12538        is also allowed to move in the next frame (in old versions before 3.1.1,
12539        the player was forced to wait again for eight frames before next try) */
12540
12541     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12542       player->move_delay = 0;   /* allow direct movement in the next frame */
12543   }
12544
12545   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12546     player->move_delay = player->move_delay_value;
12547
12548   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12549   {
12550     TestIfPlayerTouchesBadThing(jx, jy);
12551     TestIfPlayerTouchesCustomElement(jx, jy);
12552   }
12553
12554   if (!player->active)
12555     RemovePlayer(player);
12556
12557   return moved;
12558 }
12559
12560 void ScrollPlayer(struct PlayerInfo *player, int mode)
12561 {
12562   int jx = player->jx, jy = player->jy;
12563   int last_jx = player->last_jx, last_jy = player->last_jy;
12564   int move_stepsize = TILEX / player->move_delay_value;
12565
12566   if (!player->active)
12567     return;
12568
12569   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12570     return;
12571
12572   if (mode == SCROLL_INIT)
12573   {
12574     player->actual_frame_counter = FrameCounter;
12575     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12576
12577     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12578         Feld[last_jx][last_jy] == EL_EMPTY)
12579     {
12580       int last_field_block_delay = 0;   /* start with no blocking at all */
12581       int block_delay_adjustment = player->block_delay_adjustment;
12582
12583       /* if player blocks last field, add delay for exactly one move */
12584       if (player->block_last_field)
12585       {
12586         last_field_block_delay += player->move_delay_value;
12587
12588         /* when blocking enabled, prevent moving up despite gravity */
12589         if (player->gravity && player->MovDir == MV_UP)
12590           block_delay_adjustment = -1;
12591       }
12592
12593       /* add block delay adjustment (also possible when not blocking) */
12594       last_field_block_delay += block_delay_adjustment;
12595
12596       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12597       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12598     }
12599
12600     if (player->MovPos != 0)    /* player has not yet reached destination */
12601       return;
12602   }
12603   else if (!FrameReached(&player->actual_frame_counter, 1))
12604     return;
12605
12606   if (player->MovPos != 0)
12607   {
12608     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12609     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12610
12611     /* before DrawPlayer() to draw correct player graphic for this case */
12612     if (player->MovPos == 0)
12613       CheckGravityMovement(player);
12614   }
12615
12616   if (player->MovPos == 0)      /* player reached destination field */
12617   {
12618     if (player->move_delay_reset_counter > 0)
12619     {
12620       player->move_delay_reset_counter--;
12621
12622       if (player->move_delay_reset_counter == 0)
12623       {
12624         /* continue with normal speed after quickly moving through gate */
12625         HALVE_PLAYER_SPEED(player);
12626
12627         /* be able to make the next move without delay */
12628         player->move_delay = 0;
12629       }
12630     }
12631
12632     player->last_jx = jx;
12633     player->last_jy = jy;
12634
12635     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12636         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12637         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12638         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12639         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12640         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12641         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12642         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12643     {
12644       DrawPlayer(player);       /* needed here only to cleanup last field */
12645       RemovePlayer(player);
12646
12647       if (local_player->friends_still_needed == 0 ||
12648           IS_SP_ELEMENT(Feld[jx][jy]))
12649         PlayerWins(player);
12650     }
12651
12652     /* this breaks one level: "machine", level 000 */
12653     {
12654       int move_direction = player->MovDir;
12655       int enter_side = MV_DIR_OPPOSITE(move_direction);
12656       int leave_side = move_direction;
12657       int old_jx = last_jx;
12658       int old_jy = last_jy;
12659       int old_element = Feld[old_jx][old_jy];
12660       int new_element = Feld[jx][jy];
12661
12662       if (IS_CUSTOM_ELEMENT(old_element))
12663         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12664                                    CE_LEFT_BY_PLAYER,
12665                                    player->index_bit, leave_side);
12666
12667       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12668                                           CE_PLAYER_LEAVES_X,
12669                                           player->index_bit, leave_side);
12670
12671       if (IS_CUSTOM_ELEMENT(new_element))
12672         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12673                                    player->index_bit, enter_side);
12674
12675       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12676                                           CE_PLAYER_ENTERS_X,
12677                                           player->index_bit, enter_side);
12678
12679       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12680                                         CE_MOVE_OF_X, move_direction);
12681     }
12682
12683     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12684     {
12685       TestIfPlayerTouchesBadThing(jx, jy);
12686       TestIfPlayerTouchesCustomElement(jx, jy);
12687
12688       /* needed because pushed element has not yet reached its destination,
12689          so it would trigger a change event at its previous field location */
12690       if (!player->is_pushing)
12691         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12692
12693       if (!player->active)
12694         RemovePlayer(player);
12695     }
12696
12697     if (!local_player->LevelSolved && level.use_step_counter)
12698     {
12699       int i;
12700
12701       TimePlayed++;
12702
12703       if (TimeLeft > 0)
12704       {
12705         TimeLeft--;
12706
12707         if (TimeLeft <= 10 && setup.time_limit)
12708           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12709
12710         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12711
12712         DisplayGameControlValues();
12713
12714         if (!TimeLeft && setup.time_limit)
12715           for (i = 0; i < MAX_PLAYERS; i++)
12716             KillPlayer(&stored_player[i]);
12717       }
12718       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12719       {
12720         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12721
12722         DisplayGameControlValues();
12723       }
12724     }
12725
12726     if (tape.single_step && tape.recording && !tape.pausing &&
12727         !player->programmed_action)
12728       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12729
12730     if (!player->programmed_action)
12731       CheckSaveEngineSnapshot(player);
12732   }
12733 }
12734
12735 void ScrollScreen(struct PlayerInfo *player, int mode)
12736 {
12737   static unsigned int screen_frame_counter = 0;
12738
12739   if (mode == SCROLL_INIT)
12740   {
12741     /* set scrolling step size according to actual player's moving speed */
12742     ScrollStepSize = TILEX / player->move_delay_value;
12743
12744     screen_frame_counter = FrameCounter;
12745     ScreenMovDir = player->MovDir;
12746     ScreenMovPos = player->MovPos;
12747     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12748     return;
12749   }
12750   else if (!FrameReached(&screen_frame_counter, 1))
12751     return;
12752
12753   if (ScreenMovPos)
12754   {
12755     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12756     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12757     redraw_mask |= REDRAW_FIELD;
12758   }
12759   else
12760     ScreenMovDir = MV_NONE;
12761 }
12762
12763 void TestIfPlayerTouchesCustomElement(int x, int y)
12764 {
12765   static int xy[4][2] =
12766   {
12767     { 0, -1 },
12768     { -1, 0 },
12769     { +1, 0 },
12770     { 0, +1 }
12771   };
12772   static int trigger_sides[4][2] =
12773   {
12774     /* center side       border side */
12775     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12776     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12777     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12778     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12779   };
12780   static int touch_dir[4] =
12781   {
12782     MV_LEFT | MV_RIGHT,
12783     MV_UP   | MV_DOWN,
12784     MV_UP   | MV_DOWN,
12785     MV_LEFT | MV_RIGHT
12786   };
12787   int center_element = Feld[x][y];      /* should always be non-moving! */
12788   int i;
12789
12790   for (i = 0; i < NUM_DIRECTIONS; i++)
12791   {
12792     int xx = x + xy[i][0];
12793     int yy = y + xy[i][1];
12794     int center_side = trigger_sides[i][0];
12795     int border_side = trigger_sides[i][1];
12796     int border_element;
12797
12798     if (!IN_LEV_FIELD(xx, yy))
12799       continue;
12800
12801     if (IS_PLAYER(x, y))                /* player found at center element */
12802     {
12803       struct PlayerInfo *player = PLAYERINFO(x, y);
12804
12805       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12806         border_element = Feld[xx][yy];          /* may be moving! */
12807       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12808         border_element = Feld[xx][yy];
12809       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12810         border_element = MovingOrBlocked2Element(xx, yy);
12811       else
12812         continue;               /* center and border element do not touch */
12813
12814       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12815                                  player->index_bit, border_side);
12816       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12817                                           CE_PLAYER_TOUCHES_X,
12818                                           player->index_bit, border_side);
12819
12820       {
12821         /* use player element that is initially defined in the level playfield,
12822            not the player element that corresponds to the runtime player number
12823            (example: a level that contains EL_PLAYER_3 as the only player would
12824            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12825         int player_element = PLAYERINFO(x, y)->initial_element;
12826
12827         CheckElementChangeBySide(xx, yy, border_element, player_element,
12828                                  CE_TOUCHING_X, border_side);
12829       }
12830     }
12831     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12832     {
12833       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12834
12835       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12836       {
12837         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12838           continue;             /* center and border element do not touch */
12839       }
12840
12841       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12842                                  player->index_bit, center_side);
12843       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12844                                           CE_PLAYER_TOUCHES_X,
12845                                           player->index_bit, center_side);
12846
12847       {
12848         /* use player element that is initially defined in the level playfield,
12849            not the player element that corresponds to the runtime player number
12850            (example: a level that contains EL_PLAYER_3 as the only player would
12851            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12852         int player_element = PLAYERINFO(xx, yy)->initial_element;
12853
12854         CheckElementChangeBySide(x, y, center_element, player_element,
12855                                  CE_TOUCHING_X, center_side);
12856       }
12857
12858       break;
12859     }
12860   }
12861 }
12862
12863 void TestIfElementTouchesCustomElement(int x, int y)
12864 {
12865   static int xy[4][2] =
12866   {
12867     { 0, -1 },
12868     { -1, 0 },
12869     { +1, 0 },
12870     { 0, +1 }
12871   };
12872   static int trigger_sides[4][2] =
12873   {
12874     /* center side      border side */
12875     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12876     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12877     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12878     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12879   };
12880   static int touch_dir[4] =
12881   {
12882     MV_LEFT | MV_RIGHT,
12883     MV_UP   | MV_DOWN,
12884     MV_UP   | MV_DOWN,
12885     MV_LEFT | MV_RIGHT
12886   };
12887   boolean change_center_element = FALSE;
12888   int center_element = Feld[x][y];      /* should always be non-moving! */
12889   int border_element_old[NUM_DIRECTIONS];
12890   int i;
12891
12892   for (i = 0; i < NUM_DIRECTIONS; i++)
12893   {
12894     int xx = x + xy[i][0];
12895     int yy = y + xy[i][1];
12896     int border_element;
12897
12898     border_element_old[i] = -1;
12899
12900     if (!IN_LEV_FIELD(xx, yy))
12901       continue;
12902
12903     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12904       border_element = Feld[xx][yy];    /* may be moving! */
12905     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12906       border_element = Feld[xx][yy];
12907     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12908       border_element = MovingOrBlocked2Element(xx, yy);
12909     else
12910       continue;                 /* center and border element do not touch */
12911
12912     border_element_old[i] = border_element;
12913   }
12914
12915   for (i = 0; i < NUM_DIRECTIONS; i++)
12916   {
12917     int xx = x + xy[i][0];
12918     int yy = y + xy[i][1];
12919     int center_side = trigger_sides[i][0];
12920     int border_element = border_element_old[i];
12921
12922     if (border_element == -1)
12923       continue;
12924
12925     /* check for change of border element */
12926     CheckElementChangeBySide(xx, yy, border_element, center_element,
12927                              CE_TOUCHING_X, center_side);
12928
12929     /* (center element cannot be player, so we dont have to check this here) */
12930   }
12931
12932   for (i = 0; i < NUM_DIRECTIONS; i++)
12933   {
12934     int xx = x + xy[i][0];
12935     int yy = y + xy[i][1];
12936     int border_side = trigger_sides[i][1];
12937     int border_element = border_element_old[i];
12938
12939     if (border_element == -1)
12940       continue;
12941
12942     /* check for change of center element (but change it only once) */
12943     if (!change_center_element)
12944       change_center_element =
12945         CheckElementChangeBySide(x, y, center_element, border_element,
12946                                  CE_TOUCHING_X, border_side);
12947
12948     if (IS_PLAYER(xx, yy))
12949     {
12950       /* use player element that is initially defined in the level playfield,
12951          not the player element that corresponds to the runtime player number
12952          (example: a level that contains EL_PLAYER_3 as the only player would
12953          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12954       int player_element = PLAYERINFO(xx, yy)->initial_element;
12955
12956       CheckElementChangeBySide(x, y, center_element, player_element,
12957                                CE_TOUCHING_X, border_side);
12958     }
12959   }
12960 }
12961
12962 void TestIfElementHitsCustomElement(int x, int y, int direction)
12963 {
12964   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12965   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12966   int hitx = x + dx, hity = y + dy;
12967   int hitting_element = Feld[x][y];
12968   int touched_element;
12969
12970   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12971     return;
12972
12973   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12974                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12975
12976   if (IN_LEV_FIELD(hitx, hity))
12977   {
12978     int opposite_direction = MV_DIR_OPPOSITE(direction);
12979     int hitting_side = direction;
12980     int touched_side = opposite_direction;
12981     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12982                           MovDir[hitx][hity] != direction ||
12983                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12984
12985     object_hit = TRUE;
12986
12987     if (object_hit)
12988     {
12989       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12990                                CE_HITTING_X, touched_side);
12991
12992       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12993                                CE_HIT_BY_X, hitting_side);
12994
12995       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12996                                CE_HIT_BY_SOMETHING, opposite_direction);
12997
12998       if (IS_PLAYER(hitx, hity))
12999       {
13000         /* use player element that is initially defined in the level playfield,
13001            not the player element that corresponds to the runtime player number
13002            (example: a level that contains EL_PLAYER_3 as the only player would
13003            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13004         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13005
13006         CheckElementChangeBySide(x, y, hitting_element, player_element,
13007                                  CE_HITTING_X, touched_side);
13008       }
13009     }
13010   }
13011
13012   /* "hitting something" is also true when hitting the playfield border */
13013   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13014                            CE_HITTING_SOMETHING, direction);
13015 }
13016
13017 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13018 {
13019   int i, kill_x = -1, kill_y = -1;
13020
13021   int bad_element = -1;
13022   static int test_xy[4][2] =
13023   {
13024     { 0, -1 },
13025     { -1, 0 },
13026     { +1, 0 },
13027     { 0, +1 }
13028   };
13029   static int test_dir[4] =
13030   {
13031     MV_UP,
13032     MV_LEFT,
13033     MV_RIGHT,
13034     MV_DOWN
13035   };
13036
13037   for (i = 0; i < NUM_DIRECTIONS; i++)
13038   {
13039     int test_x, test_y, test_move_dir, test_element;
13040
13041     test_x = good_x + test_xy[i][0];
13042     test_y = good_y + test_xy[i][1];
13043
13044     if (!IN_LEV_FIELD(test_x, test_y))
13045       continue;
13046
13047     test_move_dir =
13048       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13049
13050     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13051
13052     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13053        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13054     */
13055     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13056         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13057     {
13058       kill_x = test_x;
13059       kill_y = test_y;
13060       bad_element = test_element;
13061
13062       break;
13063     }
13064   }
13065
13066   if (kill_x != -1 || kill_y != -1)
13067   {
13068     if (IS_PLAYER(good_x, good_y))
13069     {
13070       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13071
13072       if (player->shield_deadly_time_left > 0 &&
13073           !IS_INDESTRUCTIBLE(bad_element))
13074         Bang(kill_x, kill_y);
13075       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13076         KillPlayer(player);
13077     }
13078     else
13079       Bang(good_x, good_y);
13080   }
13081 }
13082
13083 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13084 {
13085   int i, kill_x = -1, kill_y = -1;
13086   int bad_element = Feld[bad_x][bad_y];
13087   static int test_xy[4][2] =
13088   {
13089     { 0, -1 },
13090     { -1, 0 },
13091     { +1, 0 },
13092     { 0, +1 }
13093   };
13094   static int touch_dir[4] =
13095   {
13096     MV_LEFT | MV_RIGHT,
13097     MV_UP   | MV_DOWN,
13098     MV_UP   | MV_DOWN,
13099     MV_LEFT | MV_RIGHT
13100   };
13101   static int test_dir[4] =
13102   {
13103     MV_UP,
13104     MV_LEFT,
13105     MV_RIGHT,
13106     MV_DOWN
13107   };
13108
13109   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13110     return;
13111
13112   for (i = 0; i < NUM_DIRECTIONS; i++)
13113   {
13114     int test_x, test_y, test_move_dir, test_element;
13115
13116     test_x = bad_x + test_xy[i][0];
13117     test_y = bad_y + test_xy[i][1];
13118
13119     if (!IN_LEV_FIELD(test_x, test_y))
13120       continue;
13121
13122     test_move_dir =
13123       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13124
13125     test_element = Feld[test_x][test_y];
13126
13127     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13128        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13129     */
13130     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13131         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13132     {
13133       /* good thing is player or penguin that does not move away */
13134       if (IS_PLAYER(test_x, test_y))
13135       {
13136         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13137
13138         if (bad_element == EL_ROBOT && player->is_moving)
13139           continue;     /* robot does not kill player if he is moving */
13140
13141         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13142         {
13143           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13144             continue;           /* center and border element do not touch */
13145         }
13146
13147         kill_x = test_x;
13148         kill_y = test_y;
13149
13150         break;
13151       }
13152       else if (test_element == EL_PENGUIN)
13153       {
13154         kill_x = test_x;
13155         kill_y = test_y;
13156
13157         break;
13158       }
13159     }
13160   }
13161
13162   if (kill_x != -1 || kill_y != -1)
13163   {
13164     if (IS_PLAYER(kill_x, kill_y))
13165     {
13166       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13167
13168       if (player->shield_deadly_time_left > 0 &&
13169           !IS_INDESTRUCTIBLE(bad_element))
13170         Bang(bad_x, bad_y);
13171       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13172         KillPlayer(player);
13173     }
13174     else
13175       Bang(kill_x, kill_y);
13176   }
13177 }
13178
13179 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13180 {
13181   int bad_element = Feld[bad_x][bad_y];
13182   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13183   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13184   int test_x = bad_x + dx, test_y = bad_y + dy;
13185   int test_move_dir, test_element;
13186   int kill_x = -1, kill_y = -1;
13187
13188   if (!IN_LEV_FIELD(test_x, test_y))
13189     return;
13190
13191   test_move_dir =
13192     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13193
13194   test_element = Feld[test_x][test_y];
13195
13196   if (test_move_dir != bad_move_dir)
13197   {
13198     /* good thing can be player or penguin that does not move away */
13199     if (IS_PLAYER(test_x, test_y))
13200     {
13201       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13202
13203       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13204          player as being hit when he is moving towards the bad thing, because
13205          the "get hit by" condition would be lost after the player stops) */
13206       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13207         return;         /* player moves away from bad thing */
13208
13209       kill_x = test_x;
13210       kill_y = test_y;
13211     }
13212     else if (test_element == EL_PENGUIN)
13213     {
13214       kill_x = test_x;
13215       kill_y = test_y;
13216     }
13217   }
13218
13219   if (kill_x != -1 || kill_y != -1)
13220   {
13221     if (IS_PLAYER(kill_x, kill_y))
13222     {
13223       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13224
13225       if (player->shield_deadly_time_left > 0 &&
13226           !IS_INDESTRUCTIBLE(bad_element))
13227         Bang(bad_x, bad_y);
13228       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13229         KillPlayer(player);
13230     }
13231     else
13232       Bang(kill_x, kill_y);
13233   }
13234 }
13235
13236 void TestIfPlayerTouchesBadThing(int x, int y)
13237 {
13238   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13239 }
13240
13241 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13242 {
13243   TestIfGoodThingHitsBadThing(x, y, move_dir);
13244 }
13245
13246 void TestIfBadThingTouchesPlayer(int x, int y)
13247 {
13248   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13249 }
13250
13251 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13252 {
13253   TestIfBadThingHitsGoodThing(x, y, move_dir);
13254 }
13255
13256 void TestIfFriendTouchesBadThing(int x, int y)
13257 {
13258   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13259 }
13260
13261 void TestIfBadThingTouchesFriend(int x, int y)
13262 {
13263   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13264 }
13265
13266 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13267 {
13268   int i, kill_x = bad_x, kill_y = bad_y;
13269   static int xy[4][2] =
13270   {
13271     { 0, -1 },
13272     { -1, 0 },
13273     { +1, 0 },
13274     { 0, +1 }
13275   };
13276
13277   for (i = 0; i < NUM_DIRECTIONS; i++)
13278   {
13279     int x, y, element;
13280
13281     x = bad_x + xy[i][0];
13282     y = bad_y + xy[i][1];
13283     if (!IN_LEV_FIELD(x, y))
13284       continue;
13285
13286     element = Feld[x][y];
13287     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13288         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13289     {
13290       kill_x = x;
13291       kill_y = y;
13292       break;
13293     }
13294   }
13295
13296   if (kill_x != bad_x || kill_y != bad_y)
13297     Bang(bad_x, bad_y);
13298 }
13299
13300 void KillPlayer(struct PlayerInfo *player)
13301 {
13302   int jx = player->jx, jy = player->jy;
13303
13304   if (!player->active)
13305     return;
13306
13307 #if 0
13308   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13309          player->killed, player->active, player->reanimated);
13310 #endif
13311
13312   /* the following code was introduced to prevent an infinite loop when calling
13313      -> Bang()
13314      -> CheckTriggeredElementChangeExt()
13315      -> ExecuteCustomElementAction()
13316      -> KillPlayer()
13317      -> (infinitely repeating the above sequence of function calls)
13318      which occurs when killing the player while having a CE with the setting
13319      "kill player X when explosion of <player X>"; the solution using a new
13320      field "player->killed" was chosen for backwards compatibility, although
13321      clever use of the fields "player->active" etc. would probably also work */
13322 #if 1
13323   if (player->killed)
13324     return;
13325 #endif
13326
13327   player->killed = TRUE;
13328
13329   /* remove accessible field at the player's position */
13330   Feld[jx][jy] = EL_EMPTY;
13331
13332   /* deactivate shield (else Bang()/Explode() would not work right) */
13333   player->shield_normal_time_left = 0;
13334   player->shield_deadly_time_left = 0;
13335
13336 #if 0
13337   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13338          player->killed, player->active, player->reanimated);
13339 #endif
13340
13341   Bang(jx, jy);
13342
13343 #if 0
13344   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13345          player->killed, player->active, player->reanimated);
13346 #endif
13347
13348   if (player->reanimated)       /* killed player may have been reanimated */
13349     player->killed = player->reanimated = FALSE;
13350   else
13351     BuryPlayer(player);
13352 }
13353
13354 static void KillPlayerUnlessEnemyProtected(int x, int y)
13355 {
13356   if (!PLAYER_ENEMY_PROTECTED(x, y))
13357     KillPlayer(PLAYERINFO(x, y));
13358 }
13359
13360 static void KillPlayerUnlessExplosionProtected(int x, int y)
13361 {
13362   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13363     KillPlayer(PLAYERINFO(x, y));
13364 }
13365
13366 void BuryPlayer(struct PlayerInfo *player)
13367 {
13368   int jx = player->jx, jy = player->jy;
13369
13370   if (!player->active)
13371     return;
13372
13373   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13374   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13375
13376   player->GameOver = TRUE;
13377   RemovePlayer(player);
13378 }
13379
13380 void RemovePlayer(struct PlayerInfo *player)
13381 {
13382   int jx = player->jx, jy = player->jy;
13383   int i, found = FALSE;
13384
13385   player->present = FALSE;
13386   player->active = FALSE;
13387
13388   if (!ExplodeField[jx][jy])
13389     StorePlayer[jx][jy] = 0;
13390
13391   if (player->is_moving)
13392     TEST_DrawLevelField(player->last_jx, player->last_jy);
13393
13394   for (i = 0; i < MAX_PLAYERS; i++)
13395     if (stored_player[i].active)
13396       found = TRUE;
13397
13398   if (!found)
13399     AllPlayersGone = TRUE;
13400
13401   ExitX = ZX = jx;
13402   ExitY = ZY = jy;
13403 }
13404
13405 static void setFieldForSnapping(int x, int y, int element, int direction)
13406 {
13407   struct ElementInfo *ei = &element_info[element];
13408   int direction_bit = MV_DIR_TO_BIT(direction);
13409   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13410   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13411                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13412
13413   Feld[x][y] = EL_ELEMENT_SNAPPING;
13414   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13415
13416   ResetGfxAnimation(x, y);
13417
13418   GfxElement[x][y] = element;
13419   GfxAction[x][y] = action;
13420   GfxDir[x][y] = direction;
13421   GfxFrame[x][y] = -1;
13422 }
13423
13424 /*
13425   =============================================================================
13426   checkDiagonalPushing()
13427   -----------------------------------------------------------------------------
13428   check if diagonal input device direction results in pushing of object
13429   (by checking if the alternative direction is walkable, diggable, ...)
13430   =============================================================================
13431 */
13432
13433 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13434                                     int x, int y, int real_dx, int real_dy)
13435 {
13436   int jx, jy, dx, dy, xx, yy;
13437
13438   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13439     return TRUE;
13440
13441   /* diagonal direction: check alternative direction */
13442   jx = player->jx;
13443   jy = player->jy;
13444   dx = x - jx;
13445   dy = y - jy;
13446   xx = jx + (dx == 0 ? real_dx : 0);
13447   yy = jy + (dy == 0 ? real_dy : 0);
13448
13449   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13450 }
13451
13452 /*
13453   =============================================================================
13454   DigField()
13455   -----------------------------------------------------------------------------
13456   x, y:                 field next to player (non-diagonal) to try to dig to
13457   real_dx, real_dy:     direction as read from input device (can be diagonal)
13458   =============================================================================
13459 */
13460
13461 static int DigField(struct PlayerInfo *player,
13462                     int oldx, int oldy, int x, int y,
13463                     int real_dx, int real_dy, int mode)
13464 {
13465   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13466   boolean player_was_pushing = player->is_pushing;
13467   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13468   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13469   int jx = oldx, jy = oldy;
13470   int dx = x - jx, dy = y - jy;
13471   int nextx = x + dx, nexty = y + dy;
13472   int move_direction = (dx == -1 ? MV_LEFT  :
13473                         dx == +1 ? MV_RIGHT :
13474                         dy == -1 ? MV_UP    :
13475                         dy == +1 ? MV_DOWN  : MV_NONE);
13476   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13477   int dig_side = MV_DIR_OPPOSITE(move_direction);
13478   int old_element = Feld[jx][jy];
13479   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13480   int collect_count;
13481
13482   if (is_player)                /* function can also be called by EL_PENGUIN */
13483   {
13484     if (player->MovPos == 0)
13485     {
13486       player->is_digging = FALSE;
13487       player->is_collecting = FALSE;
13488     }
13489
13490     if (player->MovPos == 0)    /* last pushing move finished */
13491       player->is_pushing = FALSE;
13492
13493     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13494     {
13495       player->is_switching = FALSE;
13496       player->push_delay = -1;
13497
13498       return MP_NO_ACTION;
13499     }
13500   }
13501
13502   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13503     old_element = Back[jx][jy];
13504
13505   /* in case of element dropped at player position, check background */
13506   else if (Back[jx][jy] != EL_EMPTY &&
13507            game.engine_version >= VERSION_IDENT(2,2,0,0))
13508     old_element = Back[jx][jy];
13509
13510   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13511     return MP_NO_ACTION;        /* field has no opening in this direction */
13512
13513   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13514     return MP_NO_ACTION;        /* field has no opening in this direction */
13515
13516   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13517   {
13518     SplashAcid(x, y);
13519
13520     Feld[jx][jy] = player->artwork_element;
13521     InitMovingField(jx, jy, MV_DOWN);
13522     Store[jx][jy] = EL_ACID;
13523     ContinueMoving(jx, jy);
13524     BuryPlayer(player);
13525
13526     return MP_DONT_RUN_INTO;
13527   }
13528
13529   if (player_can_move && DONT_RUN_INTO(element))
13530   {
13531     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13532
13533     return MP_DONT_RUN_INTO;
13534   }
13535
13536   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13537     return MP_NO_ACTION;
13538
13539   collect_count = element_info[element].collect_count_initial;
13540
13541   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13542     return MP_NO_ACTION;
13543
13544   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13545     player_can_move = player_can_move_or_snap;
13546
13547   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13548       game.engine_version >= VERSION_IDENT(2,2,0,0))
13549   {
13550     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13551                                player->index_bit, dig_side);
13552     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13553                                         player->index_bit, dig_side);
13554
13555     if (element == EL_DC_LANDMINE)
13556       Bang(x, y);
13557
13558     if (Feld[x][y] != element)          /* field changed by snapping */
13559       return MP_ACTION;
13560
13561     return MP_NO_ACTION;
13562   }
13563
13564   if (player->gravity && is_player && !player->is_auto_moving &&
13565       canFallDown(player) && move_direction != MV_DOWN &&
13566       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13567     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13568
13569   if (player_can_move &&
13570       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13571   {
13572     int sound_element = SND_ELEMENT(element);
13573     int sound_action = ACTION_WALKING;
13574
13575     if (IS_RND_GATE(element))
13576     {
13577       if (!player->key[RND_GATE_NR(element)])
13578         return MP_NO_ACTION;
13579     }
13580     else if (IS_RND_GATE_GRAY(element))
13581     {
13582       if (!player->key[RND_GATE_GRAY_NR(element)])
13583         return MP_NO_ACTION;
13584     }
13585     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13586     {
13587       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13588         return MP_NO_ACTION;
13589     }
13590     else if (element == EL_EXIT_OPEN ||
13591              element == EL_EM_EXIT_OPEN ||
13592              element == EL_EM_EXIT_OPENING ||
13593              element == EL_STEEL_EXIT_OPEN ||
13594              element == EL_EM_STEEL_EXIT_OPEN ||
13595              element == EL_EM_STEEL_EXIT_OPENING ||
13596              element == EL_SP_EXIT_OPEN ||
13597              element == EL_SP_EXIT_OPENING)
13598     {
13599       sound_action = ACTION_PASSING;    /* player is passing exit */
13600     }
13601     else if (element == EL_EMPTY)
13602     {
13603       sound_action = ACTION_MOVING;             /* nothing to walk on */
13604     }
13605
13606     /* play sound from background or player, whatever is available */
13607     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13608       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13609     else
13610       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13611   }
13612   else if (player_can_move &&
13613            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13614   {
13615     if (!ACCESS_FROM(element, opposite_direction))
13616       return MP_NO_ACTION;      /* field not accessible from this direction */
13617
13618     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13619       return MP_NO_ACTION;
13620
13621     if (IS_EM_GATE(element))
13622     {
13623       if (!player->key[EM_GATE_NR(element)])
13624         return MP_NO_ACTION;
13625     }
13626     else if (IS_EM_GATE_GRAY(element))
13627     {
13628       if (!player->key[EM_GATE_GRAY_NR(element)])
13629         return MP_NO_ACTION;
13630     }
13631     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13632     {
13633       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13634         return MP_NO_ACTION;
13635     }
13636     else if (IS_EMC_GATE(element))
13637     {
13638       if (!player->key[EMC_GATE_NR(element)])
13639         return MP_NO_ACTION;
13640     }
13641     else if (IS_EMC_GATE_GRAY(element))
13642     {
13643       if (!player->key[EMC_GATE_GRAY_NR(element)])
13644         return MP_NO_ACTION;
13645     }
13646     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13647     {
13648       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13649         return MP_NO_ACTION;
13650     }
13651     else if (element == EL_DC_GATE_WHITE ||
13652              element == EL_DC_GATE_WHITE_GRAY ||
13653              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13654     {
13655       if (player->num_white_keys == 0)
13656         return MP_NO_ACTION;
13657
13658       player->num_white_keys--;
13659     }
13660     else if (IS_SP_PORT(element))
13661     {
13662       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13663           element == EL_SP_GRAVITY_PORT_RIGHT ||
13664           element == EL_SP_GRAVITY_PORT_UP ||
13665           element == EL_SP_GRAVITY_PORT_DOWN)
13666         player->gravity = !player->gravity;
13667       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13668                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13669                element == EL_SP_GRAVITY_ON_PORT_UP ||
13670                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13671         player->gravity = TRUE;
13672       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13673                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13674                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13675                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13676         player->gravity = FALSE;
13677     }
13678
13679     /* automatically move to the next field with double speed */
13680     player->programmed_action = move_direction;
13681
13682     if (player->move_delay_reset_counter == 0)
13683     {
13684       player->move_delay_reset_counter = 2;     /* two double speed steps */
13685
13686       DOUBLE_PLAYER_SPEED(player);
13687     }
13688
13689     PlayLevelSoundAction(x, y, ACTION_PASSING);
13690   }
13691   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13692   {
13693     RemoveField(x, y);
13694
13695     if (mode != DF_SNAP)
13696     {
13697       GfxElement[x][y] = GFX_ELEMENT(element);
13698       player->is_digging = TRUE;
13699     }
13700
13701     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13702
13703     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13704                                         player->index_bit, dig_side);
13705
13706     if (mode == DF_SNAP)
13707     {
13708       if (level.block_snap_field)
13709         setFieldForSnapping(x, y, element, move_direction);
13710       else
13711         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13712
13713       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13714                                           player->index_bit, dig_side);
13715     }
13716   }
13717   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13718   {
13719     RemoveField(x, y);
13720
13721     if (is_player && mode != DF_SNAP)
13722     {
13723       GfxElement[x][y] = element;
13724       player->is_collecting = TRUE;
13725     }
13726
13727     if (element == EL_SPEED_PILL)
13728     {
13729       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13730     }
13731     else if (element == EL_EXTRA_TIME && level.time > 0)
13732     {
13733       TimeLeft += level.extra_time;
13734
13735       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13736
13737       DisplayGameControlValues();
13738     }
13739     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13740     {
13741       player->shield_normal_time_left += level.shield_normal_time;
13742       if (element == EL_SHIELD_DEADLY)
13743         player->shield_deadly_time_left += level.shield_deadly_time;
13744     }
13745     else if (element == EL_DYNAMITE ||
13746              element == EL_EM_DYNAMITE ||
13747              element == EL_SP_DISK_RED)
13748     {
13749       if (player->inventory_size < MAX_INVENTORY_SIZE)
13750         player->inventory_element[player->inventory_size++] = element;
13751
13752       DrawGameDoorValues();
13753     }
13754     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13755     {
13756       player->dynabomb_count++;
13757       player->dynabombs_left++;
13758     }
13759     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13760     {
13761       player->dynabomb_size++;
13762     }
13763     else if (element == EL_DYNABOMB_INCREASE_POWER)
13764     {
13765       player->dynabomb_xl = TRUE;
13766     }
13767     else if (IS_KEY(element))
13768     {
13769       player->key[KEY_NR(element)] = TRUE;
13770
13771       DrawGameDoorValues();
13772     }
13773     else if (element == EL_DC_KEY_WHITE)
13774     {
13775       player->num_white_keys++;
13776
13777       /* display white keys? */
13778       /* DrawGameDoorValues(); */
13779     }
13780     else if (IS_ENVELOPE(element))
13781     {
13782       player->show_envelope = element;
13783     }
13784     else if (element == EL_EMC_LENSES)
13785     {
13786       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13787
13788       RedrawAllInvisibleElementsForLenses();
13789     }
13790     else if (element == EL_EMC_MAGNIFIER)
13791     {
13792       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13793
13794       RedrawAllInvisibleElementsForMagnifier();
13795     }
13796     else if (IS_DROPPABLE(element) ||
13797              IS_THROWABLE(element))     /* can be collected and dropped */
13798     {
13799       int i;
13800
13801       if (collect_count == 0)
13802         player->inventory_infinite_element = element;
13803       else
13804         for (i = 0; i < collect_count; i++)
13805           if (player->inventory_size < MAX_INVENTORY_SIZE)
13806             player->inventory_element[player->inventory_size++] = element;
13807
13808       DrawGameDoorValues();
13809     }
13810     else if (collect_count > 0)
13811     {
13812       local_player->gems_still_needed -= collect_count;
13813       if (local_player->gems_still_needed < 0)
13814         local_player->gems_still_needed = 0;
13815
13816       game.snapshot.collected_item = TRUE;
13817
13818       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13819
13820       DisplayGameControlValues();
13821     }
13822
13823     RaiseScoreElement(element);
13824     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13825
13826     if (is_player)
13827       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13828                                           player->index_bit, dig_side);
13829
13830     if (mode == DF_SNAP)
13831     {
13832       if (level.block_snap_field)
13833         setFieldForSnapping(x, y, element, move_direction);
13834       else
13835         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13836
13837       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13838                                           player->index_bit, dig_side);
13839     }
13840   }
13841   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13842   {
13843     if (mode == DF_SNAP && element != EL_BD_ROCK)
13844       return MP_NO_ACTION;
13845
13846     if (CAN_FALL(element) && dy)
13847       return MP_NO_ACTION;
13848
13849     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13850         !(element == EL_SPRING && level.use_spring_bug))
13851       return MP_NO_ACTION;
13852
13853     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13854         ((move_direction & MV_VERTICAL &&
13855           ((element_info[element].move_pattern & MV_LEFT &&
13856             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13857            (element_info[element].move_pattern & MV_RIGHT &&
13858             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13859          (move_direction & MV_HORIZONTAL &&
13860           ((element_info[element].move_pattern & MV_UP &&
13861             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13862            (element_info[element].move_pattern & MV_DOWN &&
13863             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13864       return MP_NO_ACTION;
13865
13866     /* do not push elements already moving away faster than player */
13867     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13868         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13869       return MP_NO_ACTION;
13870
13871     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13872     {
13873       if (player->push_delay_value == -1 || !player_was_pushing)
13874         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13875     }
13876     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13877     {
13878       if (player->push_delay_value == -1)
13879         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13880     }
13881     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13882     {
13883       if (!player->is_pushing)
13884         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13885     }
13886
13887     player->is_pushing = TRUE;
13888     player->is_active = TRUE;
13889
13890     if (!(IN_LEV_FIELD(nextx, nexty) &&
13891           (IS_FREE(nextx, nexty) ||
13892            (IS_SB_ELEMENT(element) &&
13893             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13894            (IS_CUSTOM_ELEMENT(element) &&
13895             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13896       return MP_NO_ACTION;
13897
13898     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13899       return MP_NO_ACTION;
13900
13901     if (player->push_delay == -1)       /* new pushing; restart delay */
13902       player->push_delay = 0;
13903
13904     if (player->push_delay < player->push_delay_value &&
13905         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13906         element != EL_SPRING && element != EL_BALLOON)
13907     {
13908       /* make sure that there is no move delay before next try to push */
13909       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13910         player->move_delay = 0;
13911
13912       return MP_NO_ACTION;
13913     }
13914
13915     if (IS_CUSTOM_ELEMENT(element) &&
13916         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13917     {
13918       if (!DigFieldByCE(nextx, nexty, element))
13919         return MP_NO_ACTION;
13920     }
13921
13922     if (IS_SB_ELEMENT(element))
13923     {
13924       if (element == EL_SOKOBAN_FIELD_FULL)
13925       {
13926         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13927         local_player->sokobanfields_still_needed++;
13928       }
13929
13930       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13931       {
13932         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13933         local_player->sokobanfields_still_needed--;
13934       }
13935
13936       Feld[x][y] = EL_SOKOBAN_OBJECT;
13937
13938       if (Back[x][y] == Back[nextx][nexty])
13939         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13940       else if (Back[x][y] != 0)
13941         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13942                                     ACTION_EMPTYING);
13943       else
13944         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13945                                     ACTION_FILLING);
13946
13947       if (local_player->sokobanfields_still_needed == 0 &&
13948           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13949       {
13950         PlayerWins(player);
13951
13952         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13953       }
13954     }
13955     else
13956       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13957
13958     InitMovingField(x, y, move_direction);
13959     GfxAction[x][y] = ACTION_PUSHING;
13960
13961     if (mode == DF_SNAP)
13962       ContinueMoving(x, y);
13963     else
13964       MovPos[x][y] = (dx != 0 ? dx : dy);
13965
13966     Pushed[x][y] = TRUE;
13967     Pushed[nextx][nexty] = TRUE;
13968
13969     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13970       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13971     else
13972       player->push_delay_value = -1;    /* get new value later */
13973
13974     /* check for element change _after_ element has been pushed */
13975     if (game.use_change_when_pushing_bug)
13976     {
13977       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13978                                  player->index_bit, dig_side);
13979       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13980                                           player->index_bit, dig_side);
13981     }
13982   }
13983   else if (IS_SWITCHABLE(element))
13984   {
13985     if (PLAYER_SWITCHING(player, x, y))
13986     {
13987       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13988                                           player->index_bit, dig_side);
13989
13990       return MP_ACTION;
13991     }
13992
13993     player->is_switching = TRUE;
13994     player->switch_x = x;
13995     player->switch_y = y;
13996
13997     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13998
13999     if (element == EL_ROBOT_WHEEL)
14000     {
14001       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14002       ZX = x;
14003       ZY = y;
14004
14005       game.robot_wheel_active = TRUE;
14006
14007       TEST_DrawLevelField(x, y);
14008     }
14009     else if (element == EL_SP_TERMINAL)
14010     {
14011       int xx, yy;
14012
14013       SCAN_PLAYFIELD(xx, yy)
14014       {
14015         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14016         {
14017           Bang(xx, yy);
14018         }
14019         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14020         {
14021           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14022
14023           ResetGfxAnimation(xx, yy);
14024           TEST_DrawLevelField(xx, yy);
14025         }
14026       }
14027     }
14028     else if (IS_BELT_SWITCH(element))
14029     {
14030       ToggleBeltSwitch(x, y);
14031     }
14032     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14033              element == EL_SWITCHGATE_SWITCH_DOWN ||
14034              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14035              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14036     {
14037       ToggleSwitchgateSwitch(x, y);
14038     }
14039     else if (element == EL_LIGHT_SWITCH ||
14040              element == EL_LIGHT_SWITCH_ACTIVE)
14041     {
14042       ToggleLightSwitch(x, y);
14043     }
14044     else if (element == EL_TIMEGATE_SWITCH ||
14045              element == EL_DC_TIMEGATE_SWITCH)
14046     {
14047       ActivateTimegateSwitch(x, y);
14048     }
14049     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14050              element == EL_BALLOON_SWITCH_RIGHT ||
14051              element == EL_BALLOON_SWITCH_UP    ||
14052              element == EL_BALLOON_SWITCH_DOWN  ||
14053              element == EL_BALLOON_SWITCH_NONE  ||
14054              element == EL_BALLOON_SWITCH_ANY)
14055     {
14056       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14057                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14058                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14059                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14060                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14061                              move_direction);
14062     }
14063     else if (element == EL_LAMP)
14064     {
14065       Feld[x][y] = EL_LAMP_ACTIVE;
14066       local_player->lights_still_needed--;
14067
14068       ResetGfxAnimation(x, y);
14069       TEST_DrawLevelField(x, y);
14070     }
14071     else if (element == EL_TIME_ORB_FULL)
14072     {
14073       Feld[x][y] = EL_TIME_ORB_EMPTY;
14074
14075       if (level.time > 0 || level.use_time_orb_bug)
14076       {
14077         TimeLeft += level.time_orb_time;
14078         game.no_time_limit = FALSE;
14079
14080         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14081
14082         DisplayGameControlValues();
14083       }
14084
14085       ResetGfxAnimation(x, y);
14086       TEST_DrawLevelField(x, y);
14087     }
14088     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14089              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14090     {
14091       int xx, yy;
14092
14093       game.ball_state = !game.ball_state;
14094
14095       SCAN_PLAYFIELD(xx, yy)
14096       {
14097         int e = Feld[xx][yy];
14098
14099         if (game.ball_state)
14100         {
14101           if (e == EL_EMC_MAGIC_BALL)
14102             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14103           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14104             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14105         }
14106         else
14107         {
14108           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14109             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14110           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14111             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14112         }
14113       }
14114     }
14115
14116     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14117                                         player->index_bit, dig_side);
14118
14119     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14120                                         player->index_bit, dig_side);
14121
14122     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14123                                         player->index_bit, dig_side);
14124
14125     return MP_ACTION;
14126   }
14127   else
14128   {
14129     if (!PLAYER_SWITCHING(player, x, y))
14130     {
14131       player->is_switching = TRUE;
14132       player->switch_x = x;
14133       player->switch_y = y;
14134
14135       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14136                                  player->index_bit, dig_side);
14137       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14138                                           player->index_bit, dig_side);
14139
14140       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14141                                  player->index_bit, dig_side);
14142       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14143                                           player->index_bit, dig_side);
14144     }
14145
14146     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14147                                player->index_bit, dig_side);
14148     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14149                                         player->index_bit, dig_side);
14150
14151     return MP_NO_ACTION;
14152   }
14153
14154   player->push_delay = -1;
14155
14156   if (is_player)                /* function can also be called by EL_PENGUIN */
14157   {
14158     if (Feld[x][y] != element)          /* really digged/collected something */
14159     {
14160       player->is_collecting = !player->is_digging;
14161       player->is_active = TRUE;
14162     }
14163   }
14164
14165   return MP_MOVING;
14166 }
14167
14168 static boolean DigFieldByCE(int x, int y, int digging_element)
14169 {
14170   int element = Feld[x][y];
14171
14172   if (!IS_FREE(x, y))
14173   {
14174     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14175                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14176                   ACTION_BREAKING);
14177
14178     /* no element can dig solid indestructible elements */
14179     if (IS_INDESTRUCTIBLE(element) &&
14180         !IS_DIGGABLE(element) &&
14181         !IS_COLLECTIBLE(element))
14182       return FALSE;
14183
14184     if (AmoebaNr[x][y] &&
14185         (element == EL_AMOEBA_FULL ||
14186          element == EL_BD_AMOEBA ||
14187          element == EL_AMOEBA_GROWING))
14188     {
14189       AmoebaCnt[AmoebaNr[x][y]]--;
14190       AmoebaCnt2[AmoebaNr[x][y]]--;
14191     }
14192
14193     if (IS_MOVING(x, y))
14194       RemoveMovingField(x, y);
14195     else
14196     {
14197       RemoveField(x, y);
14198       TEST_DrawLevelField(x, y);
14199     }
14200
14201     /* if digged element was about to explode, prevent the explosion */
14202     ExplodeField[x][y] = EX_TYPE_NONE;
14203
14204     PlayLevelSoundAction(x, y, action);
14205   }
14206
14207   Store[x][y] = EL_EMPTY;
14208
14209   /* this makes it possible to leave the removed element again */
14210   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14211     Store[x][y] = element;
14212
14213   return TRUE;
14214 }
14215
14216 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14217 {
14218   int jx = player->jx, jy = player->jy;
14219   int x = jx + dx, y = jy + dy;
14220   int snap_direction = (dx == -1 ? MV_LEFT  :
14221                         dx == +1 ? MV_RIGHT :
14222                         dy == -1 ? MV_UP    :
14223                         dy == +1 ? MV_DOWN  : MV_NONE);
14224   boolean can_continue_snapping = (level.continuous_snapping &&
14225                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14226
14227   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14228     return FALSE;
14229
14230   if (!player->active || !IN_LEV_FIELD(x, y))
14231     return FALSE;
14232
14233   if (dx && dy)
14234     return FALSE;
14235
14236   if (!dx && !dy)
14237   {
14238     if (player->MovPos == 0)
14239       player->is_pushing = FALSE;
14240
14241     player->is_snapping = FALSE;
14242
14243     if (player->MovPos == 0)
14244     {
14245       player->is_moving = FALSE;
14246       player->is_digging = FALSE;
14247       player->is_collecting = FALSE;
14248     }
14249
14250     return FALSE;
14251   }
14252
14253   /* prevent snapping with already pressed snap key when not allowed */
14254   if (player->is_snapping && !can_continue_snapping)
14255     return FALSE;
14256
14257   player->MovDir = snap_direction;
14258
14259   if (player->MovPos == 0)
14260   {
14261     player->is_moving = FALSE;
14262     player->is_digging = FALSE;
14263     player->is_collecting = FALSE;
14264   }
14265
14266   player->is_dropping = FALSE;
14267   player->is_dropping_pressed = FALSE;
14268   player->drop_pressed_delay = 0;
14269
14270   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14271     return FALSE;
14272
14273   player->is_snapping = TRUE;
14274   player->is_active = TRUE;
14275
14276   if (player->MovPos == 0)
14277   {
14278     player->is_moving = FALSE;
14279     player->is_digging = FALSE;
14280     player->is_collecting = FALSE;
14281   }
14282
14283   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14284     TEST_DrawLevelField(player->last_jx, player->last_jy);
14285
14286   TEST_DrawLevelField(x, y);
14287
14288   return TRUE;
14289 }
14290
14291 static boolean DropElement(struct PlayerInfo *player)
14292 {
14293   int old_element, new_element;
14294   int dropx = player->jx, dropy = player->jy;
14295   int drop_direction = player->MovDir;
14296   int drop_side = drop_direction;
14297   int drop_element = get_next_dropped_element(player);
14298
14299   /* do not drop an element on top of another element; when holding drop key
14300      pressed without moving, dropped element must move away before the next
14301      element can be dropped (this is especially important if the next element
14302      is dynamite, which can be placed on background for historical reasons) */
14303   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14304     return MP_ACTION;
14305
14306   if (IS_THROWABLE(drop_element))
14307   {
14308     dropx += GET_DX_FROM_DIR(drop_direction);
14309     dropy += GET_DY_FROM_DIR(drop_direction);
14310
14311     if (!IN_LEV_FIELD(dropx, dropy))
14312       return FALSE;
14313   }
14314
14315   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14316   new_element = drop_element;           /* default: no change when dropping */
14317
14318   /* check if player is active, not moving and ready to drop */
14319   if (!player->active || player->MovPos || player->drop_delay > 0)
14320     return FALSE;
14321
14322   /* check if player has anything that can be dropped */
14323   if (new_element == EL_UNDEFINED)
14324     return FALSE;
14325
14326   /* only set if player has anything that can be dropped */
14327   player->is_dropping_pressed = TRUE;
14328
14329   /* check if drop key was pressed long enough for EM style dynamite */
14330   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14331     return FALSE;
14332
14333   /* check if anything can be dropped at the current position */
14334   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14335     return FALSE;
14336
14337   /* collected custom elements can only be dropped on empty fields */
14338   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14339     return FALSE;
14340
14341   if (old_element != EL_EMPTY)
14342     Back[dropx][dropy] = old_element;   /* store old element on this field */
14343
14344   ResetGfxAnimation(dropx, dropy);
14345   ResetRandomAnimationValue(dropx, dropy);
14346
14347   if (player->inventory_size > 0 ||
14348       player->inventory_infinite_element != EL_UNDEFINED)
14349   {
14350     if (player->inventory_size > 0)
14351     {
14352       player->inventory_size--;
14353
14354       DrawGameDoorValues();
14355
14356       if (new_element == EL_DYNAMITE)
14357         new_element = EL_DYNAMITE_ACTIVE;
14358       else if (new_element == EL_EM_DYNAMITE)
14359         new_element = EL_EM_DYNAMITE_ACTIVE;
14360       else if (new_element == EL_SP_DISK_RED)
14361         new_element = EL_SP_DISK_RED_ACTIVE;
14362     }
14363
14364     Feld[dropx][dropy] = new_element;
14365
14366     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14367       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14368                           el2img(Feld[dropx][dropy]), 0);
14369
14370     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14371
14372     /* needed if previous element just changed to "empty" in the last frame */
14373     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14374
14375     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14376                                player->index_bit, drop_side);
14377     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14378                                         CE_PLAYER_DROPS_X,
14379                                         player->index_bit, drop_side);
14380
14381     TestIfElementTouchesCustomElement(dropx, dropy);
14382   }
14383   else          /* player is dropping a dyna bomb */
14384   {
14385     player->dynabombs_left--;
14386
14387     Feld[dropx][dropy] = new_element;
14388
14389     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14390       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14391                           el2img(Feld[dropx][dropy]), 0);
14392
14393     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14394   }
14395
14396   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14397     InitField_WithBug1(dropx, dropy, FALSE);
14398
14399   new_element = Feld[dropx][dropy];     /* element might have changed */
14400
14401   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14402       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14403   {
14404     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14405       MovDir[dropx][dropy] = drop_direction;
14406
14407     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14408
14409     /* do not cause impact style collision by dropping elements that can fall */
14410     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14411   }
14412
14413   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14414   player->is_dropping = TRUE;
14415
14416   player->drop_pressed_delay = 0;
14417   player->is_dropping_pressed = FALSE;
14418
14419   player->drop_x = dropx;
14420   player->drop_y = dropy;
14421
14422   return TRUE;
14423 }
14424
14425 /* ------------------------------------------------------------------------- */
14426 /* game sound playing functions                                              */
14427 /* ------------------------------------------------------------------------- */
14428
14429 static int *loop_sound_frame = NULL;
14430 static int *loop_sound_volume = NULL;
14431
14432 void InitPlayLevelSound()
14433 {
14434   int num_sounds = getSoundListSize();
14435
14436   checked_free(loop_sound_frame);
14437   checked_free(loop_sound_volume);
14438
14439   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14440   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14441 }
14442
14443 static void PlayLevelSound(int x, int y, int nr)
14444 {
14445   int sx = SCREENX(x), sy = SCREENY(y);
14446   int volume, stereo_position;
14447   int max_distance = 8;
14448   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14449
14450   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14451       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14452     return;
14453
14454   if (!IN_LEV_FIELD(x, y) ||
14455       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14456       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14457     return;
14458
14459   volume = SOUND_MAX_VOLUME;
14460
14461   if (!IN_SCR_FIELD(sx, sy))
14462   {
14463     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14464     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14465
14466     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14467   }
14468
14469   stereo_position = (SOUND_MAX_LEFT +
14470                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14471                      (SCR_FIELDX + 2 * max_distance));
14472
14473   if (IS_LOOP_SOUND(nr))
14474   {
14475     /* This assures that quieter loop sounds do not overwrite louder ones,
14476        while restarting sound volume comparison with each new game frame. */
14477
14478     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14479       return;
14480
14481     loop_sound_volume[nr] = volume;
14482     loop_sound_frame[nr] = FrameCounter;
14483   }
14484
14485   PlaySoundExt(nr, volume, stereo_position, type);
14486 }
14487
14488 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14489 {
14490   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14491                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14492                  y < LEVELY(BY1) ? LEVELY(BY1) :
14493                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14494                  sound_action);
14495 }
14496
14497 static void PlayLevelSoundAction(int x, int y, int action)
14498 {
14499   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14500 }
14501
14502 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14503 {
14504   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14505
14506   if (sound_effect != SND_UNDEFINED)
14507     PlayLevelSound(x, y, sound_effect);
14508 }
14509
14510 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14511                                               int action)
14512 {
14513   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14514
14515   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14516     PlayLevelSound(x, y, sound_effect);
14517 }
14518
14519 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14520 {
14521   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14522
14523   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14524     PlayLevelSound(x, y, sound_effect);
14525 }
14526
14527 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14528 {
14529   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14530
14531   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14532     StopSound(sound_effect);
14533 }
14534
14535 static int getLevelMusicNr()
14536 {
14537   if (levelset.music[level_nr] != MUS_UNDEFINED)
14538     return levelset.music[level_nr];            /* from config file */
14539   else
14540     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14541 }
14542
14543 static void FadeLevelSounds()
14544 {
14545   FadeSounds();
14546 }
14547
14548 static void FadeLevelMusic()
14549 {
14550   int music_nr = getLevelMusicNr();
14551   char *curr_music = getCurrentlyPlayingMusicFilename();
14552   char *next_music = getMusicInfoEntryFilename(music_nr);
14553
14554   if (!strEqual(curr_music, next_music))
14555     FadeMusic();
14556 }
14557
14558 void FadeLevelSoundsAndMusic()
14559 {
14560   FadeLevelSounds();
14561   FadeLevelMusic();
14562 }
14563
14564 static void PlayLevelMusic()
14565 {
14566   int music_nr = getLevelMusicNr();
14567   char *curr_music = getCurrentlyPlayingMusicFilename();
14568   char *next_music = getMusicInfoEntryFilename(music_nr);
14569
14570   if (!strEqual(curr_music, next_music))
14571     PlayMusic(music_nr);
14572 }
14573
14574 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14575 {
14576   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14577   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14578   int x = xx - 1 - offset;
14579   int y = yy - 1 - offset;
14580
14581   switch (sample)
14582   {
14583     case SAMPLE_blank:
14584       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14585       break;
14586
14587     case SAMPLE_roll:
14588       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14589       break;
14590
14591     case SAMPLE_stone:
14592       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14593       break;
14594
14595     case SAMPLE_nut:
14596       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14597       break;
14598
14599     case SAMPLE_crack:
14600       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14601       break;
14602
14603     case SAMPLE_bug:
14604       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14605       break;
14606
14607     case SAMPLE_tank:
14608       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14609       break;
14610
14611     case SAMPLE_android_clone:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14613       break;
14614
14615     case SAMPLE_android_move:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14617       break;
14618
14619     case SAMPLE_spring:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14621       break;
14622
14623     case SAMPLE_slurp:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14625       break;
14626
14627     case SAMPLE_eater:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14629       break;
14630
14631     case SAMPLE_eater_eat:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14633       break;
14634
14635     case SAMPLE_alien:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14637       break;
14638
14639     case SAMPLE_collect:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14641       break;
14642
14643     case SAMPLE_diamond:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14645       break;
14646
14647     case SAMPLE_squash:
14648       /* !!! CHECK THIS !!! */
14649 #if 1
14650       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14651 #else
14652       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14653 #endif
14654       break;
14655
14656     case SAMPLE_wonderfall:
14657       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14658       break;
14659
14660     case SAMPLE_drip:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14662       break;
14663
14664     case SAMPLE_push:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14666       break;
14667
14668     case SAMPLE_dirt:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14670       break;
14671
14672     case SAMPLE_acid:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14674       break;
14675
14676     case SAMPLE_ball:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14678       break;
14679
14680     case SAMPLE_grow:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14682       break;
14683
14684     case SAMPLE_wonder:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14686       break;
14687
14688     case SAMPLE_door:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14690       break;
14691
14692     case SAMPLE_exit_open:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14694       break;
14695
14696     case SAMPLE_exit_leave:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14698       break;
14699
14700     case SAMPLE_dynamite:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14702       break;
14703
14704     case SAMPLE_tick:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14706       break;
14707
14708     case SAMPLE_press:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14710       break;
14711
14712     case SAMPLE_wheel:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14714       break;
14715
14716     case SAMPLE_boom:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14718       break;
14719
14720     case SAMPLE_die:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14722       break;
14723
14724     case SAMPLE_time:
14725       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14726       break;
14727
14728     default:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14730       break;
14731   }
14732 }
14733
14734 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14735 {
14736   int element = map_element_SP_to_RND(element_sp);
14737   int action = map_action_SP_to_RND(action_sp);
14738   int offset = (setup.sp_show_border_elements ? 0 : 1);
14739   int x = xx - offset;
14740   int y = yy - offset;
14741
14742   PlayLevelSoundElementAction(x, y, element, action);
14743 }
14744
14745 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14746 {
14747   int element = map_element_MM_to_RND(element_mm);
14748   int action = map_action_MM_to_RND(action_mm);
14749   int offset = 0;
14750   int x = xx - offset;
14751   int y = yy - offset;
14752
14753   if (!IS_MM_ELEMENT(element))
14754     element = EL_MM_DEFAULT;
14755
14756   PlayLevelSoundElementAction(x, y, element, action);
14757 }
14758
14759 void PlaySound_MM(int sound_mm)
14760 {
14761   int sound = map_sound_MM_to_RND(sound_mm);
14762
14763   if (sound == SND_UNDEFINED)
14764     return;
14765
14766   PlaySound(sound);
14767 }
14768
14769 void PlaySoundLoop_MM(int sound_mm)
14770 {
14771   int sound = map_sound_MM_to_RND(sound_mm);
14772
14773   if (sound == SND_UNDEFINED)
14774     return;
14775
14776   PlaySoundLoop(sound);
14777 }
14778
14779 void StopSound_MM(int sound_mm)
14780 {
14781   int sound = map_sound_MM_to_RND(sound_mm);
14782
14783   if (sound == SND_UNDEFINED)
14784     return;
14785
14786   StopSound(sound);
14787 }
14788
14789 void RaiseScore(int value)
14790 {
14791   local_player->score += value;
14792
14793   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14794
14795   DisplayGameControlValues();
14796 }
14797
14798 void RaiseScoreElement(int element)
14799 {
14800   switch (element)
14801   {
14802     case EL_EMERALD:
14803     case EL_BD_DIAMOND:
14804     case EL_EMERALD_YELLOW:
14805     case EL_EMERALD_RED:
14806     case EL_EMERALD_PURPLE:
14807     case EL_SP_INFOTRON:
14808       RaiseScore(level.score[SC_EMERALD]);
14809       break;
14810     case EL_DIAMOND:
14811       RaiseScore(level.score[SC_DIAMOND]);
14812       break;
14813     case EL_CRYSTAL:
14814       RaiseScore(level.score[SC_CRYSTAL]);
14815       break;
14816     case EL_PEARL:
14817       RaiseScore(level.score[SC_PEARL]);
14818       break;
14819     case EL_BUG:
14820     case EL_BD_BUTTERFLY:
14821     case EL_SP_ELECTRON:
14822       RaiseScore(level.score[SC_BUG]);
14823       break;
14824     case EL_SPACESHIP:
14825     case EL_BD_FIREFLY:
14826     case EL_SP_SNIKSNAK:
14827       RaiseScore(level.score[SC_SPACESHIP]);
14828       break;
14829     case EL_YAMYAM:
14830     case EL_DARK_YAMYAM:
14831       RaiseScore(level.score[SC_YAMYAM]);
14832       break;
14833     case EL_ROBOT:
14834       RaiseScore(level.score[SC_ROBOT]);
14835       break;
14836     case EL_PACMAN:
14837       RaiseScore(level.score[SC_PACMAN]);
14838       break;
14839     case EL_NUT:
14840       RaiseScore(level.score[SC_NUT]);
14841       break;
14842     case EL_DYNAMITE:
14843     case EL_EM_DYNAMITE:
14844     case EL_SP_DISK_RED:
14845     case EL_DYNABOMB_INCREASE_NUMBER:
14846     case EL_DYNABOMB_INCREASE_SIZE:
14847     case EL_DYNABOMB_INCREASE_POWER:
14848       RaiseScore(level.score[SC_DYNAMITE]);
14849       break;
14850     case EL_SHIELD_NORMAL:
14851     case EL_SHIELD_DEADLY:
14852       RaiseScore(level.score[SC_SHIELD]);
14853       break;
14854     case EL_EXTRA_TIME:
14855       RaiseScore(level.extra_time_score);
14856       break;
14857     case EL_KEY_1:
14858     case EL_KEY_2:
14859     case EL_KEY_3:
14860     case EL_KEY_4:
14861     case EL_EM_KEY_1:
14862     case EL_EM_KEY_2:
14863     case EL_EM_KEY_3:
14864     case EL_EM_KEY_4:
14865     case EL_EMC_KEY_5:
14866     case EL_EMC_KEY_6:
14867     case EL_EMC_KEY_7:
14868     case EL_EMC_KEY_8:
14869     case EL_DC_KEY_WHITE:
14870       RaiseScore(level.score[SC_KEY]);
14871       break;
14872     default:
14873       RaiseScore(element_info[element].collect_score);
14874       break;
14875   }
14876 }
14877
14878 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14879 {
14880   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14881   {
14882     /* closing door required in case of envelope style request dialogs */
14883     if (!skip_request)
14884       CloseDoor(DOOR_CLOSE_1);
14885
14886     if (network.enabled)
14887       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14888     else
14889     {
14890       if (quick_quit)
14891         FadeSkipNextFadeIn();
14892
14893       SetGameStatus(GAME_MODE_MAIN);
14894
14895       DrawMainMenu();
14896     }
14897   }
14898   else          /* continue playing the game */
14899   {
14900     if (tape.playing && tape.deactivate_display)
14901       TapeDeactivateDisplayOff(TRUE);
14902
14903     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14904
14905     if (tape.playing && tape.deactivate_display)
14906       TapeDeactivateDisplayOn();
14907   }
14908 }
14909
14910 void RequestQuitGame(boolean ask_if_really_quit)
14911 {
14912   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14913   boolean skip_request = AllPlayersGone || quick_quit;
14914
14915   RequestQuitGameExt(skip_request, quick_quit,
14916                      "Do you really want to quit the game?");
14917 }
14918
14919 void RequestRestartGame(char *message)
14920 {
14921   game.restart_game_message = NULL;
14922
14923   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14924   {
14925     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14926   }
14927   else
14928   {
14929     SetGameStatus(GAME_MODE_MAIN);
14930
14931     DrawMainMenu();
14932   }
14933 }
14934
14935
14936 /* ------------------------------------------------------------------------- */
14937 /* random generator functions                                                */
14938 /* ------------------------------------------------------------------------- */
14939
14940 unsigned int InitEngineRandom_RND(int seed)
14941 {
14942   game.num_random_calls = 0;
14943
14944   return InitEngineRandom(seed);
14945 }
14946
14947 unsigned int RND(int max)
14948 {
14949   if (max > 0)
14950   {
14951     game.num_random_calls++;
14952
14953     return GetEngineRandom(max);
14954   }
14955
14956   return 0;
14957 }
14958
14959
14960 /* ------------------------------------------------------------------------- */
14961 /* game engine snapshot handling functions                                   */
14962 /* ------------------------------------------------------------------------- */
14963
14964 struct EngineSnapshotInfo
14965 {
14966   /* runtime values for custom element collect score */
14967   int collect_score[NUM_CUSTOM_ELEMENTS];
14968
14969   /* runtime values for group element choice position */
14970   int choice_pos[NUM_GROUP_ELEMENTS];
14971
14972   /* runtime values for belt position animations */
14973   int belt_graphic[4][NUM_BELT_PARTS];
14974   int belt_anim_mode[4][NUM_BELT_PARTS];
14975 };
14976
14977 static struct EngineSnapshotInfo engine_snapshot_rnd;
14978 static char *snapshot_level_identifier = NULL;
14979 static int snapshot_level_nr = -1;
14980
14981 static void SaveEngineSnapshotValues_RND()
14982 {
14983   static int belt_base_active_element[4] =
14984   {
14985     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14986     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14987     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14988     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14989   };
14990   int i, j;
14991
14992   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14993   {
14994     int element = EL_CUSTOM_START + i;
14995
14996     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14997   }
14998
14999   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15000   {
15001     int element = EL_GROUP_START + i;
15002
15003     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15004   }
15005
15006   for (i = 0; i < 4; i++)
15007   {
15008     for (j = 0; j < NUM_BELT_PARTS; j++)
15009     {
15010       int element = belt_base_active_element[i] + j;
15011       int graphic = el2img(element);
15012       int anim_mode = graphic_info[graphic].anim_mode;
15013
15014       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15015       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15016     }
15017   }
15018 }
15019
15020 static void LoadEngineSnapshotValues_RND()
15021 {
15022   unsigned int num_random_calls = game.num_random_calls;
15023   int i, j;
15024
15025   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15026   {
15027     int element = EL_CUSTOM_START + i;
15028
15029     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15030   }
15031
15032   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15033   {
15034     int element = EL_GROUP_START + i;
15035
15036     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15037   }
15038
15039   for (i = 0; i < 4; i++)
15040   {
15041     for (j = 0; j < NUM_BELT_PARTS; j++)
15042     {
15043       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15044       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15045
15046       graphic_info[graphic].anim_mode = anim_mode;
15047     }
15048   }
15049
15050   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15051   {
15052     InitRND(tape.random_seed);
15053     for (i = 0; i < num_random_calls; i++)
15054       RND(1);
15055   }
15056
15057   if (game.num_random_calls != num_random_calls)
15058   {
15059     Error(ERR_INFO, "number of random calls out of sync");
15060     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15061     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15062     Error(ERR_EXIT, "this should not happen -- please debug");
15063   }
15064 }
15065
15066 void FreeEngineSnapshotSingle()
15067 {
15068   FreeSnapshotSingle();
15069
15070   setString(&snapshot_level_identifier, NULL);
15071   snapshot_level_nr = -1;
15072 }
15073
15074 void FreeEngineSnapshotList()
15075 {
15076   FreeSnapshotList();
15077 }
15078
15079 ListNode *SaveEngineSnapshotBuffers()
15080 {
15081   ListNode *buffers = NULL;
15082
15083   /* copy some special values to a structure better suited for the snapshot */
15084
15085   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15086     SaveEngineSnapshotValues_RND();
15087   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15088     SaveEngineSnapshotValues_EM();
15089   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15090     SaveEngineSnapshotValues_SP(&buffers);
15091   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15092     SaveEngineSnapshotValues_MM(&buffers);
15093
15094   /* save values stored in special snapshot structure */
15095
15096   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15097     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15099     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15100   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15101     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15102   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15103     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15104
15105   /* save further RND engine values */
15106
15107   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15108   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15109   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15110
15111   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15112   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15113   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15114   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15115
15116   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15117   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15118   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15120   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15121
15122   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15125
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15127
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15129
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15131   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15132
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15151
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15158
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15161
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15167
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15170
15171 #if 0
15172   ListNode *node = engine_snapshot_list_rnd;
15173   int num_bytes = 0;
15174
15175   while (node != NULL)
15176   {
15177     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15178
15179     node = node->next;
15180   }
15181
15182   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15183 #endif
15184
15185   return buffers;
15186 }
15187
15188 void SaveEngineSnapshotSingle()
15189 {
15190   ListNode *buffers = SaveEngineSnapshotBuffers();
15191
15192   /* finally save all snapshot buffers to single snapshot */
15193   SaveSnapshotSingle(buffers);
15194
15195   /* save level identification information */
15196   setString(&snapshot_level_identifier, leveldir_current->identifier);
15197   snapshot_level_nr = level_nr;
15198 }
15199
15200 boolean CheckSaveEngineSnapshotToList()
15201 {
15202   boolean save_snapshot =
15203     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15204      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15205       game.snapshot.changed_action) ||
15206      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15207       game.snapshot.collected_item));
15208
15209   game.snapshot.changed_action = FALSE;
15210   game.snapshot.collected_item = FALSE;
15211   game.snapshot.save_snapshot = save_snapshot;
15212
15213   return save_snapshot;
15214 }
15215
15216 void SaveEngineSnapshotToList()
15217 {
15218   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15219       tape.quick_resume)
15220     return;
15221
15222   ListNode *buffers = SaveEngineSnapshotBuffers();
15223
15224   /* finally save all snapshot buffers to snapshot list */
15225   SaveSnapshotToList(buffers);
15226 }
15227
15228 void SaveEngineSnapshotToListInitial()
15229 {
15230   FreeEngineSnapshotList();
15231
15232   SaveEngineSnapshotToList();
15233 }
15234
15235 void LoadEngineSnapshotValues()
15236 {
15237   /* restore special values from snapshot structure */
15238
15239   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15240     LoadEngineSnapshotValues_RND();
15241   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15242     LoadEngineSnapshotValues_EM();
15243   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15244     LoadEngineSnapshotValues_SP();
15245   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15246     LoadEngineSnapshotValues_MM();
15247 }
15248
15249 void LoadEngineSnapshotSingle()
15250 {
15251   LoadSnapshotSingle();
15252
15253   LoadEngineSnapshotValues();
15254 }
15255
15256 void LoadEngineSnapshot_Undo(int steps)
15257 {
15258   LoadSnapshotFromList_Older(steps);
15259
15260   LoadEngineSnapshotValues();
15261 }
15262
15263 void LoadEngineSnapshot_Redo(int steps)
15264 {
15265   LoadSnapshotFromList_Newer(steps);
15266
15267   LoadEngineSnapshotValues();
15268 }
15269
15270 boolean CheckEngineSnapshotSingle()
15271 {
15272   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15273           snapshot_level_nr == level_nr);
15274 }
15275
15276 boolean CheckEngineSnapshotList()
15277 {
15278   return CheckSnapshotList();
15279 }
15280
15281
15282 /* ---------- new game button stuff ---------------------------------------- */
15283
15284 static struct
15285 {
15286   int graphic;
15287   struct XY *pos;
15288   int gadget_id;
15289   boolean *setup_value;
15290   boolean allowed_on_tape;
15291   char *infotext;
15292 } gamebutton_info[NUM_GAME_BUTTONS] =
15293 {
15294   {
15295     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15296     GAME_CTRL_ID_STOP,                          NULL,
15297     TRUE,                                       "stop game"
15298   },
15299   {
15300     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15301     GAME_CTRL_ID_PAUSE,                         NULL,
15302     TRUE,                                       "pause game"
15303   },
15304   {
15305     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15306     GAME_CTRL_ID_PLAY,                          NULL,
15307     TRUE,                                       "play game"
15308   },
15309   {
15310     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15311     GAME_CTRL_ID_UNDO,                          NULL,
15312     TRUE,                                       "undo step"
15313   },
15314   {
15315     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15316     GAME_CTRL_ID_REDO,                          NULL,
15317     TRUE,                                       "redo step"
15318   },
15319   {
15320     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15321     GAME_CTRL_ID_SAVE,                          NULL,
15322     TRUE,                                       "save game"
15323   },
15324   {
15325     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15326     GAME_CTRL_ID_PAUSE2,                        NULL,
15327     TRUE,                                       "pause game"
15328   },
15329   {
15330     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15331     GAME_CTRL_ID_LOAD,                          NULL,
15332     TRUE,                                       "load game"
15333   },
15334   {
15335     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15336     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15337     FALSE,                                      "stop game"
15338   },
15339   {
15340     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15341     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15342     FALSE,                                      "pause game"
15343   },
15344   {
15345     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15346     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15347     FALSE,                                      "play game"
15348   },
15349   {
15350     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15351     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15352     TRUE,                                       "background music on/off"
15353   },
15354   {
15355     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15356     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15357     TRUE,                                       "sound loops on/off"
15358   },
15359   {
15360     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15361     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15362     TRUE,                                       "normal sounds on/off"
15363   },
15364   {
15365     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15366     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15367     FALSE,                                      "background music on/off"
15368   },
15369   {
15370     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15371     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15372     FALSE,                                      "sound loops on/off"
15373   },
15374   {
15375     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15376     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15377     FALSE,                                      "normal sounds on/off"
15378   }
15379 };
15380
15381 void CreateGameButtons()
15382 {
15383   int i;
15384
15385   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15386   {
15387     int graphic = gamebutton_info[i].graphic;
15388     struct GraphicInfo *gfx = &graphic_info[graphic];
15389     struct XY *pos = gamebutton_info[i].pos;
15390     struct GadgetInfo *gi;
15391     int button_type;
15392     boolean checked;
15393     unsigned int event_mask;
15394     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15395     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15396     int base_x = (on_tape ? VX : DX);
15397     int base_y = (on_tape ? VY : DY);
15398     int gd_x   = gfx->src_x;
15399     int gd_y   = gfx->src_y;
15400     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15401     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15402     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15403     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15404     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15405     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15406     int id = i;
15407
15408     if (gfx->bitmap == NULL)
15409     {
15410       game_gadget[id] = NULL;
15411
15412       continue;
15413     }
15414
15415     if (id == GAME_CTRL_ID_STOP ||
15416         id == GAME_CTRL_ID_PANEL_STOP ||
15417         id == GAME_CTRL_ID_PLAY ||
15418         id == GAME_CTRL_ID_PANEL_PLAY ||
15419         id == GAME_CTRL_ID_SAVE ||
15420         id == GAME_CTRL_ID_LOAD)
15421     {
15422       button_type = GD_TYPE_NORMAL_BUTTON;
15423       checked = FALSE;
15424       event_mask = GD_EVENT_RELEASED;
15425     }
15426     else if (id == GAME_CTRL_ID_UNDO ||
15427              id == GAME_CTRL_ID_REDO)
15428     {
15429       button_type = GD_TYPE_NORMAL_BUTTON;
15430       checked = FALSE;
15431       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15432     }
15433     else
15434     {
15435       button_type = GD_TYPE_CHECK_BUTTON;
15436       checked = (gamebutton_info[i].setup_value != NULL ?
15437                  *gamebutton_info[i].setup_value : FALSE);
15438       event_mask = GD_EVENT_PRESSED;
15439     }
15440
15441     gi = CreateGadget(GDI_CUSTOM_ID, id,
15442                       GDI_IMAGE_ID, graphic,
15443                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15444                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15445                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15446                       GDI_WIDTH, gfx->width,
15447                       GDI_HEIGHT, gfx->height,
15448                       GDI_TYPE, button_type,
15449                       GDI_STATE, GD_BUTTON_UNPRESSED,
15450                       GDI_CHECKED, checked,
15451                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15452                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15453                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15454                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15455                       GDI_DIRECT_DRAW, FALSE,
15456                       GDI_EVENT_MASK, event_mask,
15457                       GDI_CALLBACK_ACTION, HandleGameButtons,
15458                       GDI_END);
15459
15460     if (gi == NULL)
15461       Error(ERR_EXIT, "cannot create gadget");
15462
15463     game_gadget[id] = gi;
15464   }
15465 }
15466
15467 void FreeGameButtons()
15468 {
15469   int i;
15470
15471   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15472     FreeGadget(game_gadget[i]);
15473 }
15474
15475 static void UnmapGameButtonsAtSamePosition(int id)
15476 {
15477   int i;
15478
15479   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15480     if (i != id &&
15481         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15482         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15483       UnmapGadget(game_gadget[i]);
15484 }
15485
15486 static void UnmapGameButtonsAtSamePosition_All()
15487 {
15488   if (setup.show_snapshot_buttons)
15489   {
15490     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15491     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15492     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15493   }
15494   else
15495   {
15496     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15497     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15498     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15499
15500     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15501     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15502     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15503   }
15504 }
15505
15506 static void MapGameButtonsAtSamePosition(int id)
15507 {
15508   int i;
15509
15510   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15511     if (i != id &&
15512         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15513         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15514       MapGadget(game_gadget[i]);
15515
15516   UnmapGameButtonsAtSamePosition_All();
15517 }
15518
15519 void MapUndoRedoButtons()
15520 {
15521   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15522   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15523
15524   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15525   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15526
15527   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15528 }
15529
15530 void UnmapUndoRedoButtons()
15531 {
15532   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15533   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15534
15535   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15536   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15537
15538   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15539 }
15540
15541 void MapGameButtonsExt(boolean on_tape)
15542 {
15543   int i;
15544
15545   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15546     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15547         i != GAME_CTRL_ID_UNDO &&
15548         i != GAME_CTRL_ID_REDO)
15549       MapGadget(game_gadget[i]);
15550
15551   UnmapGameButtonsAtSamePosition_All();
15552
15553   RedrawGameButtons();
15554 }
15555
15556 void UnmapGameButtonsExt(boolean on_tape)
15557 {
15558   int i;
15559
15560   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15561     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15562       UnmapGadget(game_gadget[i]);
15563 }
15564
15565 void RedrawGameButtonsExt(boolean on_tape)
15566 {
15567   int i;
15568
15569   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15570     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15571       RedrawGadget(game_gadget[i]);
15572
15573   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15574   redraw_mask &= ~REDRAW_ALL;
15575 }
15576
15577 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15578 {
15579   if (gi == NULL)
15580     return;
15581
15582   gi->checked = state;
15583 }
15584
15585 void RedrawSoundButtonGadget(int id)
15586 {
15587   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15588              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15589              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15590              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15591              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15592              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15593              id);
15594
15595   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15596   RedrawGadget(game_gadget[id2]);
15597 }
15598
15599 void MapGameButtons()
15600 {
15601   MapGameButtonsExt(FALSE);
15602 }
15603
15604 void UnmapGameButtons()
15605 {
15606   UnmapGameButtonsExt(FALSE);
15607 }
15608
15609 void RedrawGameButtons()
15610 {
15611   RedrawGameButtonsExt(FALSE);
15612 }
15613
15614 void MapGameButtonsOnTape()
15615 {
15616   MapGameButtonsExt(TRUE);
15617 }
15618
15619 void UnmapGameButtonsOnTape()
15620 {
15621   UnmapGameButtonsExt(TRUE);
15622 }
15623
15624 void RedrawGameButtonsOnTape()
15625 {
15626   RedrawGameButtonsExt(TRUE);
15627 }
15628
15629 void GameUndoRedoExt()
15630 {
15631   ClearPlayerAction();
15632
15633   tape.pausing = TRUE;
15634
15635   RedrawPlayfield();
15636   UpdateAndDisplayGameControlValues();
15637
15638   DrawCompleteVideoDisplay();
15639   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15640   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15641   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15642
15643   BackToFront();
15644 }
15645
15646 void GameUndo(int steps)
15647 {
15648   if (!CheckEngineSnapshotList())
15649     return;
15650
15651   LoadEngineSnapshot_Undo(steps);
15652
15653   GameUndoRedoExt();
15654 }
15655
15656 void GameRedo(int steps)
15657 {
15658   if (!CheckEngineSnapshotList())
15659     return;
15660
15661   LoadEngineSnapshot_Redo(steps);
15662
15663   GameUndoRedoExt();
15664 }
15665
15666 static void HandleGameButtonsExt(int id, int button)
15667 {
15668   static boolean game_undo_executed = FALSE;
15669   int steps = BUTTON_STEPSIZE(button);
15670   boolean handle_game_buttons =
15671     (game_status == GAME_MODE_PLAYING ||
15672      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15673
15674   if (!handle_game_buttons)
15675     return;
15676
15677   switch (id)
15678   {
15679     case GAME_CTRL_ID_STOP:
15680     case GAME_CTRL_ID_PANEL_STOP:
15681       if (game_status == GAME_MODE_MAIN)
15682         break;
15683
15684       if (tape.playing)
15685         TapeStop();
15686       else
15687         RequestQuitGame(TRUE);
15688
15689       break;
15690
15691     case GAME_CTRL_ID_PAUSE:
15692     case GAME_CTRL_ID_PAUSE2:
15693     case GAME_CTRL_ID_PANEL_PAUSE:
15694       if (network.enabled && game_status == GAME_MODE_PLAYING)
15695       {
15696         if (tape.pausing)
15697           SendToServer_ContinuePlaying();
15698         else
15699           SendToServer_PausePlaying();
15700       }
15701       else
15702         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15703
15704       game_undo_executed = FALSE;
15705
15706       break;
15707
15708     case GAME_CTRL_ID_PLAY:
15709     case GAME_CTRL_ID_PANEL_PLAY:
15710       if (game_status == GAME_MODE_MAIN)
15711       {
15712         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15713       }
15714       else if (tape.pausing)
15715       {
15716         if (network.enabled)
15717           SendToServer_ContinuePlaying();
15718         else
15719           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15720       }
15721       break;
15722
15723     case GAME_CTRL_ID_UNDO:
15724       // Important: When using "save snapshot when collecting an item" mode,
15725       // load last (current) snapshot for first "undo" after pressing "pause"
15726       // (else the last-but-one snapshot would be loaded, because the snapshot
15727       // pointer already points to the last snapshot when pressing "pause",
15728       // which is fine for "every step/move" mode, but not for "every collect")
15729       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15730           !game_undo_executed)
15731         steps--;
15732
15733       game_undo_executed = TRUE;
15734
15735       GameUndo(steps);
15736       break;
15737
15738     case GAME_CTRL_ID_REDO:
15739       GameRedo(steps);
15740       break;
15741
15742     case GAME_CTRL_ID_SAVE:
15743       TapeQuickSave();
15744       break;
15745
15746     case GAME_CTRL_ID_LOAD:
15747       TapeQuickLoad();
15748       break;
15749
15750     case SOUND_CTRL_ID_MUSIC:
15751     case SOUND_CTRL_ID_PANEL_MUSIC:
15752       if (setup.sound_music)
15753       { 
15754         setup.sound_music = FALSE;
15755
15756         FadeMusic();
15757       }
15758       else if (audio.music_available)
15759       { 
15760         setup.sound = setup.sound_music = TRUE;
15761
15762         SetAudioMode(setup.sound);
15763
15764         if (game_status == GAME_MODE_PLAYING)
15765           PlayLevelMusic();
15766       }
15767
15768       RedrawSoundButtonGadget(id);
15769
15770       break;
15771
15772     case SOUND_CTRL_ID_LOOPS:
15773     case SOUND_CTRL_ID_PANEL_LOOPS:
15774       if (setup.sound_loops)
15775         setup.sound_loops = FALSE;
15776       else if (audio.loops_available)
15777       {
15778         setup.sound = setup.sound_loops = TRUE;
15779
15780         SetAudioMode(setup.sound);
15781       }
15782
15783       RedrawSoundButtonGadget(id);
15784
15785       break;
15786
15787     case SOUND_CTRL_ID_SIMPLE:
15788     case SOUND_CTRL_ID_PANEL_SIMPLE:
15789       if (setup.sound_simple)
15790         setup.sound_simple = FALSE;
15791       else if (audio.sound_available)
15792       {
15793         setup.sound = setup.sound_simple = TRUE;
15794
15795         SetAudioMode(setup.sound);
15796       }
15797
15798       RedrawSoundButtonGadget(id);
15799
15800       break;
15801
15802     default:
15803       break;
15804   }
15805 }
15806
15807 static void HandleGameButtons(struct GadgetInfo *gi)
15808 {
15809   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15810 }
15811
15812 void HandleSoundButtonKeys(Key key)
15813 {
15814   if (key == setup.shortcut.sound_simple)
15815     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15816   else if (key == setup.shortcut.sound_loops)
15817     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15818   else if (key == setup.shortcut.sound_music)
15819     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15820 }