added function to remove player and cleanup last player field
[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 void RemovePlayerWithCleanup(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2228   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229
2230   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2231   for (i = 0; i < MAX_NUM_KEYS; i++)
2232     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235
2236   if (game.centered_player_nr == -1)
2237   {
2238     for (i = 0; i < MAX_PLAYERS; i++)
2239     {
2240       /* only one player in Supaplex game engine */
2241       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2242         break;
2243
2244       for (k = 0; k < MAX_NUM_KEYS; k++)
2245       {
2246         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247         {
2248           if (level.native_em_level->ply[i]->keys & (1 << k))
2249             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250               get_key_element_from_nr(k);
2251         }
2252         else if (stored_player[i].key[k])
2253           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2254             get_key_element_from_nr(k);
2255       }
2256
2257       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258         getPlayerInventorySize(i);
2259
2260       if (stored_player[i].num_white_keys > 0)
2261         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2262           EL_DC_KEY_WHITE;
2263
2264       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2265         stored_player[i].num_white_keys;
2266     }
2267   }
2268   else
2269   {
2270     int player_nr = game.centered_player_nr;
2271
2272     for (k = 0; k < MAX_NUM_KEYS; k++)
2273     {
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275       {
2276         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2277           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278             get_key_element_from_nr(k);
2279       }
2280       else if (stored_player[player_nr].key[k])
2281         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282           get_key_element_from_nr(k);
2283     }
2284
2285     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286       getPlayerInventorySize(player_nr);
2287
2288     if (stored_player[player_nr].num_white_keys > 0)
2289       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290
2291     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2292       stored_player[player_nr].num_white_keys;
2293   }
2294
2295   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296   {
2297     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2298       get_inventory_element_from_pos(local_player, i);
2299     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, -i - 1);
2301   }
2302
2303   game_panel_controls[GAME_PANEL_SCORE].value = score;
2304   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305
2306   game_panel_controls[GAME_PANEL_TIME].value = time;
2307
2308   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2309   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2310   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311
2312   if (level.time == 0)
2313     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314   else
2315     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316
2317   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2318   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319
2320   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321
2322   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324      EL_EMPTY);
2325   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326     local_player->shield_normal_time_left;
2327   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329      EL_EMPTY);
2330   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331     local_player->shield_deadly_time_left;
2332
2333   game_panel_controls[GAME_PANEL_EXIT].value =
2334     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335
2336   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340      EL_EMC_MAGIC_BALL_SWITCH);
2341
2342   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345     game.light_time_left;
2346
2347   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350     game.timegate_time_left;
2351
2352   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354
2355   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358     game.lenses_time_left;
2359
2360   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363     game.magnify_time_left;
2364
2365   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2367      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2369      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2370      EL_BALLOON_SWITCH_NONE);
2371
2372   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373     local_player->dynabomb_count;
2374   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375     local_player->dynabomb_size;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378
2379   game_panel_controls[GAME_PANEL_PENGUINS].value =
2380     local_player->friends_still_needed;
2381
2382   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383     local_player->sokobanfields_still_needed;
2384   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385     local_player->sokobanfields_still_needed;
2386
2387   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389
2390   for (i = 0; i < NUM_BELTS; i++)
2391   {
2392     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2397   }
2398
2399   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402     game.magic_wall_time_left;
2403
2404   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2405     local_player->gravity;
2406
2407   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2408     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409
2410   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2412       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2413        game.panel.element[i].id : EL_UNDEFINED);
2414
2415   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2417       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2418        element_info[game.panel.element_count[i].id].element_count : 0);
2419
2420   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2422       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2423        element_info[game.panel.ce_score[i].id].collect_score : 0);
2424
2425   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2427       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2428        element_info[game.panel.ce_score_element[i].id].collect_score :
2429        EL_UNDEFINED);
2430
2431   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434
2435   /* update game panel control frames */
2436
2437   for (i = 0; game_panel_controls[i].nr != -1; i++)
2438   {
2439     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440
2441     if (gpc->type == TYPE_ELEMENT)
2442     {
2443       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444       {
2445         int last_anim_random_frame = gfx.anim_random_frame;
2446         int element = gpc->value;
2447         int graphic = el2panelimg(element);
2448
2449         if (gpc->value != gpc->last_value)
2450         {
2451           gpc->gfx_frame = 0;
2452           gpc->gfx_random = INIT_GFX_RANDOM();
2453         }
2454         else
2455         {
2456           gpc->gfx_frame++;
2457
2458           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2459               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2460             gpc->gfx_random = INIT_GFX_RANDOM();
2461         }
2462
2463         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2464           gfx.anim_random_frame = gpc->gfx_random;
2465
2466         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2467           gpc->gfx_frame = element_info[element].collect_score;
2468
2469         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2470                                               gpc->gfx_frame);
2471
2472         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473           gfx.anim_random_frame = last_anim_random_frame;
2474       }
2475     }
2476     else if (gpc->type == TYPE_GRAPHIC)
2477     {
2478       if (gpc->graphic != IMG_UNDEFINED)
2479       {
2480         int last_anim_random_frame = gfx.anim_random_frame;
2481         int graphic = gpc->graphic;
2482
2483         if (gpc->value != gpc->last_value)
2484         {
2485           gpc->gfx_frame = 0;
2486           gpc->gfx_random = INIT_GFX_RANDOM();
2487         }
2488         else
2489         {
2490           gpc->gfx_frame++;
2491
2492           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494             gpc->gfx_random = INIT_GFX_RANDOM();
2495         }
2496
2497         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498           gfx.anim_random_frame = gpc->gfx_random;
2499
2500         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506   }
2507 }
2508
2509 void DisplayGameControlValues()
2510 {
2511   boolean redraw_panel = FALSE;
2512   int i;
2513
2514   for (i = 0; game_panel_controls[i].nr != -1; i++)
2515   {
2516     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517
2518     if (PANEL_DEACTIVATED(gpc->pos))
2519       continue;
2520
2521     if (gpc->value == gpc->last_value &&
2522         gpc->frame == gpc->last_frame)
2523       continue;
2524
2525     redraw_panel = TRUE;
2526   }
2527
2528   if (!redraw_panel)
2529     return;
2530
2531   /* copy default game door content to main double buffer */
2532
2533   /* !!! CHECK AGAIN !!! */
2534   SetPanelBackground();
2535   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2536   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537
2538   /* redraw game control buttons */
2539   RedrawGameButtons();
2540
2541   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542
2543   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544   {
2545     int nr = game_panel_order[i].nr;
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2547     struct TextPosInfo *pos = gpc->pos;
2548     int type = gpc->type;
2549     int value = gpc->value;
2550     int frame = gpc->frame;
2551     int size = pos->size;
2552     int font = pos->font;
2553     boolean draw_masked = pos->draw_masked;
2554     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555
2556     if (PANEL_DEACTIVATED(pos))
2557       continue;
2558
2559     gpc->last_value = value;
2560     gpc->last_frame = frame;
2561
2562     if (type == TYPE_INTEGER)
2563     {
2564       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2565           nr == GAME_PANEL_TIME)
2566       {
2567         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568
2569         if (use_dynamic_size)           /* use dynamic number of digits */
2570         {
2571           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2572           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2573           int size2 = size1 + 1;
2574           int font1 = pos->font;
2575           int font2 = pos->font_alt;
2576
2577           size = (value < value_change ? size1 : size2);
2578           font = (value < value_change ? font1 : font2);
2579         }
2580       }
2581
2582       /* correct text size if "digits" is zero or less */
2583       if (size <= 0)
2584         size = strlen(int2str(value, size));
2585
2586       /* dynamically correct text alignment */
2587       pos->width = size * getFontWidth(font);
2588
2589       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2590                   int2str(value, size), font, mask_mode);
2591     }
2592     else if (type == TYPE_ELEMENT)
2593     {
2594       int element, graphic;
2595       Bitmap *src_bitmap;
2596       int src_x, src_y;
2597       int width, height;
2598       int dst_x = PANEL_XPOS(pos);
2599       int dst_y = PANEL_YPOS(pos);
2600
2601       if (value != EL_UNDEFINED && value != EL_EMPTY)
2602       {
2603         element = value;
2604         graphic = el2panelimg(value);
2605
2606         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607
2608         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2609           size = TILESIZE;
2610
2611         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2612                               &src_x, &src_y);
2613
2614         width  = graphic_info[graphic].width  * size / TILESIZE;
2615         height = graphic_info[graphic].height * size / TILESIZE;
2616
2617         if (draw_masked)
2618           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2619                            dst_x, dst_y);
2620         else
2621           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2622                      dst_x, dst_y);
2623       }
2624     }
2625     else if (type == TYPE_GRAPHIC)
2626     {
2627       int graphic        = gpc->graphic;
2628       int graphic_active = gpc->graphic_active;
2629       Bitmap *src_bitmap;
2630       int src_x, src_y;
2631       int width, height;
2632       int dst_x = PANEL_XPOS(pos);
2633       int dst_y = PANEL_YPOS(pos);
2634       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2635                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636
2637       if (graphic != IMG_UNDEFINED && !skip)
2638       {
2639         if (pos->style == STYLE_REVERSE)
2640           value = 100 - value;
2641
2642         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643
2644         if (pos->direction & MV_HORIZONTAL)
2645         {
2646           width  = graphic_info[graphic_active].width * value / 100;
2647           height = graphic_info[graphic_active].height;
2648
2649           if (pos->direction == MV_LEFT)
2650           {
2651             src_x += graphic_info[graphic_active].width - width;
2652             dst_x += graphic_info[graphic_active].width - width;
2653           }
2654         }
2655         else
2656         {
2657           width  = graphic_info[graphic_active].width;
2658           height = graphic_info[graphic_active].height * value / 100;
2659
2660           if (pos->direction == MV_UP)
2661           {
2662             src_y += graphic_info[graphic_active].height - height;
2663             dst_y += graphic_info[graphic_active].height - height;
2664           }
2665         }
2666
2667         if (draw_masked)
2668           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2669                            dst_x, dst_y);
2670         else
2671           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2672                      dst_x, dst_y);
2673
2674         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           if (pos->direction == MV_RIGHT)
2679           {
2680             src_x += width;
2681             dst_x += width;
2682           }
2683           else
2684           {
2685             dst_x = PANEL_XPOS(pos);
2686           }
2687
2688           width = graphic_info[graphic].width - width;
2689         }
2690         else
2691         {
2692           if (pos->direction == MV_DOWN)
2693           {
2694             src_y += height;
2695             dst_y += height;
2696           }
2697           else
2698           {
2699             dst_y = PANEL_YPOS(pos);
2700           }
2701
2702           height = graphic_info[graphic].height - height;
2703         }
2704
2705         if (draw_masked)
2706           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2707                            dst_x, dst_y);
2708         else
2709           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2710                      dst_x, dst_y);
2711       }
2712     }
2713     else if (type == TYPE_STRING)
2714     {
2715       boolean active = (value != 0);
2716       char *state_normal = "off";
2717       char *state_active = "on";
2718       char *state = (active ? state_active : state_normal);
2719       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2720                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2721                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2722                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2723
2724       if (nr == GAME_PANEL_GRAVITY_STATE)
2725       {
2726         int font1 = pos->font;          /* (used for normal state) */
2727         int font2 = pos->font_alt;      /* (used for active state) */
2728
2729         font = (active ? font2 : font1);
2730       }
2731
2732       if (s != NULL)
2733       {
2734         char *s_cut;
2735
2736         if (size <= 0)
2737         {
2738           /* don't truncate output if "chars" is zero or less */
2739           size = strlen(s);
2740
2741           /* dynamically correct text alignment */
2742           pos->width = size * getFontWidth(font);
2743         }
2744
2745         s_cut = getStringCopyN(s, size);
2746
2747         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2748                     s_cut, font, mask_mode);
2749
2750         free(s_cut);
2751       }
2752     }
2753
2754     redraw_mask |= REDRAW_DOOR_1;
2755   }
2756
2757   SetGameStatus(GAME_MODE_PLAYING);
2758 }
2759
2760 void UpdateAndDisplayGameControlValues()
2761 {
2762   if (tape.deactivate_display)
2763     return;
2764
2765   UpdateGameControlValues();
2766   DisplayGameControlValues();
2767 }
2768
2769 void UpdateGameDoorValues()
2770 {
2771   UpdateGameControlValues();
2772 }
2773
2774 void DrawGameDoorValues()
2775 {
2776   DisplayGameControlValues();
2777 }
2778
2779
2780 /*
2781   =============================================================================
2782   InitGameEngine()
2783   -----------------------------------------------------------------------------
2784   initialize game engine due to level / tape version number
2785   =============================================================================
2786 */
2787
2788 static void InitGameEngine()
2789 {
2790   int i, j, k, l, x, y;
2791
2792   /* set game engine from tape file when re-playing, else from level file */
2793   game.engine_version = (tape.playing ? tape.engine_version :
2794                          level.game_version);
2795
2796   /* set single or multi-player game mode (needed for re-playing tapes) */
2797   game.team_mode = setup.team_mode;
2798
2799   if (tape.playing)
2800   {
2801     int num_players = 0;
2802
2803     for (i = 0; i < MAX_PLAYERS; i++)
2804       if (tape.player_participates[i])
2805         num_players++;
2806
2807     /* multi-player tapes contain input data for more than one player */
2808     game.team_mode = (num_players > 1);
2809   }
2810
2811   /* ---------------------------------------------------------------------- */
2812   /* set flags for bugs and changes according to active game engine version */
2813   /* ---------------------------------------------------------------------- */
2814
2815   /*
2816     Summary of bugfix/change:
2817     Fixed handling for custom elements that change when pushed by the player.
2818
2819     Fixed/changed in version:
2820     3.1.0
2821
2822     Description:
2823     Before 3.1.0, custom elements that "change when pushing" changed directly
2824     after the player started pushing them (until then handled in "DigField()").
2825     Since 3.1.0, these custom elements are not changed until the "pushing"
2826     move of the element is finished (now handled in "ContinueMoving()").
2827
2828     Affected levels/tapes:
2829     The first condition is generally needed for all levels/tapes before version
2830     3.1.0, which might use the old behaviour before it was changed; known tapes
2831     that are affected are some tapes from the level set "Walpurgis Gardens" by
2832     Jamie Cullen.
2833     The second condition is an exception from the above case and is needed for
2834     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2835     above (including some development versions of 3.1.0), but before it was
2836     known that this change would break tapes like the above and was fixed in
2837     3.1.1, so that the changed behaviour was active although the engine version
2838     while recording maybe was before 3.1.0. There is at least one tape that is
2839     affected by this exception, which is the tape for the one-level set "Bug
2840     Machine" by Juergen Bonhagen.
2841   */
2842
2843   game.use_change_when_pushing_bug =
2844     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845      !(tape.playing &&
2846        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2847        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2848
2849   /*
2850     Summary of bugfix/change:
2851     Fixed handling for blocking the field the player leaves when moving.
2852
2853     Fixed/changed in version:
2854     3.1.1
2855
2856     Description:
2857     Before 3.1.1, when "block last field when moving" was enabled, the field
2858     the player is leaving when moving was blocked for the time of the move,
2859     and was directly unblocked afterwards. This resulted in the last field
2860     being blocked for exactly one less than the number of frames of one player
2861     move. Additionally, even when blocking was disabled, the last field was
2862     blocked for exactly one frame.
2863     Since 3.1.1, due to changes in player movement handling, the last field
2864     is not blocked at all when blocking is disabled. When blocking is enabled,
2865     the last field is blocked for exactly the number of frames of one player
2866     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2867     last field is blocked for exactly one more than the number of frames of
2868     one player move.
2869
2870     Affected levels/tapes:
2871     (!!! yet to be determined -- probably many !!!)
2872   */
2873
2874   game.use_block_last_field_bug =
2875     (game.engine_version < VERSION_IDENT(3,1,1,0));
2876
2877   game_em.use_single_button =
2878     (game.engine_version > VERSION_IDENT(4,0,0,2));
2879
2880   game_em.use_snap_key_bug =
2881     (game.engine_version < VERSION_IDENT(4,0,1,0));
2882
2883   /* ---------------------------------------------------------------------- */
2884
2885   /* set maximal allowed number of custom element changes per game frame */
2886   game.max_num_changes_per_frame = 1;
2887
2888   /* default scan direction: scan playfield from top/left to bottom/right */
2889   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890
2891   /* dynamically adjust element properties according to game engine version */
2892   InitElementPropertiesEngine(game.engine_version);
2893
2894 #if 0
2895   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2896   printf("          tape version == %06d [%s] [file: %06d]\n",
2897          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898          tape.file_version);
2899   printf("       => game.engine_version == %06d\n", game.engine_version);
2900 #endif
2901
2902   /* ---------- initialize player's initial move delay --------------------- */
2903
2904   /* dynamically adjust player properties according to level information */
2905   for (i = 0; i < MAX_PLAYERS; i++)
2906     game.initial_move_delay_value[i] =
2907       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908
2909   /* dynamically adjust player properties according to game engine version */
2910   for (i = 0; i < MAX_PLAYERS; i++)
2911     game.initial_move_delay[i] =
2912       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2913        game.initial_move_delay_value[i] : 0);
2914
2915   /* ---------- initialize player's initial push delay --------------------- */
2916
2917   /* dynamically adjust player properties according to game engine version */
2918   game.initial_push_delay_value =
2919     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920
2921   /* ---------- initialize changing elements ------------------------------- */
2922
2923   /* initialize changing elements information */
2924   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925   {
2926     struct ElementInfo *ei = &element_info[i];
2927
2928     /* this pointer might have been changed in the level editor */
2929     ei->change = &ei->change_page[0];
2930
2931     if (!IS_CUSTOM_ELEMENT(i))
2932     {
2933       ei->change->target_element = EL_EMPTY_SPACE;
2934       ei->change->delay_fixed = 0;
2935       ei->change->delay_random = 0;
2936       ei->change->delay_frames = 1;
2937     }
2938
2939     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940     {
2941       ei->has_change_event[j] = FALSE;
2942
2943       ei->event_page_nr[j] = 0;
2944       ei->event_page[j] = &ei->change_page[0];
2945     }
2946   }
2947
2948   /* add changing elements from pre-defined list */
2949   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950   {
2951     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2952     struct ElementInfo *ei = &element_info[ch_delay->element];
2953
2954     ei->change->target_element       = ch_delay->target_element;
2955     ei->change->delay_fixed          = ch_delay->change_delay;
2956
2957     ei->change->pre_change_function  = ch_delay->pre_change_function;
2958     ei->change->change_function      = ch_delay->change_function;
2959     ei->change->post_change_function = ch_delay->post_change_function;
2960
2961     ei->change->can_change = TRUE;
2962     ei->change->can_change_or_has_action = TRUE;
2963
2964     ei->has_change_event[CE_DELAY] = TRUE;
2965
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2968   }
2969
2970   /* ---------- initialize internal run-time variables --------------------- */
2971
2972   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973   {
2974     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975
2976     for (j = 0; j < ei->num_change_pages; j++)
2977     {
2978       ei->change_page[j].can_change_or_has_action =
2979         (ei->change_page[j].can_change |
2980          ei->change_page[j].has_action);
2981     }
2982   }
2983
2984   /* add change events from custom element configuration */
2985   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986   {
2987     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988
2989     for (j = 0; j < ei->num_change_pages; j++)
2990     {
2991       if (!ei->change_page[j].can_change_or_has_action)
2992         continue;
2993
2994       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995       {
2996         /* only add event page for the first page found with this event */
2997         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998         {
2999           ei->has_change_event[k] = TRUE;
3000
3001           ei->event_page_nr[k] = j;
3002           ei->event_page[k] = &ei->change_page[j];
3003         }
3004       }
3005     }
3006   }
3007
3008   /* ---------- initialize reference elements in change conditions --------- */
3009
3010   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011   {
3012     int element = EL_CUSTOM_START + i;
3013     struct ElementInfo *ei = &element_info[element];
3014
3015     for (j = 0; j < ei->num_change_pages; j++)
3016     {
3017       int trigger_element = ei->change_page[j].initial_trigger_element;
3018
3019       if (trigger_element >= EL_PREV_CE_8 &&
3020           trigger_element <= EL_NEXT_CE_8)
3021         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022
3023       ei->change_page[j].trigger_element = trigger_element;
3024     }
3025   }
3026
3027   /* ---------- initialize run-time trigger player and element ------------- */
3028
3029   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030   {
3031     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032
3033     for (j = 0; j < ei->num_change_pages; j++)
3034     {
3035       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3038       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3039       ei->change_page[j].actual_trigger_ce_value = 0;
3040       ei->change_page[j].actual_trigger_ce_score = 0;
3041     }
3042   }
3043
3044   /* ---------- initialize trigger events ---------------------------------- */
3045
3046   /* initialize trigger events information */
3047   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3048     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049       trigger_events[i][j] = FALSE;
3050
3051   /* add trigger events from element change event properties */
3052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053   {
3054     struct ElementInfo *ei = &element_info[i];
3055
3056     for (j = 0; j < ei->num_change_pages; j++)
3057     {
3058       if (!ei->change_page[j].can_change_or_has_action)
3059         continue;
3060
3061       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062       {
3063         int trigger_element = ei->change_page[j].trigger_element;
3064
3065         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066         {
3067           if (ei->change_page[j].has_event[k])
3068           {
3069             if (IS_GROUP_ELEMENT(trigger_element))
3070             {
3071               struct ElementGroupInfo *group =
3072                 element_info[trigger_element].group;
3073
3074               for (l = 0; l < group->num_elements_resolved; l++)
3075                 trigger_events[group->element_resolved[l]][k] = TRUE;
3076             }
3077             else if (trigger_element == EL_ANY_ELEMENT)
3078               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3079                 trigger_events[l][k] = TRUE;
3080             else
3081               trigger_events[trigger_element][k] = TRUE;
3082           }
3083         }
3084       }
3085     }
3086   }
3087
3088   /* ---------- initialize push delay -------------------------------------- */
3089
3090   /* initialize push delay values to default */
3091   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092   {
3093     if (!IS_CUSTOM_ELEMENT(i))
3094     {
3095       /* set default push delay values (corrected since version 3.0.7-1) */
3096       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097       {
3098         element_info[i].push_delay_fixed = 2;
3099         element_info[i].push_delay_random = 8;
3100       }
3101       else
3102       {
3103         element_info[i].push_delay_fixed = 8;
3104         element_info[i].push_delay_random = 8;
3105       }
3106     }
3107   }
3108
3109   /* set push delay value for certain elements from pre-defined list */
3110   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111   {
3112     int e = push_delay_list[i].element;
3113
3114     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3115     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3116   }
3117
3118   /* set push delay value for Supaplex elements for newer engine versions */
3119   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120   {
3121     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122     {
3123       if (IS_SP_ELEMENT(i))
3124       {
3125         /* set SP push delay to just enough to push under a falling zonk */
3126         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127
3128         element_info[i].push_delay_fixed  = delay;
3129         element_info[i].push_delay_random = 0;
3130       }
3131     }
3132   }
3133
3134   /* ---------- initialize move stepsize ----------------------------------- */
3135
3136   /* initialize move stepsize values to default */
3137   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3138     if (!IS_CUSTOM_ELEMENT(i))
3139       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140
3141   /* set move stepsize value for certain elements from pre-defined list */
3142   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143   {
3144     int e = move_stepsize_list[i].element;
3145
3146     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3147   }
3148
3149   /* ---------- initialize collect score ----------------------------------- */
3150
3151   /* initialize collect score values for custom elements from initial value */
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153     if (IS_CUSTOM_ELEMENT(i))
3154       element_info[i].collect_score = element_info[i].collect_score_initial;
3155
3156   /* ---------- initialize collect count ----------------------------------- */
3157
3158   /* initialize collect count values for non-custom elements */
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160     if (!IS_CUSTOM_ELEMENT(i))
3161       element_info[i].collect_count_initial = 0;
3162
3163   /* add collect count values for all elements from pre-defined list */
3164   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3165     element_info[collect_count_list[i].element].collect_count_initial =
3166       collect_count_list[i].count;
3167
3168   /* ---------- initialize access direction -------------------------------- */
3169
3170   /* initialize access direction values to default (access from every side) */
3171   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3172     if (!IS_CUSTOM_ELEMENT(i))
3173       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174
3175   /* set access direction value for certain elements from pre-defined list */
3176   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3177     element_info[access_direction_list[i].element].access_direction =
3178       access_direction_list[i].direction;
3179
3180   /* ---------- initialize explosion content ------------------------------- */
3181   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182   {
3183     if (IS_CUSTOM_ELEMENT(i))
3184       continue;
3185
3186     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187     {
3188       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189
3190       element_info[i].content.e[x][y] =
3191         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3192          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3193          i == EL_PLAYER_3 ? EL_EMERALD :
3194          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3195          i == EL_MOLE ? EL_EMERALD_RED :
3196          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3197          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3198          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3199          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3200          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3201          i == EL_WALL_EMERALD ? EL_EMERALD :
3202          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3203          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3204          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3205          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3206          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3207          i == EL_WALL_PEARL ? EL_PEARL :
3208          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3209          EL_EMPTY);
3210     }
3211   }
3212
3213   /* ---------- initialize recursion detection ------------------------------ */
3214   recursion_loop_depth = 0;
3215   recursion_loop_detected = FALSE;
3216   recursion_loop_element = EL_UNDEFINED;
3217
3218   /* ---------- initialize graphics engine ---------------------------------- */
3219   game.scroll_delay_value =
3220     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3221      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3222   game.scroll_delay_value =
3223     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224
3225   /* ---------- initialize game engine snapshots ---------------------------- */
3226   for (i = 0; i < MAX_PLAYERS; i++)
3227     game.snapshot.last_action[i] = 0;
3228   game.snapshot.changed_action = FALSE;
3229   game.snapshot.collected_item = FALSE;
3230   game.snapshot.mode =
3231     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3232      SNAPSHOT_MODE_EVERY_STEP :
3233      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3234      SNAPSHOT_MODE_EVERY_MOVE :
3235      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3236      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3237   game.snapshot.save_snapshot = FALSE;
3238
3239   /* ---------- initialize level time for Supaplex engine ------------------- */
3240   /* Supaplex levels with time limit currently unsupported -- should be added */
3241   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3242     level.time = 0;
3243 }
3244
3245 int get_num_special_action(int element, int action_first, int action_last)
3246 {
3247   int num_special_action = 0;
3248   int i, j;
3249
3250   for (i = action_first; i <= action_last; i++)
3251   {
3252     boolean found = FALSE;
3253
3254     for (j = 0; j < NUM_DIRECTIONS; j++)
3255       if (el_act_dir2img(element, i, j) !=
3256           el_act_dir2img(element, ACTION_DEFAULT, j))
3257         found = TRUE;
3258
3259     if (found)
3260       num_special_action++;
3261     else
3262       break;
3263   }
3264
3265   return num_special_action;
3266 }
3267
3268
3269 /*
3270   =============================================================================
3271   InitGame()
3272   -----------------------------------------------------------------------------
3273   initialize and start new game
3274   =============================================================================
3275 */
3276
3277 #if DEBUG_INIT_PLAYER
3278 static void DebugPrintPlayerStatus(char *message)
3279 {
3280   int i;
3281
3282   if (!options.debug)
3283     return;
3284
3285   printf("%s:\n", message);
3286
3287   for (i = 0; i < MAX_PLAYERS; i++)
3288   {
3289     struct PlayerInfo *player = &stored_player[i];
3290
3291     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3292            i + 1,
3293            player->present,
3294            player->connected,
3295            player->connected_locally,
3296            player->connected_network,
3297            player->active);
3298
3299     if (local_player == player)
3300       printf(" (local player)");
3301
3302     printf("\n");
3303   }
3304 }
3305 #endif
3306
3307 void InitGame()
3308 {
3309   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3310   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int fade_mask = REDRAW_FIELD;
3312
3313   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3314   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3315   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3316   int initial_move_dir = MV_DOWN;
3317   int i, j, x, y;
3318
3319   // required here to update video display before fading (FIX THIS)
3320   DrawMaskedBorder(REDRAW_DOOR_2);
3321
3322   if (!game.restart_level)
3323     CloseDoor(DOOR_CLOSE_1);
3324
3325   SetGameStatus(GAME_MODE_PLAYING);
3326
3327   if (level_editor_test_game)
3328     FadeSkipNextFadeIn();
3329   else
3330     FadeSetEnterScreen();
3331
3332   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3333     fade_mask = REDRAW_ALL;
3334
3335   FadeLevelSoundsAndMusic();
3336
3337   ExpireSoundLoops(TRUE);
3338
3339   if (!level_editor_test_game)
3340     FadeOut(fade_mask);
3341
3342   /* needed if different viewport properties defined for playing */
3343   ChangeViewportPropertiesIfNeeded();
3344
3345   ClearField();
3346
3347   DrawCompleteVideoDisplay();
3348
3349   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3350
3351   InitGameEngine();
3352   InitGameControlValues();
3353
3354   /* don't play tapes over network */
3355   network_playing = (network.enabled && !tape.playing);
3356
3357   for (i = 0; i < MAX_PLAYERS; i++)
3358   {
3359     struct PlayerInfo *player = &stored_player[i];
3360
3361     player->index_nr = i;
3362     player->index_bit = (1 << i);
3363     player->element_nr = EL_PLAYER_1 + i;
3364
3365     player->present = FALSE;
3366     player->active = FALSE;
3367     player->mapped = FALSE;
3368
3369     player->killed = FALSE;
3370     player->reanimated = FALSE;
3371
3372     player->action = 0;
3373     player->effective_action = 0;
3374     player->programmed_action = 0;
3375
3376     player->mouse_action.lx = 0;
3377     player->mouse_action.ly = 0;
3378     player->mouse_action.button = 0;
3379     player->mouse_action.button_hint = 0;
3380
3381     player->effective_mouse_action.lx = 0;
3382     player->effective_mouse_action.ly = 0;
3383     player->effective_mouse_action.button = 0;
3384     player->effective_mouse_action.button_hint = 0;
3385
3386     player->score = 0;
3387     player->score_final = 0;
3388
3389     player->health = MAX_HEALTH;
3390     player->health_final = MAX_HEALTH;
3391
3392     player->gems_still_needed = level.gems_needed;
3393     player->sokobanfields_still_needed = 0;
3394     player->lights_still_needed = 0;
3395     player->friends_still_needed = 0;
3396
3397     for (j = 0; j < MAX_NUM_KEYS; j++)
3398       player->key[j] = FALSE;
3399
3400     player->num_white_keys = 0;
3401
3402     player->dynabomb_count = 0;
3403     player->dynabomb_size = 1;
3404     player->dynabombs_left = 0;
3405     player->dynabomb_xl = FALSE;
3406
3407     player->MovDir = initial_move_dir;
3408     player->MovPos = 0;
3409     player->GfxPos = 0;
3410     player->GfxDir = initial_move_dir;
3411     player->GfxAction = ACTION_DEFAULT;
3412     player->Frame = 0;
3413     player->StepFrame = 0;
3414
3415     player->initial_element = player->element_nr;
3416     player->artwork_element =
3417       (level.use_artwork_element[i] ? level.artwork_element[i] :
3418        player->element_nr);
3419     player->use_murphy = FALSE;
3420
3421     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3422     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3423
3424     player->gravity = level.initial_player_gravity[i];
3425
3426     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3427
3428     player->actual_frame_counter = 0;
3429
3430     player->step_counter = 0;
3431
3432     player->last_move_dir = initial_move_dir;
3433
3434     player->is_active = FALSE;
3435
3436     player->is_waiting = FALSE;
3437     player->is_moving = FALSE;
3438     player->is_auto_moving = FALSE;
3439     player->is_digging = FALSE;
3440     player->is_snapping = FALSE;
3441     player->is_collecting = FALSE;
3442     player->is_pushing = FALSE;
3443     player->is_switching = FALSE;
3444     player->is_dropping = FALSE;
3445     player->is_dropping_pressed = FALSE;
3446
3447     player->is_bored = FALSE;
3448     player->is_sleeping = FALSE;
3449
3450     player->was_waiting = TRUE;
3451     player->was_moving = FALSE;
3452     player->was_snapping = FALSE;
3453     player->was_dropping = FALSE;
3454
3455     player->force_dropping = FALSE;
3456
3457     player->frame_counter_bored = -1;
3458     player->frame_counter_sleeping = -1;
3459
3460     player->anim_delay_counter = 0;
3461     player->post_delay_counter = 0;
3462
3463     player->dir_waiting = initial_move_dir;
3464     player->action_waiting = ACTION_DEFAULT;
3465     player->last_action_waiting = ACTION_DEFAULT;
3466     player->special_action_bored = ACTION_DEFAULT;
3467     player->special_action_sleeping = ACTION_DEFAULT;
3468
3469     player->switch_x = -1;
3470     player->switch_y = -1;
3471
3472     player->drop_x = -1;
3473     player->drop_y = -1;
3474
3475     player->show_envelope = 0;
3476
3477     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3478
3479     player->push_delay       = -1;      /* initialized when pushing starts */
3480     player->push_delay_value = game.initial_push_delay_value;
3481
3482     player->drop_delay = 0;
3483     player->drop_pressed_delay = 0;
3484
3485     player->last_jx = -1;
3486     player->last_jy = -1;
3487     player->jx = -1;
3488     player->jy = -1;
3489
3490     player->shield_normal_time_left = 0;
3491     player->shield_deadly_time_left = 0;
3492
3493     player->inventory_infinite_element = EL_UNDEFINED;
3494     player->inventory_size = 0;
3495
3496     if (level.use_initial_inventory[i])
3497     {
3498       for (j = 0; j < level.initial_inventory_size[i]; j++)
3499       {
3500         int element = level.initial_inventory_content[i][j];
3501         int collect_count = element_info[element].collect_count_initial;
3502         int k;
3503
3504         if (!IS_CUSTOM_ELEMENT(element))
3505           collect_count = 1;
3506
3507         if (collect_count == 0)
3508           player->inventory_infinite_element = element;
3509         else
3510           for (k = 0; k < collect_count; k++)
3511             if (player->inventory_size < MAX_INVENTORY_SIZE)
3512               player->inventory_element[player->inventory_size++] = element;
3513       }
3514     }
3515
3516     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3517     SnapField(player, 0, 0);
3518
3519     player->LevelSolved = FALSE;
3520     player->GameOver = FALSE;
3521
3522     player->LevelSolved_GameWon = FALSE;
3523     player->LevelSolved_GameEnd = FALSE;
3524     player->LevelSolved_PanelOff = FALSE;
3525     player->LevelSolved_SaveTape = FALSE;
3526     player->LevelSolved_SaveScore = FALSE;
3527
3528     player->LevelSolved_CountingTime = 0;
3529     player->LevelSolved_CountingScore = 0;
3530     player->LevelSolved_CountingHealth = 0;
3531
3532     map_player_action[i] = i;
3533   }
3534
3535   network_player_action_received = FALSE;
3536
3537   /* initial null action */
3538   if (network_playing)
3539     SendToServer_MovePlayer(MV_NONE);
3540
3541   ZX = ZY = -1;
3542   ExitX = ExitY = -1;
3543
3544   FrameCounter = 0;
3545   TimeFrames = 0;
3546   TimePlayed = 0;
3547   TimeLeft = level.time;
3548   TapeTime = 0;
3549
3550   ScreenMovDir = MV_NONE;
3551   ScreenMovPos = 0;
3552   ScreenGfxPos = 0;
3553
3554   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3555
3556   AllPlayersGone = FALSE;
3557
3558   game.no_time_limit = (level.time == 0);
3559
3560   game.yamyam_content_nr = 0;
3561   game.robot_wheel_active = FALSE;
3562   game.magic_wall_active = FALSE;
3563   game.magic_wall_time_left = 0;
3564   game.light_time_left = 0;
3565   game.timegate_time_left = 0;
3566   game.switchgate_pos = 0;
3567   game.wind_direction = level.wind_direction_initial;
3568
3569   game.lenses_time_left = 0;
3570   game.magnify_time_left = 0;
3571
3572   game.ball_state = level.ball_state_initial;
3573   game.ball_content_nr = 0;
3574
3575   game.envelope_active = FALSE;
3576
3577   for (i = 0; i < NUM_BELTS; i++)
3578   {
3579     game.belt_dir[i] = MV_NONE;
3580     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3581   }
3582
3583   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3584     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3585
3586 #if DEBUG_INIT_PLAYER
3587   DebugPrintPlayerStatus("Player status at level initialization");
3588 #endif
3589
3590   SCAN_PLAYFIELD(x, y)
3591   {
3592     Feld[x][y] = level.field[x][y];
3593     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3594     ChangeDelay[x][y] = 0;
3595     ChangePage[x][y] = -1;
3596     CustomValue[x][y] = 0;              /* initialized in InitField() */
3597     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3598     AmoebaNr[x][y] = 0;
3599     WasJustMoving[x][y] = 0;
3600     WasJustFalling[x][y] = 0;
3601     CheckCollision[x][y] = 0;
3602     CheckImpact[x][y] = 0;
3603     Stop[x][y] = FALSE;
3604     Pushed[x][y] = FALSE;
3605
3606     ChangeCount[x][y] = 0;
3607     ChangeEvent[x][y] = -1;
3608
3609     ExplodePhase[x][y] = 0;
3610     ExplodeDelay[x][y] = 0;
3611     ExplodeField[x][y] = EX_TYPE_NONE;
3612
3613     RunnerVisit[x][y] = 0;
3614     PlayerVisit[x][y] = 0;
3615
3616     GfxFrame[x][y] = 0;
3617     GfxRandom[x][y] = INIT_GFX_RANDOM();
3618     GfxElement[x][y] = EL_UNDEFINED;
3619     GfxAction[x][y] = ACTION_DEFAULT;
3620     GfxDir[x][y] = MV_NONE;
3621     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3622   }
3623
3624   SCAN_PLAYFIELD(x, y)
3625   {
3626     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3627       emulate_bd = FALSE;
3628     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3629       emulate_sb = FALSE;
3630     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3631       emulate_sp = FALSE;
3632
3633     InitField(x, y, TRUE);
3634
3635     ResetGfxAnimation(x, y);
3636   }
3637
3638   InitBeltMovement();
3639
3640   for (i = 0; i < MAX_PLAYERS; i++)
3641   {
3642     struct PlayerInfo *player = &stored_player[i];
3643
3644     /* set number of special actions for bored and sleeping animation */
3645     player->num_special_action_bored =
3646       get_num_special_action(player->artwork_element,
3647                              ACTION_BORING_1, ACTION_BORING_LAST);
3648     player->num_special_action_sleeping =
3649       get_num_special_action(player->artwork_element,
3650                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3651   }
3652
3653   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3654                     emulate_sb ? EMU_SOKOBAN :
3655                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3656
3657   /* initialize type of slippery elements */
3658   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3659   {
3660     if (!IS_CUSTOM_ELEMENT(i))
3661     {
3662       /* default: elements slip down either to the left or right randomly */
3663       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3664
3665       /* SP style elements prefer to slip down on the left side */
3666       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3667         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3668
3669       /* BD style elements prefer to slip down on the left side */
3670       if (game.emulation == EMU_BOULDERDASH)
3671         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3672     }
3673   }
3674
3675   /* initialize explosion and ignition delay */
3676   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3677   {
3678     if (!IS_CUSTOM_ELEMENT(i))
3679     {
3680       int num_phase = 8;
3681       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3682                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3683                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3684       int last_phase = (num_phase + 1) * delay;
3685       int half_phase = (num_phase / 2) * delay;
3686
3687       element_info[i].explosion_delay = last_phase - 1;
3688       element_info[i].ignition_delay = half_phase;
3689
3690       if (i == EL_BLACK_ORB)
3691         element_info[i].ignition_delay = 1;
3692     }
3693   }
3694
3695   /* correct non-moving belts to start moving left */
3696   for (i = 0; i < NUM_BELTS; i++)
3697     if (game.belt_dir[i] == MV_NONE)
3698       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3699
3700 #if USE_NEW_PLAYER_ASSIGNMENTS
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     stored_player[i].connected = FALSE;
3704
3705     /* in network game mode, the local player might not be the first player */
3706     if (stored_player[i].connected_locally)
3707       local_player = &stored_player[i];
3708   }
3709
3710   if (!network.enabled)
3711     local_player->connected = TRUE;
3712
3713   if (tape.playing)
3714   {
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716       stored_player[i].connected = tape.player_participates[i];
3717   }
3718   else if (network.enabled)
3719   {
3720     /* add team mode players connected over the network (needed for correct
3721        assignment of player figures from level to locally playing players) */
3722
3723     for (i = 0; i < MAX_PLAYERS; i++)
3724       if (stored_player[i].connected_network)
3725         stored_player[i].connected = TRUE;
3726   }
3727   else if (game.team_mode)
3728   {
3729     /* try to guess locally connected team mode players (needed for correct
3730        assignment of player figures from level to locally playing players) */
3731
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733       if (setup.input[i].use_joystick ||
3734           setup.input[i].key.left != KSYM_UNDEFINED)
3735         stored_player[i].connected = TRUE;
3736   }
3737
3738 #if DEBUG_INIT_PLAYER
3739   DebugPrintPlayerStatus("Player status after level initialization");
3740 #endif
3741
3742 #if DEBUG_INIT_PLAYER
3743   if (options.debug)
3744     printf("Reassigning players ...\n");
3745 #endif
3746
3747   /* check if any connected player was not found in playfield */
3748   for (i = 0; i < MAX_PLAYERS; i++)
3749   {
3750     struct PlayerInfo *player = &stored_player[i];
3751
3752     if (player->connected && !player->present)
3753     {
3754       struct PlayerInfo *field_player = NULL;
3755
3756 #if DEBUG_INIT_PLAYER
3757       if (options.debug)
3758         printf("- looking for field player for player %d ...\n", i + 1);
3759 #endif
3760
3761       /* assign first free player found that is present in the playfield */
3762
3763       /* first try: look for unmapped playfield player that is not connected */
3764       for (j = 0; j < MAX_PLAYERS; j++)
3765         if (field_player == NULL &&
3766             stored_player[j].present &&
3767             !stored_player[j].mapped &&
3768             !stored_player[j].connected)
3769           field_player = &stored_player[j];
3770
3771       /* second try: look for *any* unmapped playfield player */
3772       for (j = 0; j < MAX_PLAYERS; j++)
3773         if (field_player == NULL &&
3774             stored_player[j].present &&
3775             !stored_player[j].mapped)
3776           field_player = &stored_player[j];
3777
3778       if (field_player != NULL)
3779       {
3780         int jx = field_player->jx, jy = field_player->jy;
3781
3782 #if DEBUG_INIT_PLAYER
3783         if (options.debug)
3784           printf("- found player %d\n", field_player->index_nr + 1);
3785 #endif
3786
3787         player->present = FALSE;
3788         player->active = FALSE;
3789
3790         field_player->present = TRUE;
3791         field_player->active = TRUE;
3792
3793         /*
3794         player->initial_element = field_player->initial_element;
3795         player->artwork_element = field_player->artwork_element;
3796
3797         player->block_last_field       = field_player->block_last_field;
3798         player->block_delay_adjustment = field_player->block_delay_adjustment;
3799         */
3800
3801         StorePlayer[jx][jy] = field_player->element_nr;
3802
3803         field_player->jx = field_player->last_jx = jx;
3804         field_player->jy = field_player->last_jy = jy;
3805
3806         if (local_player == player)
3807           local_player = field_player;
3808
3809         map_player_action[field_player->index_nr] = i;
3810
3811         field_player->mapped = TRUE;
3812
3813 #if DEBUG_INIT_PLAYER
3814         if (options.debug)
3815           printf("- map_player_action[%d] == %d\n",
3816                  field_player->index_nr + 1, i + 1);
3817 #endif
3818       }
3819     }
3820
3821     if (player->connected && player->present)
3822       player->mapped = TRUE;
3823   }
3824
3825 #if DEBUG_INIT_PLAYER
3826   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3827 #endif
3828
3829 #else
3830
3831   /* check if any connected player was not found in playfield */
3832   for (i = 0; i < MAX_PLAYERS; i++)
3833   {
3834     struct PlayerInfo *player = &stored_player[i];
3835
3836     if (player->connected && !player->present)
3837     {
3838       for (j = 0; j < MAX_PLAYERS; j++)
3839       {
3840         struct PlayerInfo *field_player = &stored_player[j];
3841         int jx = field_player->jx, jy = field_player->jy;
3842
3843         /* assign first free player found that is present in the playfield */
3844         if (field_player->present && !field_player->connected)
3845         {
3846           player->present = TRUE;
3847           player->active = TRUE;
3848
3849           field_player->present = FALSE;
3850           field_player->active = FALSE;
3851
3852           player->initial_element = field_player->initial_element;
3853           player->artwork_element = field_player->artwork_element;
3854
3855           player->block_last_field       = field_player->block_last_field;
3856           player->block_delay_adjustment = field_player->block_delay_adjustment;
3857
3858           StorePlayer[jx][jy] = player->element_nr;
3859
3860           player->jx = player->last_jx = jx;
3861           player->jy = player->last_jy = jy;
3862
3863           break;
3864         }
3865       }
3866     }
3867   }
3868 #endif
3869
3870 #if 0
3871   printf("::: local_player->present == %d\n", local_player->present);
3872 #endif
3873
3874   /* set focus to local player for network games, else to all players */
3875   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3876   game.centered_player_nr_next = game.centered_player_nr;
3877   game.set_centered_player = FALSE;
3878
3879   if (network_playing && tape.recording)
3880   {
3881     /* store client dependent player focus when recording network games */
3882     tape.centered_player_nr_next = game.centered_player_nr_next;
3883     tape.set_centered_player = TRUE;
3884   }
3885
3886   if (tape.playing)
3887   {
3888     /* when playing a tape, eliminate all players who do not participate */
3889
3890 #if USE_NEW_PLAYER_ASSIGNMENTS
3891
3892     if (!game.team_mode)
3893     {
3894       for (i = 0; i < MAX_PLAYERS; i++)
3895       {
3896         if (stored_player[i].active &&
3897             !tape.player_participates[map_player_action[i]])
3898         {
3899           struct PlayerInfo *player = &stored_player[i];
3900           int jx = player->jx, jy = player->jy;
3901
3902 #if DEBUG_INIT_PLAYER
3903           if (options.debug)
3904             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3905 #endif
3906
3907           player->active = FALSE;
3908           StorePlayer[jx][jy] = 0;
3909           Feld[jx][jy] = EL_EMPTY;
3910         }
3911       }
3912     }
3913
3914 #else
3915
3916     for (i = 0; i < MAX_PLAYERS; i++)
3917     {
3918       if (stored_player[i].active &&
3919           !tape.player_participates[i])
3920       {
3921         struct PlayerInfo *player = &stored_player[i];
3922         int jx = player->jx, jy = player->jy;
3923
3924         player->active = FALSE;
3925         StorePlayer[jx][jy] = 0;
3926         Feld[jx][jy] = EL_EMPTY;
3927       }
3928     }
3929 #endif
3930   }
3931   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3932   {
3933     /* when in single player mode, eliminate all but the local player */
3934
3935     for (i = 0; i < MAX_PLAYERS; i++)
3936     {
3937       struct PlayerInfo *player = &stored_player[i];
3938
3939       if (player->active && player != local_player)
3940       {
3941         int jx = player->jx, jy = player->jy;
3942
3943         player->active = FALSE;
3944         player->present = FALSE;
3945
3946         StorePlayer[jx][jy] = 0;
3947         Feld[jx][jy] = EL_EMPTY;
3948       }
3949     }
3950   }
3951
3952   /* when recording the game, store which players take part in the game */
3953   if (tape.recording)
3954   {
3955 #if USE_NEW_PLAYER_ASSIGNMENTS
3956     for (i = 0; i < MAX_PLAYERS; i++)
3957       if (stored_player[i].connected)
3958         tape.player_participates[i] = TRUE;
3959 #else
3960     for (i = 0; i < MAX_PLAYERS; i++)
3961       if (stored_player[i].active)
3962         tape.player_participates[i] = TRUE;
3963 #endif
3964   }
3965
3966 #if DEBUG_INIT_PLAYER
3967   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3968 #endif
3969
3970   if (BorderElement == EL_EMPTY)
3971   {
3972     SBX_Left = 0;
3973     SBX_Right = lev_fieldx - SCR_FIELDX;
3974     SBY_Upper = 0;
3975     SBY_Lower = lev_fieldy - SCR_FIELDY;
3976   }
3977   else
3978   {
3979     SBX_Left = -1;
3980     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3981     SBY_Upper = -1;
3982     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3983   }
3984
3985   if (full_lev_fieldx <= SCR_FIELDX)
3986     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3987   if (full_lev_fieldy <= SCR_FIELDY)
3988     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3989
3990   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3991     SBX_Left--;
3992   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3993     SBY_Upper--;
3994
3995   /* if local player not found, look for custom element that might create
3996      the player (make some assumptions about the right custom element) */
3997   if (!local_player->present)
3998   {
3999     int start_x = 0, start_y = 0;
4000     int found_rating = 0;
4001     int found_element = EL_UNDEFINED;
4002     int player_nr = local_player->index_nr;
4003
4004     SCAN_PLAYFIELD(x, y)
4005     {
4006       int element = Feld[x][y];
4007       int content;
4008       int xx, yy;
4009       boolean is_player;
4010
4011       if (level.use_start_element[player_nr] &&
4012           level.start_element[player_nr] == element &&
4013           found_rating < 4)
4014       {
4015         start_x = x;
4016         start_y = y;
4017
4018         found_rating = 4;
4019         found_element = element;
4020       }
4021
4022       if (!IS_CUSTOM_ELEMENT(element))
4023         continue;
4024
4025       if (CAN_CHANGE(element))
4026       {
4027         for (i = 0; i < element_info[element].num_change_pages; i++)
4028         {
4029           /* check for player created from custom element as single target */
4030           content = element_info[element].change_page[i].target_element;
4031           is_player = ELEM_IS_PLAYER(content);
4032
4033           if (is_player && (found_rating < 3 ||
4034                             (found_rating == 3 && element < found_element)))
4035           {
4036             start_x = x;
4037             start_y = y;
4038
4039             found_rating = 3;
4040             found_element = element;
4041           }
4042         }
4043       }
4044
4045       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4046       {
4047         /* check for player created from custom element as explosion content */
4048         content = element_info[element].content.e[xx][yy];
4049         is_player = ELEM_IS_PLAYER(content);
4050
4051         if (is_player && (found_rating < 2 ||
4052                           (found_rating == 2 && element < found_element)))
4053         {
4054           start_x = x + xx - 1;
4055           start_y = y + yy - 1;
4056
4057           found_rating = 2;
4058           found_element = element;
4059         }
4060
4061         if (!CAN_CHANGE(element))
4062           continue;
4063
4064         for (i = 0; i < element_info[element].num_change_pages; i++)
4065         {
4066           /* check for player created from custom element as extended target */
4067           content =
4068             element_info[element].change_page[i].target_content.e[xx][yy];
4069
4070           is_player = ELEM_IS_PLAYER(content);
4071
4072           if (is_player && (found_rating < 1 ||
4073                             (found_rating == 1 && element < found_element)))
4074           {
4075             start_x = x + xx - 1;
4076             start_y = y + yy - 1;
4077
4078             found_rating = 1;
4079             found_element = element;
4080           }
4081         }
4082       }
4083     }
4084
4085     scroll_x = SCROLL_POSITION_X(start_x);
4086     scroll_y = SCROLL_POSITION_Y(start_y);
4087   }
4088   else
4089   {
4090     scroll_x = SCROLL_POSITION_X(local_player->jx);
4091     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4092   }
4093
4094   /* !!! FIX THIS (START) !!! */
4095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4096   {
4097     InitGameEngine_EM();
4098   }
4099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4100   {
4101     InitGameEngine_SP();
4102   }
4103   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4104   {
4105     InitGameEngine_MM();
4106   }
4107   else
4108   {
4109     DrawLevel(REDRAW_FIELD);
4110     DrawAllPlayers();
4111
4112     /* after drawing the level, correct some elements */
4113     if (game.timegate_time_left == 0)
4114       CloseAllOpenTimegates();
4115   }
4116
4117   /* blit playfield from scroll buffer to normal back buffer for fading in */
4118   BlitScreenToBitmap(backbuffer);
4119   /* !!! FIX THIS (END) !!! */
4120
4121   DrawMaskedBorder(fade_mask);
4122
4123   FadeIn(fade_mask);
4124
4125 #if 1
4126   // full screen redraw is required at this point in the following cases:
4127   // - special editor door undrawn when game was started from level editor
4128   // - drawing area (playfield) was changed and has to be removed completely
4129   redraw_mask = REDRAW_ALL;
4130   BackToFront();
4131 #endif
4132
4133   if (!game.restart_level)
4134   {
4135     /* copy default game door content to main double buffer */
4136
4137     /* !!! CHECK AGAIN !!! */
4138     SetPanelBackground();
4139     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4140     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4141   }
4142
4143   SetPanelBackground();
4144   SetDrawBackgroundMask(REDRAW_DOOR_1);
4145
4146   UpdateAndDisplayGameControlValues();
4147
4148   if (!game.restart_level)
4149   {
4150     UnmapGameButtons();
4151     UnmapTapeButtons();
4152
4153     FreeGameButtons();
4154     CreateGameButtons();
4155
4156     MapGameButtons();
4157     MapTapeButtons();
4158
4159     /* copy actual game door content to door double buffer for OpenDoor() */
4160     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4161
4162     OpenDoor(DOOR_OPEN_ALL);
4163
4164     KeyboardAutoRepeatOffUnlessAutoplay();
4165
4166 #if DEBUG_INIT_PLAYER
4167     DebugPrintPlayerStatus("Player status (final)");
4168 #endif
4169   }
4170
4171   UnmapAllGadgets();
4172
4173   MapGameButtons();
4174   MapTapeButtons();
4175
4176   if (!game.restart_level && !tape.playing)
4177   {
4178     LevelStats_incPlayed(level_nr);
4179
4180     SaveLevelSetup_SeriesInfo();
4181   }
4182
4183   game.restart_level = FALSE;
4184   game.restart_game_message = NULL;
4185
4186   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4187     InitGameActions_MM();
4188
4189   SaveEngineSnapshotToListInitial();
4190
4191   if (!game.restart_level)
4192   {
4193     PlaySound(SND_GAME_STARTING);
4194
4195     if (setup.sound_music)
4196       PlayLevelMusic();
4197   }
4198 }
4199
4200 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4201                         int actual_player_x, int actual_player_y)
4202 {
4203   /* this is used for non-R'n'D game engines to update certain engine values */
4204
4205   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4206   {
4207     actual_player_x = correctLevelPosX_EM(actual_player_x);
4208     actual_player_y = correctLevelPosY_EM(actual_player_y);
4209   }
4210
4211   /* needed to determine if sounds are played within the visible screen area */
4212   scroll_x = actual_scroll_x;
4213   scroll_y = actual_scroll_y;
4214
4215   /* needed to get player position for "follow finger" playing input method */
4216   local_player->jx = actual_player_x;
4217   local_player->jy = actual_player_y;
4218 }
4219
4220 void InitMovDir(int x, int y)
4221 {
4222   int i, element = Feld[x][y];
4223   static int xy[4][2] =
4224   {
4225     {  0, +1 },
4226     { +1,  0 },
4227     {  0, -1 },
4228     { -1,  0 }
4229   };
4230   static int direction[3][4] =
4231   {
4232     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4233     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4234     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4235   };
4236
4237   switch (element)
4238   {
4239     case EL_BUG_RIGHT:
4240     case EL_BUG_UP:
4241     case EL_BUG_LEFT:
4242     case EL_BUG_DOWN:
4243       Feld[x][y] = EL_BUG;
4244       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4245       break;
4246
4247     case EL_SPACESHIP_RIGHT:
4248     case EL_SPACESHIP_UP:
4249     case EL_SPACESHIP_LEFT:
4250     case EL_SPACESHIP_DOWN:
4251       Feld[x][y] = EL_SPACESHIP;
4252       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4253       break;
4254
4255     case EL_BD_BUTTERFLY_RIGHT:
4256     case EL_BD_BUTTERFLY_UP:
4257     case EL_BD_BUTTERFLY_LEFT:
4258     case EL_BD_BUTTERFLY_DOWN:
4259       Feld[x][y] = EL_BD_BUTTERFLY;
4260       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4261       break;
4262
4263     case EL_BD_FIREFLY_RIGHT:
4264     case EL_BD_FIREFLY_UP:
4265     case EL_BD_FIREFLY_LEFT:
4266     case EL_BD_FIREFLY_DOWN:
4267       Feld[x][y] = EL_BD_FIREFLY;
4268       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4269       break;
4270
4271     case EL_PACMAN_RIGHT:
4272     case EL_PACMAN_UP:
4273     case EL_PACMAN_LEFT:
4274     case EL_PACMAN_DOWN:
4275       Feld[x][y] = EL_PACMAN;
4276       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4277       break;
4278
4279     case EL_YAMYAM_LEFT:
4280     case EL_YAMYAM_RIGHT:
4281     case EL_YAMYAM_UP:
4282     case EL_YAMYAM_DOWN:
4283       Feld[x][y] = EL_YAMYAM;
4284       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4285       break;
4286
4287     case EL_SP_SNIKSNAK:
4288       MovDir[x][y] = MV_UP;
4289       break;
4290
4291     case EL_SP_ELECTRON:
4292       MovDir[x][y] = MV_LEFT;
4293       break;
4294
4295     case EL_MOLE_LEFT:
4296     case EL_MOLE_RIGHT:
4297     case EL_MOLE_UP:
4298     case EL_MOLE_DOWN:
4299       Feld[x][y] = EL_MOLE;
4300       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4301       break;
4302
4303     default:
4304       if (IS_CUSTOM_ELEMENT(element))
4305       {
4306         struct ElementInfo *ei = &element_info[element];
4307         int move_direction_initial = ei->move_direction_initial;
4308         int move_pattern = ei->move_pattern;
4309
4310         if (move_direction_initial == MV_START_PREVIOUS)
4311         {
4312           if (MovDir[x][y] != MV_NONE)
4313             return;
4314
4315           move_direction_initial = MV_START_AUTOMATIC;
4316         }
4317
4318         if (move_direction_initial == MV_START_RANDOM)
4319           MovDir[x][y] = 1 << RND(4);
4320         else if (move_direction_initial & MV_ANY_DIRECTION)
4321           MovDir[x][y] = move_direction_initial;
4322         else if (move_pattern == MV_ALL_DIRECTIONS ||
4323                  move_pattern == MV_TURNING_LEFT ||
4324                  move_pattern == MV_TURNING_RIGHT ||
4325                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4326                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4327                  move_pattern == MV_TURNING_RANDOM)
4328           MovDir[x][y] = 1 << RND(4);
4329         else if (move_pattern == MV_HORIZONTAL)
4330           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4331         else if (move_pattern == MV_VERTICAL)
4332           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4333         else if (move_pattern & MV_ANY_DIRECTION)
4334           MovDir[x][y] = element_info[element].move_pattern;
4335         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4336                  move_pattern == MV_ALONG_RIGHT_SIDE)
4337         {
4338           /* use random direction as default start direction */
4339           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4340             MovDir[x][y] = 1 << RND(4);
4341
4342           for (i = 0; i < NUM_DIRECTIONS; i++)
4343           {
4344             int x1 = x + xy[i][0];
4345             int y1 = y + xy[i][1];
4346
4347             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4348             {
4349               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4350                 MovDir[x][y] = direction[0][i];
4351               else
4352                 MovDir[x][y] = direction[1][i];
4353
4354               break;
4355             }
4356           }
4357         }                
4358       }
4359       else
4360       {
4361         MovDir[x][y] = 1 << RND(4);
4362
4363         if (element != EL_BUG &&
4364             element != EL_SPACESHIP &&
4365             element != EL_BD_BUTTERFLY &&
4366             element != EL_BD_FIREFLY)
4367           break;
4368
4369         for (i = 0; i < NUM_DIRECTIONS; i++)
4370         {
4371           int x1 = x + xy[i][0];
4372           int y1 = y + xy[i][1];
4373
4374           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4375           {
4376             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4377             {
4378               MovDir[x][y] = direction[0][i];
4379               break;
4380             }
4381             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4382                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4383             {
4384               MovDir[x][y] = direction[1][i];
4385               break;
4386             }
4387           }
4388         }
4389       }
4390       break;
4391   }
4392
4393   GfxDir[x][y] = MovDir[x][y];
4394 }
4395
4396 void InitAmoebaNr(int x, int y)
4397 {
4398   int i;
4399   int group_nr = AmoebeNachbarNr(x, y);
4400
4401   if (group_nr == 0)
4402   {
4403     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4404     {
4405       if (AmoebaCnt[i] == 0)
4406       {
4407         group_nr = i;
4408         break;
4409       }
4410     }
4411   }
4412
4413   AmoebaNr[x][y] = group_nr;
4414   AmoebaCnt[group_nr]++;
4415   AmoebaCnt2[group_nr]++;
4416 }
4417
4418 static void PlayerWins(struct PlayerInfo *player)
4419 {
4420   player->LevelSolved = TRUE;
4421   player->GameOver = TRUE;
4422
4423   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4424                          level.native_em_level->lev->score :
4425                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4426                          game_mm.score :
4427                          player->score);
4428   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4429                           MM_HEALTH(game_mm.laser_overload_value) :
4430                           player->health);
4431
4432   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4433                                       TimeLeft);
4434   player->LevelSolved_CountingScore = player->score_final;
4435   player->LevelSolved_CountingHealth = player->health_final;
4436 }
4437
4438 void GameWon()
4439 {
4440   static int time_count_steps;
4441   static int time, time_final;
4442   static int score, score_final;
4443   static int health, health_final;
4444   static int game_over_delay_1 = 0;
4445   static int game_over_delay_2 = 0;
4446   static int game_over_delay_3 = 0;
4447   int game_over_delay_value_1 = 50;
4448   int game_over_delay_value_2 = 25;
4449   int game_over_delay_value_3 = 50;
4450
4451   if (!local_player->LevelSolved_GameWon)
4452   {
4453     int i;
4454
4455     /* do not start end game actions before the player stops moving (to exit) */
4456     if (local_player->MovPos)
4457       return;
4458
4459     local_player->LevelSolved_GameWon = TRUE;
4460     local_player->LevelSolved_SaveTape = tape.recording;
4461     local_player->LevelSolved_SaveScore = !tape.playing;
4462
4463     if (!tape.playing)
4464     {
4465       LevelStats_incSolved(level_nr);
4466
4467       SaveLevelSetup_SeriesInfo();
4468     }
4469
4470     if (tape.auto_play)         /* tape might already be stopped here */
4471       tape.auto_play_level_solved = TRUE;
4472
4473     TapeStop();
4474
4475     game_over_delay_1 = 0;
4476     game_over_delay_2 = 0;
4477     game_over_delay_3 = game_over_delay_value_3;
4478
4479     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4480     score = score_final = local_player->score_final;
4481     health = health_final = local_player->health_final;
4482
4483     if (level.score[SC_TIME_BONUS] > 0)
4484     {
4485       if (TimeLeft > 0)
4486       {
4487         time_final = 0;
4488         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4489       }
4490       else if (game.no_time_limit && TimePlayed < 999)
4491       {
4492         time_final = 999;
4493         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4494       }
4495
4496       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4497
4498       game_over_delay_1 = game_over_delay_value_1;
4499
4500       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4501       {
4502         health_final = 0;
4503         score_final += health * level.score[SC_TIME_BONUS];
4504
4505         game_over_delay_2 = game_over_delay_value_2;
4506       }
4507
4508       local_player->score_final = score_final;
4509       local_player->health_final = health_final;
4510     }
4511
4512     if (level_editor_test_game)
4513     {
4514       time = time_final;
4515       score = score_final;
4516
4517       local_player->LevelSolved_CountingTime = time;
4518       local_player->LevelSolved_CountingScore = score;
4519
4520       game_panel_controls[GAME_PANEL_TIME].value = time;
4521       game_panel_controls[GAME_PANEL_SCORE].value = score;
4522
4523       DisplayGameControlValues();
4524     }
4525
4526     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4527     {
4528       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4529       {
4530         /* close exit door after last player */
4531         if ((AllPlayersGone &&
4532              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4533               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4534               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4535             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4536             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4537         {
4538           int element = Feld[ExitX][ExitY];
4539
4540           Feld[ExitX][ExitY] =
4541             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4542              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4543              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4544              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4545              EL_EM_STEEL_EXIT_CLOSING);
4546
4547           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4548         }
4549
4550         /* player disappears */
4551         DrawLevelField(ExitX, ExitY);
4552       }
4553
4554       for (i = 0; i < MAX_PLAYERS; i++)
4555       {
4556         struct PlayerInfo *player = &stored_player[i];
4557
4558         if (player->present)
4559         {
4560           RemovePlayer(player);
4561
4562           /* player disappears */
4563           DrawLevelField(player->jx, player->jy);
4564         }
4565       }
4566     }
4567
4568     PlaySound(SND_GAME_WINNING);
4569   }
4570
4571   if (game_over_delay_1 > 0)
4572   {
4573     game_over_delay_1--;
4574
4575     return;
4576   }
4577
4578   if (time != time_final)
4579   {
4580     int time_to_go = ABS(time_final - time);
4581     int time_count_dir = (time < time_final ? +1 : -1);
4582
4583     if (time_to_go < time_count_steps)
4584       time_count_steps = 1;
4585
4586     time  += time_count_steps * time_count_dir;
4587     score += time_count_steps * level.score[SC_TIME_BONUS];
4588
4589     local_player->LevelSolved_CountingTime = time;
4590     local_player->LevelSolved_CountingScore = score;
4591
4592     game_panel_controls[GAME_PANEL_TIME].value = time;
4593     game_panel_controls[GAME_PANEL_SCORE].value = score;
4594
4595     DisplayGameControlValues();
4596
4597     if (time == time_final)
4598       StopSound(SND_GAME_LEVELTIME_BONUS);
4599     else if (setup.sound_loops)
4600       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4601     else
4602       PlaySound(SND_GAME_LEVELTIME_BONUS);
4603
4604     return;
4605   }
4606
4607   if (game_over_delay_2 > 0)
4608   {
4609     game_over_delay_2--;
4610
4611     return;
4612   }
4613
4614   if (health != health_final)
4615   {
4616     int health_count_dir = (health < health_final ? +1 : -1);
4617
4618     health += health_count_dir;
4619     score  += level.score[SC_TIME_BONUS];
4620
4621     local_player->LevelSolved_CountingHealth = health;
4622     local_player->LevelSolved_CountingScore = score;
4623
4624     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4625     game_panel_controls[GAME_PANEL_SCORE].value = score;
4626
4627     DisplayGameControlValues();
4628
4629     if (health == health_final)
4630       StopSound(SND_GAME_LEVELTIME_BONUS);
4631     else if (setup.sound_loops)
4632       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4633     else
4634       PlaySound(SND_GAME_LEVELTIME_BONUS);
4635
4636     return;
4637   }
4638
4639   local_player->LevelSolved_PanelOff = TRUE;
4640
4641   if (game_over_delay_3 > 0)
4642   {
4643     game_over_delay_3--;
4644
4645     return;
4646   }
4647
4648   GameEnd();
4649 }
4650
4651 void GameEnd()
4652 {
4653   int hi_pos;
4654   int last_level_nr = level_nr;
4655
4656   local_player->LevelSolved_GameEnd = TRUE;
4657
4658   if (local_player->LevelSolved_SaveTape)
4659   {
4660     /* make sure that request dialog to save tape does not open door again */
4661     if (!global.use_envelope_request)
4662       CloseDoor(DOOR_CLOSE_1);
4663
4664     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4665   }
4666
4667   /* if no tape is to be saved, close both doors simultaneously */
4668   CloseDoor(DOOR_CLOSE_ALL);
4669
4670   if (level_editor_test_game)
4671   {
4672     SetGameStatus(GAME_MODE_MAIN);
4673
4674     DrawMainMenu();
4675
4676     return;
4677   }
4678
4679   if (!local_player->LevelSolved_SaveScore)
4680   {
4681     SetGameStatus(GAME_MODE_MAIN);
4682
4683     DrawMainMenu();
4684
4685     return;
4686   }
4687
4688   if (level_nr == leveldir_current->handicap_level)
4689   {
4690     leveldir_current->handicap_level++;
4691
4692     SaveLevelSetup_SeriesInfo();
4693   }
4694
4695   if (setup.increment_levels &&
4696       level_nr < leveldir_current->last_level)
4697   {
4698     level_nr++;         /* advance to next level */
4699     TapeErase();        /* start with empty tape */
4700
4701     if (setup.auto_play_next_level)
4702     {
4703       LoadLevel(level_nr);
4704
4705       SaveLevelSetup_SeriesInfo();
4706     }
4707   }
4708
4709   hi_pos = NewHiScore(last_level_nr);
4710
4711   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4712   {
4713     SetGameStatus(GAME_MODE_SCORES);
4714
4715     DrawHallOfFame(last_level_nr, hi_pos);
4716   }
4717   else if (!setup.auto_play_next_level || !setup.increment_levels)
4718   {
4719     SetGameStatus(GAME_MODE_MAIN);
4720
4721     DrawMainMenu();
4722   }
4723   else
4724   {
4725     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4726   }
4727 }
4728
4729 int NewHiScore(int level_nr)
4730 {
4731   int k, l;
4732   int position = -1;
4733   boolean one_score_entry_per_name = !program.many_scores_per_name;
4734
4735   LoadScore(level_nr);
4736
4737   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4738       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4739     return -1;
4740
4741   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4742   {
4743     if (local_player->score_final > highscore[k].Score)
4744     {
4745       /* player has made it to the hall of fame */
4746
4747       if (k < MAX_SCORE_ENTRIES - 1)
4748       {
4749         int m = MAX_SCORE_ENTRIES - 1;
4750
4751         if (one_score_entry_per_name)
4752         {
4753           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4754             if (strEqual(setup.player_name, highscore[l].Name))
4755               m = l;
4756
4757           if (m == k)   /* player's new highscore overwrites his old one */
4758             goto put_into_list;
4759         }
4760
4761         for (l = m; l > k; l--)
4762         {
4763           strcpy(highscore[l].Name, highscore[l - 1].Name);
4764           highscore[l].Score = highscore[l - 1].Score;
4765         }
4766       }
4767
4768       put_into_list:
4769
4770       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4771       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4772       highscore[k].Score = local_player->score_final; 
4773       position = k;
4774
4775       break;
4776     }
4777     else if (one_score_entry_per_name &&
4778              !strncmp(setup.player_name, highscore[k].Name,
4779                       MAX_PLAYER_NAME_LEN))
4780       break;    /* player already there with a higher score */
4781   }
4782
4783   if (position >= 0) 
4784     SaveScore(level_nr);
4785
4786   return position;
4787 }
4788
4789 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4790 {
4791   int element = Feld[x][y];
4792   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4793   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4794   int horiz_move = (dx != 0);
4795   int sign = (horiz_move ? dx : dy);
4796   int step = sign * element_info[element].move_stepsize;
4797
4798   /* special values for move stepsize for spring and things on conveyor belt */
4799   if (horiz_move)
4800   {
4801     if (CAN_FALL(element) &&
4802         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4803       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4804     else if (element == EL_SPRING)
4805       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4806   }
4807
4808   return step;
4809 }
4810
4811 inline static int getElementMoveStepsize(int x, int y)
4812 {
4813   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4814 }
4815
4816 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4817 {
4818   if (player->GfxAction != action || player->GfxDir != dir)
4819   {
4820     player->GfxAction = action;
4821     player->GfxDir = dir;
4822     player->Frame = 0;
4823     player->StepFrame = 0;
4824   }
4825 }
4826
4827 static void ResetGfxFrame(int x, int y)
4828 {
4829   // profiling showed that "autotest" spends 10~20% of its time in this function
4830   if (DrawingDeactivatedField())
4831     return;
4832
4833   int element = Feld[x][y];
4834   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4835
4836   if (graphic_info[graphic].anim_global_sync)
4837     GfxFrame[x][y] = FrameCounter;
4838   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4839     GfxFrame[x][y] = CustomValue[x][y];
4840   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4841     GfxFrame[x][y] = element_info[element].collect_score;
4842   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4843     GfxFrame[x][y] = ChangeDelay[x][y];
4844 }
4845
4846 static void ResetGfxAnimation(int x, int y)
4847 {
4848   GfxAction[x][y] = ACTION_DEFAULT;
4849   GfxDir[x][y] = MovDir[x][y];
4850   GfxFrame[x][y] = 0;
4851
4852   ResetGfxFrame(x, y);
4853 }
4854
4855 static void ResetRandomAnimationValue(int x, int y)
4856 {
4857   GfxRandom[x][y] = INIT_GFX_RANDOM();
4858 }
4859
4860 void InitMovingField(int x, int y, int direction)
4861 {
4862   int element = Feld[x][y];
4863   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4864   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4865   int newx = x + dx;
4866   int newy = y + dy;
4867   boolean is_moving_before, is_moving_after;
4868
4869   /* check if element was/is moving or being moved before/after mode change */
4870   is_moving_before = (WasJustMoving[x][y] != 0);
4871   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4872
4873   /* reset animation only for moving elements which change direction of moving
4874      or which just started or stopped moving
4875      (else CEs with property "can move" / "not moving" are reset each frame) */
4876   if (is_moving_before != is_moving_after ||
4877       direction != MovDir[x][y])
4878     ResetGfxAnimation(x, y);
4879
4880   MovDir[x][y] = direction;
4881   GfxDir[x][y] = direction;
4882
4883   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4884                      direction == MV_DOWN && CAN_FALL(element) ?
4885                      ACTION_FALLING : ACTION_MOVING);
4886
4887   /* this is needed for CEs with property "can move" / "not moving" */
4888
4889   if (is_moving_after)
4890   {
4891     if (Feld[newx][newy] == EL_EMPTY)
4892       Feld[newx][newy] = EL_BLOCKED;
4893
4894     MovDir[newx][newy] = MovDir[x][y];
4895
4896     CustomValue[newx][newy] = CustomValue[x][y];
4897
4898     GfxFrame[newx][newy] = GfxFrame[x][y];
4899     GfxRandom[newx][newy] = GfxRandom[x][y];
4900     GfxAction[newx][newy] = GfxAction[x][y];
4901     GfxDir[newx][newy] = GfxDir[x][y];
4902   }
4903 }
4904
4905 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4906 {
4907   int direction = MovDir[x][y];
4908   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4909   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4910
4911   *goes_to_x = newx;
4912   *goes_to_y = newy;
4913 }
4914
4915 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4916 {
4917   int oldx = x, oldy = y;
4918   int direction = MovDir[x][y];
4919
4920   if (direction == MV_LEFT)
4921     oldx++;
4922   else if (direction == MV_RIGHT)
4923     oldx--;
4924   else if (direction == MV_UP)
4925     oldy++;
4926   else if (direction == MV_DOWN)
4927     oldy--;
4928
4929   *comes_from_x = oldx;
4930   *comes_from_y = oldy;
4931 }
4932
4933 int MovingOrBlocked2Element(int x, int y)
4934 {
4935   int element = Feld[x][y];
4936
4937   if (element == EL_BLOCKED)
4938   {
4939     int oldx, oldy;
4940
4941     Blocked2Moving(x, y, &oldx, &oldy);
4942     return Feld[oldx][oldy];
4943   }
4944   else
4945     return element;
4946 }
4947
4948 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4949 {
4950   /* like MovingOrBlocked2Element(), but if element is moving
4951      and (x,y) is the field the moving element is just leaving,
4952      return EL_BLOCKED instead of the element value */
4953   int element = Feld[x][y];
4954
4955   if (IS_MOVING(x, y))
4956   {
4957     if (element == EL_BLOCKED)
4958     {
4959       int oldx, oldy;
4960
4961       Blocked2Moving(x, y, &oldx, &oldy);
4962       return Feld[oldx][oldy];
4963     }
4964     else
4965       return EL_BLOCKED;
4966   }
4967   else
4968     return element;
4969 }
4970
4971 static void RemoveField(int x, int y)
4972 {
4973   Feld[x][y] = EL_EMPTY;
4974
4975   MovPos[x][y] = 0;
4976   MovDir[x][y] = 0;
4977   MovDelay[x][y] = 0;
4978
4979   CustomValue[x][y] = 0;
4980
4981   AmoebaNr[x][y] = 0;
4982   ChangeDelay[x][y] = 0;
4983   ChangePage[x][y] = -1;
4984   Pushed[x][y] = FALSE;
4985
4986   GfxElement[x][y] = EL_UNDEFINED;
4987   GfxAction[x][y] = ACTION_DEFAULT;
4988   GfxDir[x][y] = MV_NONE;
4989 }
4990
4991 void RemoveMovingField(int x, int y)
4992 {
4993   int oldx = x, oldy = y, newx = x, newy = y;
4994   int element = Feld[x][y];
4995   int next_element = EL_UNDEFINED;
4996
4997   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4998     return;
4999
5000   if (IS_MOVING(x, y))
5001   {
5002     Moving2Blocked(x, y, &newx, &newy);
5003
5004     if (Feld[newx][newy] != EL_BLOCKED)
5005     {
5006       /* element is moving, but target field is not free (blocked), but
5007          already occupied by something different (example: acid pool);
5008          in this case, only remove the moving field, but not the target */
5009
5010       RemoveField(oldx, oldy);
5011
5012       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5013
5014       TEST_DrawLevelField(oldx, oldy);
5015
5016       return;
5017     }
5018   }
5019   else if (element == EL_BLOCKED)
5020   {
5021     Blocked2Moving(x, y, &oldx, &oldy);
5022     if (!IS_MOVING(oldx, oldy))
5023       return;
5024   }
5025
5026   if (element == EL_BLOCKED &&
5027       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5028        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5029        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5030        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5031        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5032        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5033     next_element = get_next_element(Feld[oldx][oldy]);
5034
5035   RemoveField(oldx, oldy);
5036   RemoveField(newx, newy);
5037
5038   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5039
5040   if (next_element != EL_UNDEFINED)
5041     Feld[oldx][oldy] = next_element;
5042
5043   TEST_DrawLevelField(oldx, oldy);
5044   TEST_DrawLevelField(newx, newy);
5045 }
5046
5047 void DrawDynamite(int x, int y)
5048 {
5049   int sx = SCREENX(x), sy = SCREENY(y);
5050   int graphic = el2img(Feld[x][y]);
5051   int frame;
5052
5053   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5054     return;
5055
5056   if (IS_WALKABLE_INSIDE(Back[x][y]))
5057     return;
5058
5059   if (Back[x][y])
5060     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5061   else if (Store[x][y])
5062     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5063
5064   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5065
5066   if (Back[x][y] || Store[x][y])
5067     DrawGraphicThruMask(sx, sy, graphic, frame);
5068   else
5069     DrawGraphic(sx, sy, graphic, frame);
5070 }
5071
5072 void CheckDynamite(int x, int y)
5073 {
5074   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5075   {
5076     MovDelay[x][y]--;
5077
5078     if (MovDelay[x][y] != 0)
5079     {
5080       DrawDynamite(x, y);
5081       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5082
5083       return;
5084     }
5085   }
5086
5087   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5088
5089   Bang(x, y);
5090 }
5091
5092 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5093 {
5094   boolean num_checked_players = 0;
5095   int i;
5096
5097   for (i = 0; i < MAX_PLAYERS; i++)
5098   {
5099     if (stored_player[i].active)
5100     {
5101       int sx = stored_player[i].jx;
5102       int sy = stored_player[i].jy;
5103
5104       if (num_checked_players == 0)
5105       {
5106         *sx1 = *sx2 = sx;
5107         *sy1 = *sy2 = sy;
5108       }
5109       else
5110       {
5111         *sx1 = MIN(*sx1, sx);
5112         *sy1 = MIN(*sy1, sy);
5113         *sx2 = MAX(*sx2, sx);
5114         *sy2 = MAX(*sy2, sy);
5115       }
5116
5117       num_checked_players++;
5118     }
5119   }
5120 }
5121
5122 static boolean checkIfAllPlayersFitToScreen_RND()
5123 {
5124   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5125
5126   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5127
5128   return (sx2 - sx1 < SCR_FIELDX &&
5129           sy2 - sy1 < SCR_FIELDY);
5130 }
5131
5132 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5133 {
5134   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5135
5136   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5137
5138   *sx = (sx1 + sx2) / 2;
5139   *sy = (sy1 + sy2) / 2;
5140 }
5141
5142 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5143                         boolean center_screen, boolean quick_relocation)
5144 {
5145   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5146   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5147   boolean no_delay = (tape.warp_forward);
5148   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5149   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5150   int new_scroll_x, new_scroll_y;
5151
5152   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5153   {
5154     /* case 1: quick relocation inside visible screen (without scrolling) */
5155
5156     RedrawPlayfield();
5157
5158     return;
5159   }
5160
5161   if (!level.shifted_relocation || center_screen)
5162   {
5163     /* relocation _with_ centering of screen */
5164
5165     new_scroll_x = SCROLL_POSITION_X(x);
5166     new_scroll_y = SCROLL_POSITION_Y(y);
5167   }
5168   else
5169   {
5170     /* relocation _without_ centering of screen */
5171
5172     int center_scroll_x = SCROLL_POSITION_X(old_x);
5173     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5174     int offset_x = x + (scroll_x - center_scroll_x);
5175     int offset_y = y + (scroll_y - center_scroll_y);
5176
5177     /* for new screen position, apply previous offset to center position */
5178     new_scroll_x = SCROLL_POSITION_X(offset_x);
5179     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5180   }
5181
5182   if (quick_relocation)
5183   {
5184     /* case 2: quick relocation (redraw without visible scrolling) */
5185
5186     scroll_x = new_scroll_x;
5187     scroll_y = new_scroll_y;
5188
5189     RedrawPlayfield();
5190
5191     return;
5192   }
5193
5194   /* case 3: visible relocation (with scrolling to new position) */
5195
5196   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5197
5198   SetVideoFrameDelay(wait_delay_value);
5199
5200   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5201   {
5202     int dx = 0, dy = 0;
5203     int fx = FX, fy = FY;
5204
5205     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5206     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5207
5208     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5209       break;
5210
5211     scroll_x -= dx;
5212     scroll_y -= dy;
5213
5214     fx += dx * TILEX / 2;
5215     fy += dy * TILEY / 2;
5216
5217     ScrollLevel(dx, dy);
5218     DrawAllPlayers();
5219
5220     /* scroll in two steps of half tile size to make things smoother */
5221     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5222
5223     /* scroll second step to align at full tile size */
5224     BlitScreenToBitmap(window);
5225   }
5226
5227   DrawAllPlayers();
5228   BackToFront();
5229
5230   SetVideoFrameDelay(frame_delay_value_old);
5231 }
5232
5233 void RelocatePlayer(int jx, int jy, int el_player_raw)
5234 {
5235   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5236   int player_nr = GET_PLAYER_NR(el_player);
5237   struct PlayerInfo *player = &stored_player[player_nr];
5238   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5239   boolean no_delay = (tape.warp_forward);
5240   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5241   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5242   int old_jx = player->jx;
5243   int old_jy = player->jy;
5244   int old_element = Feld[old_jx][old_jy];
5245   int element = Feld[jx][jy];
5246   boolean player_relocated = (old_jx != jx || old_jy != jy);
5247
5248   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5249   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5250   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5251   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5252   int leave_side_horiz = move_dir_horiz;
5253   int leave_side_vert  = move_dir_vert;
5254   int enter_side = enter_side_horiz | enter_side_vert;
5255   int leave_side = leave_side_horiz | leave_side_vert;
5256
5257   if (player->GameOver)         /* do not reanimate dead player */
5258     return;
5259
5260   if (!player_relocated)        /* no need to relocate the player */
5261     return;
5262
5263   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5264   {
5265     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5266     DrawLevelField(jx, jy);
5267   }
5268
5269   if (player->present)
5270   {
5271     while (player->MovPos)
5272     {
5273       ScrollPlayer(player, SCROLL_GO_ON);
5274       ScrollScreen(NULL, SCROLL_GO_ON);
5275
5276       AdvanceFrameAndPlayerCounters(player->index_nr);
5277
5278       DrawPlayer(player);
5279
5280       BackToFront_WithFrameDelay(wait_delay_value);
5281     }
5282
5283     DrawPlayer(player);         /* needed here only to cleanup last field */
5284     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5285
5286     player->is_moving = FALSE;
5287   }
5288
5289   if (IS_CUSTOM_ELEMENT(old_element))
5290     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5291                                CE_LEFT_BY_PLAYER,
5292                                player->index_bit, leave_side);
5293
5294   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5295                                       CE_PLAYER_LEAVES_X,
5296                                       player->index_bit, leave_side);
5297
5298   Feld[jx][jy] = el_player;
5299   InitPlayerField(jx, jy, el_player, TRUE);
5300
5301   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5302      possible that the relocation target field did not contain a player element,
5303      but a walkable element, to which the new player was relocated -- in this
5304      case, restore that (already initialized!) element on the player field */
5305   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5306   {
5307     Feld[jx][jy] = element;     /* restore previously existing element */
5308   }
5309
5310   /* only visually relocate centered player */
5311   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5312                      FALSE, level.instant_relocation);
5313
5314   TestIfPlayerTouchesBadThing(jx, jy);
5315   TestIfPlayerTouchesCustomElement(jx, jy);
5316
5317   if (IS_CUSTOM_ELEMENT(element))
5318     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5319                                player->index_bit, enter_side);
5320
5321   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5322                                       player->index_bit, enter_side);
5323
5324   if (player->is_switching)
5325   {
5326     /* ensure that relocation while still switching an element does not cause
5327        a new element to be treated as also switched directly after relocation
5328        (this is important for teleporter switches that teleport the player to
5329        a place where another teleporter switch is in the same direction, which
5330        would then incorrectly be treated as immediately switched before the
5331        direction key that caused the switch was released) */
5332
5333     player->switch_x += jx - old_jx;
5334     player->switch_y += jy - old_jy;
5335   }
5336 }
5337
5338 void Explode(int ex, int ey, int phase, int mode)
5339 {
5340   int x, y;
5341   int last_phase;
5342   int border_element;
5343
5344   /* !!! eliminate this variable !!! */
5345   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5346
5347   if (game.explosions_delayed)
5348   {
5349     ExplodeField[ex][ey] = mode;
5350     return;
5351   }
5352
5353   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5354   {
5355     int center_element = Feld[ex][ey];
5356     int artwork_element, explosion_element;     /* set these values later */
5357
5358     /* remove things displayed in background while burning dynamite */
5359     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5360       Back[ex][ey] = 0;
5361
5362     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5363     {
5364       /* put moving element to center field (and let it explode there) */
5365       center_element = MovingOrBlocked2Element(ex, ey);
5366       RemoveMovingField(ex, ey);
5367       Feld[ex][ey] = center_element;
5368     }
5369
5370     /* now "center_element" is finally determined -- set related values now */
5371     artwork_element = center_element;           /* for custom player artwork */
5372     explosion_element = center_element;         /* for custom player artwork */
5373
5374     if (IS_PLAYER(ex, ey))
5375     {
5376       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5377
5378       artwork_element = stored_player[player_nr].artwork_element;
5379
5380       if (level.use_explosion_element[player_nr])
5381       {
5382         explosion_element = level.explosion_element[player_nr];
5383         artwork_element = explosion_element;
5384       }
5385     }
5386
5387     if (mode == EX_TYPE_NORMAL ||
5388         mode == EX_TYPE_CENTER ||
5389         mode == EX_TYPE_CROSS)
5390       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5391
5392     last_phase = element_info[explosion_element].explosion_delay + 1;
5393
5394     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5395     {
5396       int xx = x - ex + 1;
5397       int yy = y - ey + 1;
5398       int element;
5399
5400       if (!IN_LEV_FIELD(x, y) ||
5401           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5402           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5403         continue;
5404
5405       element = Feld[x][y];
5406
5407       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5408       {
5409         element = MovingOrBlocked2Element(x, y);
5410
5411         if (!IS_EXPLOSION_PROOF(element))
5412           RemoveMovingField(x, y);
5413       }
5414
5415       /* indestructible elements can only explode in center (but not flames) */
5416       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5417                                            mode == EX_TYPE_BORDER)) ||
5418           element == EL_FLAMES)
5419         continue;
5420
5421       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5422          behaviour, for example when touching a yamyam that explodes to rocks
5423          with active deadly shield, a rock is created under the player !!! */
5424       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5425 #if 0
5426       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5427           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5428            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5429 #else
5430       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5431 #endif
5432       {
5433         if (IS_ACTIVE_BOMB(element))
5434         {
5435           /* re-activate things under the bomb like gate or penguin */
5436           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5437           Back[x][y] = 0;
5438         }
5439
5440         continue;
5441       }
5442
5443       /* save walkable background elements while explosion on same tile */
5444       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5445           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5446         Back[x][y] = element;
5447
5448       /* ignite explodable elements reached by other explosion */
5449       if (element == EL_EXPLOSION)
5450         element = Store2[x][y];
5451
5452       if (AmoebaNr[x][y] &&
5453           (element == EL_AMOEBA_FULL ||
5454            element == EL_BD_AMOEBA ||
5455            element == EL_AMOEBA_GROWING))
5456       {
5457         AmoebaCnt[AmoebaNr[x][y]]--;
5458         AmoebaCnt2[AmoebaNr[x][y]]--;
5459       }
5460
5461       RemoveField(x, y);
5462
5463       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5464       {
5465         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5466
5467         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5468
5469         if (PLAYERINFO(ex, ey)->use_murphy)
5470           Store[x][y] = EL_EMPTY;
5471       }
5472
5473       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5474          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5475       else if (ELEM_IS_PLAYER(center_element))
5476         Store[x][y] = EL_EMPTY;
5477       else if (center_element == EL_YAMYAM)
5478         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5479       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5480         Store[x][y] = element_info[center_element].content.e[xx][yy];
5481 #if 1
5482       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5483          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5484          otherwise) -- FIX THIS !!! */
5485       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5486         Store[x][y] = element_info[element].content.e[1][1];
5487 #else
5488       else if (!CAN_EXPLODE(element))
5489         Store[x][y] = element_info[element].content.e[1][1];
5490 #endif
5491       else
5492         Store[x][y] = EL_EMPTY;
5493
5494       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5495           center_element == EL_AMOEBA_TO_DIAMOND)
5496         Store2[x][y] = element;
5497
5498       Feld[x][y] = EL_EXPLOSION;
5499       GfxElement[x][y] = artwork_element;
5500
5501       ExplodePhase[x][y] = 1;
5502       ExplodeDelay[x][y] = last_phase;
5503
5504       Stop[x][y] = TRUE;
5505     }
5506
5507     if (center_element == EL_YAMYAM)
5508       game.yamyam_content_nr =
5509         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5510
5511     return;
5512   }
5513
5514   if (Stop[ex][ey])
5515     return;
5516
5517   x = ex;
5518   y = ey;
5519
5520   if (phase == 1)
5521     GfxFrame[x][y] = 0;         /* restart explosion animation */
5522
5523   last_phase = ExplodeDelay[x][y];
5524
5525   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5526
5527   /* this can happen if the player leaves an explosion just in time */
5528   if (GfxElement[x][y] == EL_UNDEFINED)
5529     GfxElement[x][y] = EL_EMPTY;
5530
5531   border_element = Store2[x][y];
5532   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5533     border_element = StorePlayer[x][y];
5534
5535   if (phase == element_info[border_element].ignition_delay ||
5536       phase == last_phase)
5537   {
5538     boolean border_explosion = FALSE;
5539
5540     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5541         !PLAYER_EXPLOSION_PROTECTED(x, y))
5542     {
5543       KillPlayerUnlessExplosionProtected(x, y);
5544       border_explosion = TRUE;
5545     }
5546     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5547     {
5548       Feld[x][y] = Store2[x][y];
5549       Store2[x][y] = 0;
5550       Bang(x, y);
5551       border_explosion = TRUE;
5552     }
5553     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5554     {
5555       AmoebeUmwandeln(x, y);
5556       Store2[x][y] = 0;
5557       border_explosion = TRUE;
5558     }
5559
5560     /* if an element just explodes due to another explosion (chain-reaction),
5561        do not immediately end the new explosion when it was the last frame of
5562        the explosion (as it would be done in the following "if"-statement!) */
5563     if (border_explosion && phase == last_phase)
5564       return;
5565   }
5566
5567   if (phase == last_phase)
5568   {
5569     int element;
5570
5571     element = Feld[x][y] = Store[x][y];
5572     Store[x][y] = Store2[x][y] = 0;
5573     GfxElement[x][y] = EL_UNDEFINED;
5574
5575     /* player can escape from explosions and might therefore be still alive */
5576     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5577         element <= EL_PLAYER_IS_EXPLODING_4)
5578     {
5579       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5580       int explosion_element = EL_PLAYER_1 + player_nr;
5581       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5582       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5583
5584       if (level.use_explosion_element[player_nr])
5585         explosion_element = level.explosion_element[player_nr];
5586
5587       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5588                     element_info[explosion_element].content.e[xx][yy]);
5589     }
5590
5591     /* restore probably existing indestructible background element */
5592     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5593       element = Feld[x][y] = Back[x][y];
5594     Back[x][y] = 0;
5595
5596     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5597     GfxDir[x][y] = MV_NONE;
5598     ChangeDelay[x][y] = 0;
5599     ChangePage[x][y] = -1;
5600
5601     CustomValue[x][y] = 0;
5602
5603     InitField_WithBug2(x, y, FALSE);
5604
5605     TEST_DrawLevelField(x, y);
5606
5607     TestIfElementTouchesCustomElement(x, y);
5608
5609     if (GFX_CRUMBLED(element))
5610       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5611
5612     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5613       StorePlayer[x][y] = 0;
5614
5615     if (ELEM_IS_PLAYER(element))
5616       RelocatePlayer(x, y, element);
5617   }
5618   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5619   {
5620     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5621     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5622
5623     if (phase == delay)
5624       TEST_DrawLevelFieldCrumbled(x, y);
5625
5626     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5627     {
5628       DrawLevelElement(x, y, Back[x][y]);
5629       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5630     }
5631     else if (IS_WALKABLE_UNDER(Back[x][y]))
5632     {
5633       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5634       DrawLevelElementThruMask(x, y, Back[x][y]);
5635     }
5636     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5637       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5638   }
5639 }
5640
5641 void DynaExplode(int ex, int ey)
5642 {
5643   int i, j;
5644   int dynabomb_element = Feld[ex][ey];
5645   int dynabomb_size = 1;
5646   boolean dynabomb_xl = FALSE;
5647   struct PlayerInfo *player;
5648   static int xy[4][2] =
5649   {
5650     { 0, -1 },
5651     { -1, 0 },
5652     { +1, 0 },
5653     { 0, +1 }
5654   };
5655
5656   if (IS_ACTIVE_BOMB(dynabomb_element))
5657   {
5658     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5659     dynabomb_size = player->dynabomb_size;
5660     dynabomb_xl = player->dynabomb_xl;
5661     player->dynabombs_left++;
5662   }
5663
5664   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5665
5666   for (i = 0; i < NUM_DIRECTIONS; i++)
5667   {
5668     for (j = 1; j <= dynabomb_size; j++)
5669     {
5670       int x = ex + j * xy[i][0];
5671       int y = ey + j * xy[i][1];
5672       int element;
5673
5674       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5675         break;
5676
5677       element = Feld[x][y];
5678
5679       /* do not restart explosions of fields with active bombs */
5680       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5681         continue;
5682
5683       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5684
5685       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5686           !IS_DIGGABLE(element) && !dynabomb_xl)
5687         break;
5688     }
5689   }
5690 }
5691
5692 void Bang(int x, int y)
5693 {
5694   int element = MovingOrBlocked2Element(x, y);
5695   int explosion_type = EX_TYPE_NORMAL;
5696
5697   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5698   {
5699     struct PlayerInfo *player = PLAYERINFO(x, y);
5700
5701     element = Feld[x][y] = player->initial_element;
5702
5703     if (level.use_explosion_element[player->index_nr])
5704     {
5705       int explosion_element = level.explosion_element[player->index_nr];
5706
5707       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5708         explosion_type = EX_TYPE_CROSS;
5709       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5710         explosion_type = EX_TYPE_CENTER;
5711     }
5712   }
5713
5714   switch (element)
5715   {
5716     case EL_BUG:
5717     case EL_SPACESHIP:
5718     case EL_BD_BUTTERFLY:
5719     case EL_BD_FIREFLY:
5720     case EL_YAMYAM:
5721     case EL_DARK_YAMYAM:
5722     case EL_ROBOT:
5723     case EL_PACMAN:
5724     case EL_MOLE:
5725       RaiseScoreElement(element);
5726       break;
5727
5728     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5729     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5730     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5731     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5732     case EL_DYNABOMB_INCREASE_NUMBER:
5733     case EL_DYNABOMB_INCREASE_SIZE:
5734     case EL_DYNABOMB_INCREASE_POWER:
5735       explosion_type = EX_TYPE_DYNA;
5736       break;
5737
5738     case EL_DC_LANDMINE:
5739       explosion_type = EX_TYPE_CENTER;
5740       break;
5741
5742     case EL_PENGUIN:
5743     case EL_LAMP:
5744     case EL_LAMP_ACTIVE:
5745     case EL_AMOEBA_TO_DIAMOND:
5746       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5747         explosion_type = EX_TYPE_CENTER;
5748       break;
5749
5750     default:
5751       if (element_info[element].explosion_type == EXPLODES_CROSS)
5752         explosion_type = EX_TYPE_CROSS;
5753       else if (element_info[element].explosion_type == EXPLODES_1X1)
5754         explosion_type = EX_TYPE_CENTER;
5755       break;
5756   }
5757
5758   if (explosion_type == EX_TYPE_DYNA)
5759     DynaExplode(x, y);
5760   else
5761     Explode(x, y, EX_PHASE_START, explosion_type);
5762
5763   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5764 }
5765
5766 void SplashAcid(int x, int y)
5767 {
5768   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5769       (!IN_LEV_FIELD(x - 1, y - 2) ||
5770        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5771     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5772
5773   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5774       (!IN_LEV_FIELD(x + 1, y - 2) ||
5775        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5776     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5777
5778   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5779 }
5780
5781 static void InitBeltMovement()
5782 {
5783   static int belt_base_element[4] =
5784   {
5785     EL_CONVEYOR_BELT_1_LEFT,
5786     EL_CONVEYOR_BELT_2_LEFT,
5787     EL_CONVEYOR_BELT_3_LEFT,
5788     EL_CONVEYOR_BELT_4_LEFT
5789   };
5790   static int belt_base_active_element[4] =
5791   {
5792     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5793     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5794     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5795     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5796   };
5797
5798   int x, y, i, j;
5799
5800   /* set frame order for belt animation graphic according to belt direction */
5801   for (i = 0; i < NUM_BELTS; i++)
5802   {
5803     int belt_nr = i;
5804
5805     for (j = 0; j < NUM_BELT_PARTS; j++)
5806     {
5807       int element = belt_base_active_element[belt_nr] + j;
5808       int graphic_1 = el2img(element);
5809       int graphic_2 = el2panelimg(element);
5810
5811       if (game.belt_dir[i] == MV_LEFT)
5812       {
5813         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5814         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5815       }
5816       else
5817       {
5818         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5819         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5820       }
5821     }
5822   }
5823
5824   SCAN_PLAYFIELD(x, y)
5825   {
5826     int element = Feld[x][y];
5827
5828     for (i = 0; i < NUM_BELTS; i++)
5829     {
5830       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5831       {
5832         int e_belt_nr = getBeltNrFromBeltElement(element);
5833         int belt_nr = i;
5834
5835         if (e_belt_nr == belt_nr)
5836         {
5837           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5838
5839           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5840         }
5841       }
5842     }
5843   }
5844 }
5845
5846 static void ToggleBeltSwitch(int x, int y)
5847 {
5848   static int belt_base_element[4] =
5849   {
5850     EL_CONVEYOR_BELT_1_LEFT,
5851     EL_CONVEYOR_BELT_2_LEFT,
5852     EL_CONVEYOR_BELT_3_LEFT,
5853     EL_CONVEYOR_BELT_4_LEFT
5854   };
5855   static int belt_base_active_element[4] =
5856   {
5857     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5858     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5859     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5860     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5861   };
5862   static int belt_base_switch_element[4] =
5863   {
5864     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5865     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5866     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5867     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5868   };
5869   static int belt_move_dir[4] =
5870   {
5871     MV_LEFT,
5872     MV_NONE,
5873     MV_RIGHT,
5874     MV_NONE,
5875   };
5876
5877   int element = Feld[x][y];
5878   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5879   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5880   int belt_dir = belt_move_dir[belt_dir_nr];
5881   int xx, yy, i;
5882
5883   if (!IS_BELT_SWITCH(element))
5884     return;
5885
5886   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5887   game.belt_dir[belt_nr] = belt_dir;
5888
5889   if (belt_dir_nr == 3)
5890     belt_dir_nr = 1;
5891
5892   /* set frame order for belt animation graphic according to belt direction */
5893   for (i = 0; i < NUM_BELT_PARTS; i++)
5894   {
5895     int element = belt_base_active_element[belt_nr] + i;
5896     int graphic_1 = el2img(element);
5897     int graphic_2 = el2panelimg(element);
5898
5899     if (belt_dir == MV_LEFT)
5900     {
5901       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5903     }
5904     else
5905     {
5906       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5907       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5908     }
5909   }
5910
5911   SCAN_PLAYFIELD(xx, yy)
5912   {
5913     int element = Feld[xx][yy];
5914
5915     if (IS_BELT_SWITCH(element))
5916     {
5917       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5918
5919       if (e_belt_nr == belt_nr)
5920       {
5921         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5922         TEST_DrawLevelField(xx, yy);
5923       }
5924     }
5925     else if (IS_BELT(element) && belt_dir != MV_NONE)
5926     {
5927       int e_belt_nr = getBeltNrFromBeltElement(element);
5928
5929       if (e_belt_nr == belt_nr)
5930       {
5931         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5932
5933         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5934         TEST_DrawLevelField(xx, yy);
5935       }
5936     }
5937     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5938     {
5939       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5940
5941       if (e_belt_nr == belt_nr)
5942       {
5943         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5944
5945         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5946         TEST_DrawLevelField(xx, yy);
5947       }
5948     }
5949   }
5950 }
5951
5952 static void ToggleSwitchgateSwitch(int x, int y)
5953 {
5954   int xx, yy;
5955
5956   game.switchgate_pos = !game.switchgate_pos;
5957
5958   SCAN_PLAYFIELD(xx, yy)
5959   {
5960     int element = Feld[xx][yy];
5961
5962     if (element == EL_SWITCHGATE_SWITCH_UP)
5963     {
5964       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5965       TEST_DrawLevelField(xx, yy);
5966     }
5967     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5968     {
5969       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5970       TEST_DrawLevelField(xx, yy);
5971     }
5972     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5973     {
5974       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5975       TEST_DrawLevelField(xx, yy);
5976     }
5977     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5978     {
5979       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5980       TEST_DrawLevelField(xx, yy);
5981     }
5982     else if (element == EL_SWITCHGATE_OPEN ||
5983              element == EL_SWITCHGATE_OPENING)
5984     {
5985       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5986
5987       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5988     }
5989     else if (element == EL_SWITCHGATE_CLOSED ||
5990              element == EL_SWITCHGATE_CLOSING)
5991     {
5992       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5993
5994       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5995     }
5996   }
5997 }
5998
5999 static int getInvisibleActiveFromInvisibleElement(int element)
6000 {
6001   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6002           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6003           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6004           element);
6005 }
6006
6007 static int getInvisibleFromInvisibleActiveElement(int element)
6008 {
6009   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6010           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6011           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6012           element);
6013 }
6014
6015 static void RedrawAllLightSwitchesAndInvisibleElements()
6016 {
6017   int x, y;
6018
6019   SCAN_PLAYFIELD(x, y)
6020   {
6021     int element = Feld[x][y];
6022
6023     if (element == EL_LIGHT_SWITCH &&
6024         game.light_time_left > 0)
6025     {
6026       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6027       TEST_DrawLevelField(x, y);
6028     }
6029     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6030              game.light_time_left == 0)
6031     {
6032       Feld[x][y] = EL_LIGHT_SWITCH;
6033       TEST_DrawLevelField(x, y);
6034     }
6035     else if (element == EL_EMC_DRIPPER &&
6036              game.light_time_left > 0)
6037     {
6038       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6039       TEST_DrawLevelField(x, y);
6040     }
6041     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6042              game.light_time_left == 0)
6043     {
6044       Feld[x][y] = EL_EMC_DRIPPER;
6045       TEST_DrawLevelField(x, y);
6046     }
6047     else if (element == EL_INVISIBLE_STEELWALL ||
6048              element == EL_INVISIBLE_WALL ||
6049              element == EL_INVISIBLE_SAND)
6050     {
6051       if (game.light_time_left > 0)
6052         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6053
6054       TEST_DrawLevelField(x, y);
6055
6056       /* uncrumble neighbour fields, if needed */
6057       if (element == EL_INVISIBLE_SAND)
6058         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6059     }
6060     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6061              element == EL_INVISIBLE_WALL_ACTIVE ||
6062              element == EL_INVISIBLE_SAND_ACTIVE)
6063     {
6064       if (game.light_time_left == 0)
6065         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6066
6067       TEST_DrawLevelField(x, y);
6068
6069       /* re-crumble neighbour fields, if needed */
6070       if (element == EL_INVISIBLE_SAND)
6071         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6072     }
6073   }
6074 }
6075
6076 static void RedrawAllInvisibleElementsForLenses()
6077 {
6078   int x, y;
6079
6080   SCAN_PLAYFIELD(x, y)
6081   {
6082     int element = Feld[x][y];
6083
6084     if (element == EL_EMC_DRIPPER &&
6085         game.lenses_time_left > 0)
6086     {
6087       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088       TEST_DrawLevelField(x, y);
6089     }
6090     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091              game.lenses_time_left == 0)
6092     {
6093       Feld[x][y] = EL_EMC_DRIPPER;
6094       TEST_DrawLevelField(x, y);
6095     }
6096     else if (element == EL_INVISIBLE_STEELWALL ||
6097              element == EL_INVISIBLE_WALL ||
6098              element == EL_INVISIBLE_SAND)
6099     {
6100       if (game.lenses_time_left > 0)
6101         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6102
6103       TEST_DrawLevelField(x, y);
6104
6105       /* uncrumble neighbour fields, if needed */
6106       if (element == EL_INVISIBLE_SAND)
6107         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108     }
6109     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110              element == EL_INVISIBLE_WALL_ACTIVE ||
6111              element == EL_INVISIBLE_SAND_ACTIVE)
6112     {
6113       if (game.lenses_time_left == 0)
6114         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6115
6116       TEST_DrawLevelField(x, y);
6117
6118       /* re-crumble neighbour fields, if needed */
6119       if (element == EL_INVISIBLE_SAND)
6120         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6121     }
6122   }
6123 }
6124
6125 static void RedrawAllInvisibleElementsForMagnifier()
6126 {
6127   int x, y;
6128
6129   SCAN_PLAYFIELD(x, y)
6130   {
6131     int element = Feld[x][y];
6132
6133     if (element == EL_EMC_FAKE_GRASS &&
6134         game.magnify_time_left > 0)
6135     {
6136       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6137       TEST_DrawLevelField(x, y);
6138     }
6139     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6140              game.magnify_time_left == 0)
6141     {
6142       Feld[x][y] = EL_EMC_FAKE_GRASS;
6143       TEST_DrawLevelField(x, y);
6144     }
6145     else if (IS_GATE_GRAY(element) &&
6146              game.magnify_time_left > 0)
6147     {
6148       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6149                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6150                     IS_EM_GATE_GRAY(element) ?
6151                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6152                     IS_EMC_GATE_GRAY(element) ?
6153                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6154                     IS_DC_GATE_GRAY(element) ?
6155                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6156                     element);
6157       TEST_DrawLevelField(x, y);
6158     }
6159     else if (IS_GATE_GRAY_ACTIVE(element) &&
6160              game.magnify_time_left == 0)
6161     {
6162       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6163                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6164                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6165                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6166                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6167                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6168                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6169                     EL_DC_GATE_WHITE_GRAY :
6170                     element);
6171       TEST_DrawLevelField(x, y);
6172     }
6173   }
6174 }
6175
6176 static void ToggleLightSwitch(int x, int y)
6177 {
6178   int element = Feld[x][y];
6179
6180   game.light_time_left =
6181     (element == EL_LIGHT_SWITCH ?
6182      level.time_light * FRAMES_PER_SECOND : 0);
6183
6184   RedrawAllLightSwitchesAndInvisibleElements();
6185 }
6186
6187 static void ActivateTimegateSwitch(int x, int y)
6188 {
6189   int xx, yy;
6190
6191   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6192
6193   SCAN_PLAYFIELD(xx, yy)
6194   {
6195     int element = Feld[xx][yy];
6196
6197     if (element == EL_TIMEGATE_CLOSED ||
6198         element == EL_TIMEGATE_CLOSING)
6199     {
6200       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6201       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6202     }
6203
6204     /*
6205     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6206     {
6207       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6208       TEST_DrawLevelField(xx, yy);
6209     }
6210     */
6211
6212   }
6213
6214   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6215                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6216 }
6217
6218 void Impact(int x, int y)
6219 {
6220   boolean last_line = (y == lev_fieldy - 1);
6221   boolean object_hit = FALSE;
6222   boolean impact = (last_line || object_hit);
6223   int element = Feld[x][y];
6224   int smashed = EL_STEELWALL;
6225
6226   if (!last_line)       /* check if element below was hit */
6227   {
6228     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6229       return;
6230
6231     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6232                                          MovDir[x][y + 1] != MV_DOWN ||
6233                                          MovPos[x][y + 1] <= TILEY / 2));
6234
6235     /* do not smash moving elements that left the smashed field in time */
6236     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6237         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6238       object_hit = FALSE;
6239
6240 #if USE_QUICKSAND_IMPACT_BUGFIX
6241     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6242     {
6243       RemoveMovingField(x, y + 1);
6244       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6245       Feld[x][y + 2] = EL_ROCK;
6246       TEST_DrawLevelField(x, y + 2);
6247
6248       object_hit = TRUE;
6249     }
6250
6251     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6252     {
6253       RemoveMovingField(x, y + 1);
6254       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6255       Feld[x][y + 2] = EL_ROCK;
6256       TEST_DrawLevelField(x, y + 2);
6257
6258       object_hit = TRUE;
6259     }
6260 #endif
6261
6262     if (object_hit)
6263       smashed = MovingOrBlocked2Element(x, y + 1);
6264
6265     impact = (last_line || object_hit);
6266   }
6267
6268   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6269   {
6270     SplashAcid(x, y + 1);
6271     return;
6272   }
6273
6274   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6275   /* only reset graphic animation if graphic really changes after impact */
6276   if (impact &&
6277       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6278   {
6279     ResetGfxAnimation(x, y);
6280     TEST_DrawLevelField(x, y);
6281   }
6282
6283   if (impact && CAN_EXPLODE_IMPACT(element))
6284   {
6285     Bang(x, y);
6286     return;
6287   }
6288   else if (impact && element == EL_PEARL &&
6289            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6290   {
6291     ResetGfxAnimation(x, y);
6292
6293     Feld[x][y] = EL_PEARL_BREAKING;
6294     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6295     return;
6296   }
6297   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6298   {
6299     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6300
6301     return;
6302   }
6303
6304   if (impact && element == EL_AMOEBA_DROP)
6305   {
6306     if (object_hit && IS_PLAYER(x, y + 1))
6307       KillPlayerUnlessEnemyProtected(x, y + 1);
6308     else if (object_hit && smashed == EL_PENGUIN)
6309       Bang(x, y + 1);
6310     else
6311     {
6312       Feld[x][y] = EL_AMOEBA_GROWING;
6313       Store[x][y] = EL_AMOEBA_WET;
6314
6315       ResetRandomAnimationValue(x, y);
6316     }
6317     return;
6318   }
6319
6320   if (object_hit)               /* check which object was hit */
6321   {
6322     if ((CAN_PASS_MAGIC_WALL(element) && 
6323          (smashed == EL_MAGIC_WALL ||
6324           smashed == EL_BD_MAGIC_WALL)) ||
6325         (CAN_PASS_DC_MAGIC_WALL(element) &&
6326          smashed == EL_DC_MAGIC_WALL))
6327     {
6328       int xx, yy;
6329       int activated_magic_wall =
6330         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6331          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6332          EL_DC_MAGIC_WALL_ACTIVE);
6333
6334       /* activate magic wall / mill */
6335       SCAN_PLAYFIELD(xx, yy)
6336       {
6337         if (Feld[xx][yy] == smashed)
6338           Feld[xx][yy] = activated_magic_wall;
6339       }
6340
6341       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6342       game.magic_wall_active = TRUE;
6343
6344       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6345                             SND_MAGIC_WALL_ACTIVATING :
6346                             smashed == EL_BD_MAGIC_WALL ?
6347                             SND_BD_MAGIC_WALL_ACTIVATING :
6348                             SND_DC_MAGIC_WALL_ACTIVATING));
6349     }
6350
6351     if (IS_PLAYER(x, y + 1))
6352     {
6353       if (CAN_SMASH_PLAYER(element))
6354       {
6355         KillPlayerUnlessEnemyProtected(x, y + 1);
6356         return;
6357       }
6358     }
6359     else if (smashed == EL_PENGUIN)
6360     {
6361       if (CAN_SMASH_PLAYER(element))
6362       {
6363         Bang(x, y + 1);
6364         return;
6365       }
6366     }
6367     else if (element == EL_BD_DIAMOND)
6368     {
6369       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6370       {
6371         Bang(x, y + 1);
6372         return;
6373       }
6374     }
6375     else if (((element == EL_SP_INFOTRON ||
6376                element == EL_SP_ZONK) &&
6377               (smashed == EL_SP_SNIKSNAK ||
6378                smashed == EL_SP_ELECTRON ||
6379                smashed == EL_SP_DISK_ORANGE)) ||
6380              (element == EL_SP_INFOTRON &&
6381               smashed == EL_SP_DISK_YELLOW))
6382     {
6383       Bang(x, y + 1);
6384       return;
6385     }
6386     else if (CAN_SMASH_EVERYTHING(element))
6387     {
6388       if (IS_CLASSIC_ENEMY(smashed) ||
6389           CAN_EXPLODE_SMASHED(smashed))
6390       {
6391         Bang(x, y + 1);
6392         return;
6393       }
6394       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6395       {
6396         if (smashed == EL_LAMP ||
6397             smashed == EL_LAMP_ACTIVE)
6398         {
6399           Bang(x, y + 1);
6400           return;
6401         }
6402         else if (smashed == EL_NUT)
6403         {
6404           Feld[x][y + 1] = EL_NUT_BREAKING;
6405           PlayLevelSound(x, y, SND_NUT_BREAKING);
6406           RaiseScoreElement(EL_NUT);
6407           return;
6408         }
6409         else if (smashed == EL_PEARL)
6410         {
6411           ResetGfxAnimation(x, y);
6412
6413           Feld[x][y + 1] = EL_PEARL_BREAKING;
6414           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6415           return;
6416         }
6417         else if (smashed == EL_DIAMOND)
6418         {
6419           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6420           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6421           return;
6422         }
6423         else if (IS_BELT_SWITCH(smashed))
6424         {
6425           ToggleBeltSwitch(x, y + 1);
6426         }
6427         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6428                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6429                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6430                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6431         {
6432           ToggleSwitchgateSwitch(x, y + 1);
6433         }
6434         else if (smashed == EL_LIGHT_SWITCH ||
6435                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6436         {
6437           ToggleLightSwitch(x, y + 1);
6438         }
6439         else
6440         {
6441           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6442
6443           CheckElementChangeBySide(x, y + 1, smashed, element,
6444                                    CE_SWITCHED, CH_SIDE_TOP);
6445           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6446                                             CH_SIDE_TOP);
6447         }
6448       }
6449       else
6450       {
6451         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6452       }
6453     }
6454   }
6455
6456   /* play sound of magic wall / mill */
6457   if (!last_line &&
6458       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6459        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6460        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6461   {
6462     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6463       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6464     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6465       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6466     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6467       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6468
6469     return;
6470   }
6471
6472   /* play sound of object that hits the ground */
6473   if (last_line || object_hit)
6474     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6475 }
6476
6477 inline static void TurnRoundExt(int x, int y)
6478 {
6479   static struct
6480   {
6481     int dx, dy;
6482   } move_xy[] =
6483   {
6484     {  0,  0 },
6485     { -1,  0 },
6486     { +1,  0 },
6487     {  0,  0 },
6488     {  0, -1 },
6489     {  0,  0 }, { 0, 0 }, { 0, 0 },
6490     {  0, +1 }
6491   };
6492   static struct
6493   {
6494     int left, right, back;
6495   } turn[] =
6496   {
6497     { 0,        0,              0        },
6498     { MV_DOWN,  MV_UP,          MV_RIGHT },
6499     { MV_UP,    MV_DOWN,        MV_LEFT  },
6500     { 0,        0,              0        },
6501     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6502     { 0,        0,              0        },
6503     { 0,        0,              0        },
6504     { 0,        0,              0        },
6505     { MV_RIGHT, MV_LEFT,        MV_UP    }
6506   };
6507
6508   int element = Feld[x][y];
6509   int move_pattern = element_info[element].move_pattern;
6510
6511   int old_move_dir = MovDir[x][y];
6512   int left_dir  = turn[old_move_dir].left;
6513   int right_dir = turn[old_move_dir].right;
6514   int back_dir  = turn[old_move_dir].back;
6515
6516   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6517   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6518   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6519   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6520
6521   int left_x  = x + left_dx,  left_y  = y + left_dy;
6522   int right_x = x + right_dx, right_y = y + right_dy;
6523   int move_x  = x + move_dx,  move_y  = y + move_dy;
6524
6525   int xx, yy;
6526
6527   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6528   {
6529     TestIfBadThingTouchesOtherBadThing(x, y);
6530
6531     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6532       MovDir[x][y] = right_dir;
6533     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6534       MovDir[x][y] = left_dir;
6535
6536     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6537       MovDelay[x][y] = 9;
6538     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6539       MovDelay[x][y] = 1;
6540   }
6541   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6542   {
6543     TestIfBadThingTouchesOtherBadThing(x, y);
6544
6545     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6546       MovDir[x][y] = left_dir;
6547     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6548       MovDir[x][y] = right_dir;
6549
6550     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6551       MovDelay[x][y] = 9;
6552     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6553       MovDelay[x][y] = 1;
6554   }
6555   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6556   {
6557     TestIfBadThingTouchesOtherBadThing(x, y);
6558
6559     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6560       MovDir[x][y] = left_dir;
6561     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6562       MovDir[x][y] = right_dir;
6563
6564     if (MovDir[x][y] != old_move_dir)
6565       MovDelay[x][y] = 9;
6566   }
6567   else if (element == EL_YAMYAM)
6568   {
6569     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6570     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6571
6572     if (can_turn_left && can_turn_right)
6573       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6574     else if (can_turn_left)
6575       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6576     else if (can_turn_right)
6577       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6578     else
6579       MovDir[x][y] = back_dir;
6580
6581     MovDelay[x][y] = 16 + 16 * RND(3);
6582   }
6583   else if (element == EL_DARK_YAMYAM)
6584   {
6585     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6586                                                          left_x, left_y);
6587     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6588                                                          right_x, right_y);
6589
6590     if (can_turn_left && can_turn_right)
6591       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6592     else if (can_turn_left)
6593       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6594     else if (can_turn_right)
6595       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6596     else
6597       MovDir[x][y] = back_dir;
6598
6599     MovDelay[x][y] = 16 + 16 * RND(3);
6600   }
6601   else if (element == EL_PACMAN)
6602   {
6603     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6604     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6605
6606     if (can_turn_left && can_turn_right)
6607       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608     else if (can_turn_left)
6609       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610     else if (can_turn_right)
6611       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6612     else
6613       MovDir[x][y] = back_dir;
6614
6615     MovDelay[x][y] = 6 + RND(40);
6616   }
6617   else if (element == EL_PIG)
6618   {
6619     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6620     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6621     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6622     boolean should_turn_left, should_turn_right, should_move_on;
6623     int rnd_value = 24;
6624     int rnd = RND(rnd_value);
6625
6626     should_turn_left = (can_turn_left &&
6627                         (!can_move_on ||
6628                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6629                                                    y + back_dy + left_dy)));
6630     should_turn_right = (can_turn_right &&
6631                          (!can_move_on ||
6632                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6633                                                     y + back_dy + right_dy)));
6634     should_move_on = (can_move_on &&
6635                       (!can_turn_left ||
6636                        !can_turn_right ||
6637                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6638                                                  y + move_dy + left_dy) ||
6639                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6640                                                  y + move_dy + right_dy)));
6641
6642     if (should_turn_left || should_turn_right || should_move_on)
6643     {
6644       if (should_turn_left && should_turn_right && should_move_on)
6645         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6646                         rnd < 2 * rnd_value / 3 ? right_dir :
6647                         old_move_dir);
6648       else if (should_turn_left && should_turn_right)
6649         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6650       else if (should_turn_left && should_move_on)
6651         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6652       else if (should_turn_right && should_move_on)
6653         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6654       else if (should_turn_left)
6655         MovDir[x][y] = left_dir;
6656       else if (should_turn_right)
6657         MovDir[x][y] = right_dir;
6658       else if (should_move_on)
6659         MovDir[x][y] = old_move_dir;
6660     }
6661     else if (can_move_on && rnd > rnd_value / 8)
6662       MovDir[x][y] = old_move_dir;
6663     else if (can_turn_left && can_turn_right)
6664       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6665     else if (can_turn_left && rnd > rnd_value / 8)
6666       MovDir[x][y] = left_dir;
6667     else if (can_turn_right && rnd > rnd_value/8)
6668       MovDir[x][y] = right_dir;
6669     else
6670       MovDir[x][y] = back_dir;
6671
6672     xx = x + move_xy[MovDir[x][y]].dx;
6673     yy = y + move_xy[MovDir[x][y]].dy;
6674
6675     if (!IN_LEV_FIELD(xx, yy) ||
6676         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6677       MovDir[x][y] = old_move_dir;
6678
6679     MovDelay[x][y] = 0;
6680   }
6681   else if (element == EL_DRAGON)
6682   {
6683     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6684     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6685     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6686     int rnd_value = 24;
6687     int rnd = RND(rnd_value);
6688
6689     if (can_move_on && rnd > rnd_value / 8)
6690       MovDir[x][y] = old_move_dir;
6691     else if (can_turn_left && can_turn_right)
6692       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6693     else if (can_turn_left && rnd > rnd_value / 8)
6694       MovDir[x][y] = left_dir;
6695     else if (can_turn_right && rnd > rnd_value / 8)
6696       MovDir[x][y] = right_dir;
6697     else
6698       MovDir[x][y] = back_dir;
6699
6700     xx = x + move_xy[MovDir[x][y]].dx;
6701     yy = y + move_xy[MovDir[x][y]].dy;
6702
6703     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6704       MovDir[x][y] = old_move_dir;
6705
6706     MovDelay[x][y] = 0;
6707   }
6708   else if (element == EL_MOLE)
6709   {
6710     boolean can_move_on =
6711       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6712                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6713                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6714     if (!can_move_on)
6715     {
6716       boolean can_turn_left =
6717         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6718                               IS_AMOEBOID(Feld[left_x][left_y])));
6719
6720       boolean can_turn_right =
6721         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6722                               IS_AMOEBOID(Feld[right_x][right_y])));
6723
6724       if (can_turn_left && can_turn_right)
6725         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6726       else if (can_turn_left)
6727         MovDir[x][y] = left_dir;
6728       else
6729         MovDir[x][y] = right_dir;
6730     }
6731
6732     if (MovDir[x][y] != old_move_dir)
6733       MovDelay[x][y] = 9;
6734   }
6735   else if (element == EL_BALLOON)
6736   {
6737     MovDir[x][y] = game.wind_direction;
6738     MovDelay[x][y] = 0;
6739   }
6740   else if (element == EL_SPRING)
6741   {
6742     if (MovDir[x][y] & MV_HORIZONTAL)
6743     {
6744       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6745           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6746       {
6747         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6748         ResetGfxAnimation(move_x, move_y);
6749         TEST_DrawLevelField(move_x, move_y);
6750
6751         MovDir[x][y] = back_dir;
6752       }
6753       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6754                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6755         MovDir[x][y] = MV_NONE;
6756     }
6757
6758     MovDelay[x][y] = 0;
6759   }
6760   else if (element == EL_ROBOT ||
6761            element == EL_SATELLITE ||
6762            element == EL_PENGUIN ||
6763            element == EL_EMC_ANDROID)
6764   {
6765     int attr_x = -1, attr_y = -1;
6766
6767     if (AllPlayersGone)
6768     {
6769       attr_x = ExitX;
6770       attr_y = ExitY;
6771     }
6772     else
6773     {
6774       int i;
6775
6776       for (i = 0; i < MAX_PLAYERS; i++)
6777       {
6778         struct PlayerInfo *player = &stored_player[i];
6779         int jx = player->jx, jy = player->jy;
6780
6781         if (!player->active)
6782           continue;
6783
6784         if (attr_x == -1 ||
6785             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6786         {
6787           attr_x = jx;
6788           attr_y = jy;
6789         }
6790       }
6791     }
6792
6793     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6794         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6795          game.engine_version < VERSION_IDENT(3,1,0,0)))
6796     {
6797       attr_x = ZX;
6798       attr_y = ZY;
6799     }
6800
6801     if (element == EL_PENGUIN)
6802     {
6803       int i;
6804       static int xy[4][2] =
6805       {
6806         { 0, -1 },
6807         { -1, 0 },
6808         { +1, 0 },
6809         { 0, +1 }
6810       };
6811
6812       for (i = 0; i < NUM_DIRECTIONS; i++)
6813       {
6814         int ex = x + xy[i][0];
6815         int ey = y + xy[i][1];
6816
6817         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6818                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6819                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6820                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6821         {
6822           attr_x = ex;
6823           attr_y = ey;
6824           break;
6825         }
6826       }
6827     }
6828
6829     MovDir[x][y] = MV_NONE;
6830     if (attr_x < x)
6831       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6832     else if (attr_x > x)
6833       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6834     if (attr_y < y)
6835       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6836     else if (attr_y > y)
6837       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6838
6839     if (element == EL_ROBOT)
6840     {
6841       int newx, newy;
6842
6843       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6844         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6845       Moving2Blocked(x, y, &newx, &newy);
6846
6847       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6848         MovDelay[x][y] = 8 + 8 * !RND(3);
6849       else
6850         MovDelay[x][y] = 16;
6851     }
6852     else if (element == EL_PENGUIN)
6853     {
6854       int newx, newy;
6855
6856       MovDelay[x][y] = 1;
6857
6858       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6859       {
6860         boolean first_horiz = RND(2);
6861         int new_move_dir = MovDir[x][y];
6862
6863         MovDir[x][y] =
6864           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6865         Moving2Blocked(x, y, &newx, &newy);
6866
6867         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6868           return;
6869
6870         MovDir[x][y] =
6871           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6872         Moving2Blocked(x, y, &newx, &newy);
6873
6874         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6875           return;
6876
6877         MovDir[x][y] = old_move_dir;
6878         return;
6879       }
6880     }
6881     else if (element == EL_SATELLITE)
6882     {
6883       int newx, newy;
6884
6885       MovDelay[x][y] = 1;
6886
6887       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6888       {
6889         boolean first_horiz = RND(2);
6890         int new_move_dir = MovDir[x][y];
6891
6892         MovDir[x][y] =
6893           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894         Moving2Blocked(x, y, &newx, &newy);
6895
6896         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6897           return;
6898
6899         MovDir[x][y] =
6900           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6901         Moving2Blocked(x, y, &newx, &newy);
6902
6903         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6904           return;
6905
6906         MovDir[x][y] = old_move_dir;
6907         return;
6908       }
6909     }
6910     else if (element == EL_EMC_ANDROID)
6911     {
6912       static int check_pos[16] =
6913       {
6914         -1,             /*  0 => (invalid)          */
6915         7,              /*  1 => MV_LEFT            */
6916         3,              /*  2 => MV_RIGHT           */
6917         -1,             /*  3 => (invalid)          */
6918         1,              /*  4 =>            MV_UP   */
6919         0,              /*  5 => MV_LEFT  | MV_UP   */
6920         2,              /*  6 => MV_RIGHT | MV_UP   */
6921         -1,             /*  7 => (invalid)          */
6922         5,              /*  8 =>            MV_DOWN */
6923         6,              /*  9 => MV_LEFT  | MV_DOWN */
6924         4,              /* 10 => MV_RIGHT | MV_DOWN */
6925         -1,             /* 11 => (invalid)          */
6926         -1,             /* 12 => (invalid)          */
6927         -1,             /* 13 => (invalid)          */
6928         -1,             /* 14 => (invalid)          */
6929         -1,             /* 15 => (invalid)          */
6930       };
6931       static struct
6932       {
6933         int dx, dy;
6934         int dir;
6935       } check_xy[8] =
6936       {
6937         { -1, -1,       MV_LEFT  | MV_UP   },
6938         {  0, -1,                  MV_UP   },
6939         { +1, -1,       MV_RIGHT | MV_UP   },
6940         { +1,  0,       MV_RIGHT           },
6941         { +1, +1,       MV_RIGHT | MV_DOWN },
6942         {  0, +1,                  MV_DOWN },
6943         { -1, +1,       MV_LEFT  | MV_DOWN },
6944         { -1,  0,       MV_LEFT            },
6945       };
6946       int start_pos, check_order;
6947       boolean can_clone = FALSE;
6948       int i;
6949
6950       /* check if there is any free field around current position */
6951       for (i = 0; i < 8; i++)
6952       {
6953         int newx = x + check_xy[i].dx;
6954         int newy = y + check_xy[i].dy;
6955
6956         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6957         {
6958           can_clone = TRUE;
6959
6960           break;
6961         }
6962       }
6963
6964       if (can_clone)            /* randomly find an element to clone */
6965       {
6966         can_clone = FALSE;
6967
6968         start_pos = check_pos[RND(8)];
6969         check_order = (RND(2) ? -1 : +1);
6970
6971         for (i = 0; i < 8; i++)
6972         {
6973           int pos_raw = start_pos + i * check_order;
6974           int pos = (pos_raw + 8) % 8;
6975           int newx = x + check_xy[pos].dx;
6976           int newy = y + check_xy[pos].dy;
6977
6978           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6979           {
6980             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6981             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6982
6983             Store[x][y] = Feld[newx][newy];
6984
6985             can_clone = TRUE;
6986
6987             break;
6988           }
6989         }
6990       }
6991
6992       if (can_clone)            /* randomly find a direction to move */
6993       {
6994         can_clone = FALSE;
6995
6996         start_pos = check_pos[RND(8)];
6997         check_order = (RND(2) ? -1 : +1);
6998
6999         for (i = 0; i < 8; i++)
7000         {
7001           int pos_raw = start_pos + i * check_order;
7002           int pos = (pos_raw + 8) % 8;
7003           int newx = x + check_xy[pos].dx;
7004           int newy = y + check_xy[pos].dy;
7005           int new_move_dir = check_xy[pos].dir;
7006
7007           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7008           {
7009             MovDir[x][y] = new_move_dir;
7010             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7011
7012             can_clone = TRUE;
7013
7014             break;
7015           }
7016         }
7017       }
7018
7019       if (can_clone)            /* cloning and moving successful */
7020         return;
7021
7022       /* cannot clone -- try to move towards player */
7023
7024       start_pos = check_pos[MovDir[x][y] & 0x0f];
7025       check_order = (RND(2) ? -1 : +1);
7026
7027       for (i = 0; i < 3; i++)
7028       {
7029         /* first check start_pos, then previous/next or (next/previous) pos */
7030         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7031         int pos = (pos_raw + 8) % 8;
7032         int newx = x + check_xy[pos].dx;
7033         int newy = y + check_xy[pos].dy;
7034         int new_move_dir = check_xy[pos].dir;
7035
7036         if (IS_PLAYER(newx, newy))
7037           break;
7038
7039         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7040         {
7041           MovDir[x][y] = new_move_dir;
7042           MovDelay[x][y] = level.android_move_time * 8 + 1;
7043
7044           break;
7045         }
7046       }
7047     }
7048   }
7049   else if (move_pattern == MV_TURNING_LEFT ||
7050            move_pattern == MV_TURNING_RIGHT ||
7051            move_pattern == MV_TURNING_LEFT_RIGHT ||
7052            move_pattern == MV_TURNING_RIGHT_LEFT ||
7053            move_pattern == MV_TURNING_RANDOM ||
7054            move_pattern == MV_ALL_DIRECTIONS)
7055   {
7056     boolean can_turn_left =
7057       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7058     boolean can_turn_right =
7059       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7060
7061     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7062       return;
7063
7064     if (move_pattern == MV_TURNING_LEFT)
7065       MovDir[x][y] = left_dir;
7066     else if (move_pattern == MV_TURNING_RIGHT)
7067       MovDir[x][y] = right_dir;
7068     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7069       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7070     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7071       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7072     else if (move_pattern == MV_TURNING_RANDOM)
7073       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7074                       can_turn_right && !can_turn_left ? right_dir :
7075                       RND(2) ? left_dir : right_dir);
7076     else if (can_turn_left && can_turn_right)
7077       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7078     else if (can_turn_left)
7079       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7080     else if (can_turn_right)
7081       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7082     else
7083       MovDir[x][y] = back_dir;
7084
7085     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7086   }
7087   else if (move_pattern == MV_HORIZONTAL ||
7088            move_pattern == MV_VERTICAL)
7089   {
7090     if (move_pattern & old_move_dir)
7091       MovDir[x][y] = back_dir;
7092     else if (move_pattern == MV_HORIZONTAL)
7093       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7094     else if (move_pattern == MV_VERTICAL)
7095       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7096
7097     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7098   }
7099   else if (move_pattern & MV_ANY_DIRECTION)
7100   {
7101     MovDir[x][y] = move_pattern;
7102     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7103   }
7104   else if (move_pattern & MV_WIND_DIRECTION)
7105   {
7106     MovDir[x][y] = game.wind_direction;
7107     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108   }
7109   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7110   {
7111     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7112       MovDir[x][y] = left_dir;
7113     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7114       MovDir[x][y] = right_dir;
7115
7116     if (MovDir[x][y] != old_move_dir)
7117       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118   }
7119   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7120   {
7121     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7122       MovDir[x][y] = right_dir;
7123     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7124       MovDir[x][y] = left_dir;
7125
7126     if (MovDir[x][y] != old_move_dir)
7127       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128   }
7129   else if (move_pattern == MV_TOWARDS_PLAYER ||
7130            move_pattern == MV_AWAY_FROM_PLAYER)
7131   {
7132     int attr_x = -1, attr_y = -1;
7133     int newx, newy;
7134     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7135
7136     if (AllPlayersGone)
7137     {
7138       attr_x = ExitX;
7139       attr_y = ExitY;
7140     }
7141     else
7142     {
7143       int i;
7144
7145       for (i = 0; i < MAX_PLAYERS; i++)
7146       {
7147         struct PlayerInfo *player = &stored_player[i];
7148         int jx = player->jx, jy = player->jy;
7149
7150         if (!player->active)
7151           continue;
7152
7153         if (attr_x == -1 ||
7154             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7155         {
7156           attr_x = jx;
7157           attr_y = jy;
7158         }
7159       }
7160     }
7161
7162     MovDir[x][y] = MV_NONE;
7163     if (attr_x < x)
7164       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7165     else if (attr_x > x)
7166       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7167     if (attr_y < y)
7168       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7169     else if (attr_y > y)
7170       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7171
7172     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173
7174     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7175     {
7176       boolean first_horiz = RND(2);
7177       int new_move_dir = MovDir[x][y];
7178
7179       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7180       {
7181         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7182         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7183
7184         return;
7185       }
7186
7187       MovDir[x][y] =
7188         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7189       Moving2Blocked(x, y, &newx, &newy);
7190
7191       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7192         return;
7193
7194       MovDir[x][y] =
7195         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196       Moving2Blocked(x, y, &newx, &newy);
7197
7198       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7199         return;
7200
7201       MovDir[x][y] = old_move_dir;
7202     }
7203   }
7204   else if (move_pattern == MV_WHEN_PUSHED ||
7205            move_pattern == MV_WHEN_DROPPED)
7206   {
7207     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7208       MovDir[x][y] = MV_NONE;
7209
7210     MovDelay[x][y] = 0;
7211   }
7212   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7213   {
7214     static int test_xy[7][2] =
7215     {
7216       { 0, -1 },
7217       { -1, 0 },
7218       { +1, 0 },
7219       { 0, +1 },
7220       { 0, -1 },
7221       { -1, 0 },
7222       { +1, 0 },
7223     };
7224     static int test_dir[7] =
7225     {
7226       MV_UP,
7227       MV_LEFT,
7228       MV_RIGHT,
7229       MV_DOWN,
7230       MV_UP,
7231       MV_LEFT,
7232       MV_RIGHT,
7233     };
7234     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7235     int move_preference = -1000000;     /* start with very low preference */
7236     int new_move_dir = MV_NONE;
7237     int start_test = RND(4);
7238     int i;
7239
7240     for (i = 0; i < NUM_DIRECTIONS; i++)
7241     {
7242       int move_dir = test_dir[start_test + i];
7243       int move_dir_preference;
7244
7245       xx = x + test_xy[start_test + i][0];
7246       yy = y + test_xy[start_test + i][1];
7247
7248       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7249           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7250       {
7251         new_move_dir = move_dir;
7252
7253         break;
7254       }
7255
7256       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7257         continue;
7258
7259       move_dir_preference = -1 * RunnerVisit[xx][yy];
7260       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7261         move_dir_preference = PlayerVisit[xx][yy];
7262
7263       if (move_dir_preference > move_preference)
7264       {
7265         /* prefer field that has not been visited for the longest time */
7266         move_preference = move_dir_preference;
7267         new_move_dir = move_dir;
7268       }
7269       else if (move_dir_preference == move_preference &&
7270                move_dir == old_move_dir)
7271       {
7272         /* prefer last direction when all directions are preferred equally */
7273         move_preference = move_dir_preference;
7274         new_move_dir = move_dir;
7275       }
7276     }
7277
7278     MovDir[x][y] = new_move_dir;
7279     if (old_move_dir != new_move_dir)
7280       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7281   }
7282 }
7283
7284 static void TurnRound(int x, int y)
7285 {
7286   int direction = MovDir[x][y];
7287
7288   TurnRoundExt(x, y);
7289
7290   GfxDir[x][y] = MovDir[x][y];
7291
7292   if (direction != MovDir[x][y])
7293     GfxFrame[x][y] = 0;
7294
7295   if (MovDelay[x][y])
7296     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7297
7298   ResetGfxFrame(x, y);
7299 }
7300
7301 static boolean JustBeingPushed(int x, int y)
7302 {
7303   int i;
7304
7305   for (i = 0; i < MAX_PLAYERS; i++)
7306   {
7307     struct PlayerInfo *player = &stored_player[i];
7308
7309     if (player->active && player->is_pushing && player->MovPos)
7310     {
7311       int next_jx = player->jx + (player->jx - player->last_jx);
7312       int next_jy = player->jy + (player->jy - player->last_jy);
7313
7314       if (x == next_jx && y == next_jy)
7315         return TRUE;
7316     }
7317   }
7318
7319   return FALSE;
7320 }
7321
7322 void StartMoving(int x, int y)
7323 {
7324   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7325   int element = Feld[x][y];
7326
7327   if (Stop[x][y])
7328     return;
7329
7330   if (MovDelay[x][y] == 0)
7331     GfxAction[x][y] = ACTION_DEFAULT;
7332
7333   if (CAN_FALL(element) && y < lev_fieldy - 1)
7334   {
7335     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7336         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7337       if (JustBeingPushed(x, y))
7338         return;
7339
7340     if (element == EL_QUICKSAND_FULL)
7341     {
7342       if (IS_FREE(x, y + 1))
7343       {
7344         InitMovingField(x, y, MV_DOWN);
7345         started_moving = TRUE;
7346
7347         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7348 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7349         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7350           Store[x][y] = EL_ROCK;
7351 #else
7352         Store[x][y] = EL_ROCK;
7353 #endif
7354
7355         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7356       }
7357       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7358       {
7359         if (!MovDelay[x][y])
7360         {
7361           MovDelay[x][y] = TILEY + 1;
7362
7363           ResetGfxAnimation(x, y);
7364           ResetGfxAnimation(x, y + 1);
7365         }
7366
7367         if (MovDelay[x][y])
7368         {
7369           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7370           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7371
7372           MovDelay[x][y]--;
7373           if (MovDelay[x][y])
7374             return;
7375         }
7376
7377         Feld[x][y] = EL_QUICKSAND_EMPTY;
7378         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7379         Store[x][y + 1] = Store[x][y];
7380         Store[x][y] = 0;
7381
7382         PlayLevelSoundAction(x, y, ACTION_FILLING);
7383       }
7384       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7385       {
7386         if (!MovDelay[x][y])
7387         {
7388           MovDelay[x][y] = TILEY + 1;
7389
7390           ResetGfxAnimation(x, y);
7391           ResetGfxAnimation(x, y + 1);
7392         }
7393
7394         if (MovDelay[x][y])
7395         {
7396           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7397           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7398
7399           MovDelay[x][y]--;
7400           if (MovDelay[x][y])
7401             return;
7402         }
7403
7404         Feld[x][y] = EL_QUICKSAND_EMPTY;
7405         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406         Store[x][y + 1] = Store[x][y];
7407         Store[x][y] = 0;
7408
7409         PlayLevelSoundAction(x, y, ACTION_FILLING);
7410       }
7411     }
7412     else if (element == EL_QUICKSAND_FAST_FULL)
7413     {
7414       if (IS_FREE(x, y + 1))
7415       {
7416         InitMovingField(x, y, MV_DOWN);
7417         started_moving = TRUE;
7418
7419         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7420 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7421         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7422           Store[x][y] = EL_ROCK;
7423 #else
7424         Store[x][y] = EL_ROCK;
7425 #endif
7426
7427         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7428       }
7429       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7430       {
7431         if (!MovDelay[x][y])
7432         {
7433           MovDelay[x][y] = TILEY + 1;
7434
7435           ResetGfxAnimation(x, y);
7436           ResetGfxAnimation(x, y + 1);
7437         }
7438
7439         if (MovDelay[x][y])
7440         {
7441           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7442           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7443
7444           MovDelay[x][y]--;
7445           if (MovDelay[x][y])
7446             return;
7447         }
7448
7449         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7450         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7451         Store[x][y + 1] = Store[x][y];
7452         Store[x][y] = 0;
7453
7454         PlayLevelSoundAction(x, y, ACTION_FILLING);
7455       }
7456       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7457       {
7458         if (!MovDelay[x][y])
7459         {
7460           MovDelay[x][y] = TILEY + 1;
7461
7462           ResetGfxAnimation(x, y);
7463           ResetGfxAnimation(x, y + 1);
7464         }
7465
7466         if (MovDelay[x][y])
7467         {
7468           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7469           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7470
7471           MovDelay[x][y]--;
7472           if (MovDelay[x][y])
7473             return;
7474         }
7475
7476         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7477         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7478         Store[x][y + 1] = Store[x][y];
7479         Store[x][y] = 0;
7480
7481         PlayLevelSoundAction(x, y, ACTION_FILLING);
7482       }
7483     }
7484     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7485              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7486     {
7487       InitMovingField(x, y, MV_DOWN);
7488       started_moving = TRUE;
7489
7490       Feld[x][y] = EL_QUICKSAND_FILLING;
7491       Store[x][y] = element;
7492
7493       PlayLevelSoundAction(x, y, ACTION_FILLING);
7494     }
7495     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7496              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7497     {
7498       InitMovingField(x, y, MV_DOWN);
7499       started_moving = TRUE;
7500
7501       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7502       Store[x][y] = element;
7503
7504       PlayLevelSoundAction(x, y, ACTION_FILLING);
7505     }
7506     else if (element == EL_MAGIC_WALL_FULL)
7507     {
7508       if (IS_FREE(x, y + 1))
7509       {
7510         InitMovingField(x, y, MV_DOWN);
7511         started_moving = TRUE;
7512
7513         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7514         Store[x][y] = EL_CHANGED(Store[x][y]);
7515       }
7516       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7517       {
7518         if (!MovDelay[x][y])
7519           MovDelay[x][y] = TILEY / 4 + 1;
7520
7521         if (MovDelay[x][y])
7522         {
7523           MovDelay[x][y]--;
7524           if (MovDelay[x][y])
7525             return;
7526         }
7527
7528         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7529         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7530         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7531         Store[x][y] = 0;
7532       }
7533     }
7534     else if (element == EL_BD_MAGIC_WALL_FULL)
7535     {
7536       if (IS_FREE(x, y + 1))
7537       {
7538         InitMovingField(x, y, MV_DOWN);
7539         started_moving = TRUE;
7540
7541         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7542         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7543       }
7544       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7545       {
7546         if (!MovDelay[x][y])
7547           MovDelay[x][y] = TILEY / 4 + 1;
7548
7549         if (MovDelay[x][y])
7550         {
7551           MovDelay[x][y]--;
7552           if (MovDelay[x][y])
7553             return;
7554         }
7555
7556         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7557         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7558         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7559         Store[x][y] = 0;
7560       }
7561     }
7562     else if (element == EL_DC_MAGIC_WALL_FULL)
7563     {
7564       if (IS_FREE(x, y + 1))
7565       {
7566         InitMovingField(x, y, MV_DOWN);
7567         started_moving = TRUE;
7568
7569         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7570         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7571       }
7572       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7573       {
7574         if (!MovDelay[x][y])
7575           MovDelay[x][y] = TILEY / 4 + 1;
7576
7577         if (MovDelay[x][y])
7578         {
7579           MovDelay[x][y]--;
7580           if (MovDelay[x][y])
7581             return;
7582         }
7583
7584         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7585         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7586         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7587         Store[x][y] = 0;
7588       }
7589     }
7590     else if ((CAN_PASS_MAGIC_WALL(element) &&
7591               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7592                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7593              (CAN_PASS_DC_MAGIC_WALL(element) &&
7594               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7595
7596     {
7597       InitMovingField(x, y, MV_DOWN);
7598       started_moving = TRUE;
7599
7600       Feld[x][y] =
7601         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7602          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7603          EL_DC_MAGIC_WALL_FILLING);
7604       Store[x][y] = element;
7605     }
7606     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7607     {
7608       SplashAcid(x, y + 1);
7609
7610       InitMovingField(x, y, MV_DOWN);
7611       started_moving = TRUE;
7612
7613       Store[x][y] = EL_ACID;
7614     }
7615     else if (
7616              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7617               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7618              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7619               CAN_FALL(element) && WasJustFalling[x][y] &&
7620               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7621
7622              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7623               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7624               (Feld[x][y + 1] == EL_BLOCKED)))
7625     {
7626       /* this is needed for a special case not covered by calling "Impact()"
7627          from "ContinueMoving()": if an element moves to a tile directly below
7628          another element which was just falling on that tile (which was empty
7629          in the previous frame), the falling element above would just stop
7630          instead of smashing the element below (in previous version, the above
7631          element was just checked for "moving" instead of "falling", resulting
7632          in incorrect smashes caused by horizontal movement of the above
7633          element; also, the case of the player being the element to smash was
7634          simply not covered here... :-/ ) */
7635
7636       CheckCollision[x][y] = 0;
7637       CheckImpact[x][y] = 0;
7638
7639       Impact(x, y);
7640     }
7641     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7642     {
7643       if (MovDir[x][y] == MV_NONE)
7644       {
7645         InitMovingField(x, y, MV_DOWN);
7646         started_moving = TRUE;
7647       }
7648     }
7649     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7650     {
7651       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7652         MovDir[x][y] = MV_DOWN;
7653
7654       InitMovingField(x, y, MV_DOWN);
7655       started_moving = TRUE;
7656     }
7657     else if (element == EL_AMOEBA_DROP)
7658     {
7659       Feld[x][y] = EL_AMOEBA_GROWING;
7660       Store[x][y] = EL_AMOEBA_WET;
7661     }
7662     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7663               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7664              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7665              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7666     {
7667       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7668                                 (IS_FREE(x - 1, y + 1) ||
7669                                  Feld[x - 1][y + 1] == EL_ACID));
7670       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7671                                 (IS_FREE(x + 1, y + 1) ||
7672                                  Feld[x + 1][y + 1] == EL_ACID));
7673       boolean can_fall_any  = (can_fall_left || can_fall_right);
7674       boolean can_fall_both = (can_fall_left && can_fall_right);
7675       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7676
7677       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7678       {
7679         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7680           can_fall_right = FALSE;
7681         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7682           can_fall_left = FALSE;
7683         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7684           can_fall_right = FALSE;
7685         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7686           can_fall_left = FALSE;
7687
7688         can_fall_any  = (can_fall_left || can_fall_right);
7689         can_fall_both = FALSE;
7690       }
7691
7692       if (can_fall_both)
7693       {
7694         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7695           can_fall_right = FALSE;       /* slip down on left side */
7696         else
7697           can_fall_left = !(can_fall_right = RND(2));
7698
7699         can_fall_both = FALSE;
7700       }
7701
7702       if (can_fall_any)
7703       {
7704         /* if not determined otherwise, prefer left side for slipping down */
7705         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7706         started_moving = TRUE;
7707       }
7708     }
7709     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7710     {
7711       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7712       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7713       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7714       int belt_dir = game.belt_dir[belt_nr];
7715
7716       if ((belt_dir == MV_LEFT  && left_is_free) ||
7717           (belt_dir == MV_RIGHT && right_is_free))
7718       {
7719         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7720
7721         InitMovingField(x, y, belt_dir);
7722         started_moving = TRUE;
7723
7724         Pushed[x][y] = TRUE;
7725         Pushed[nextx][y] = TRUE;
7726
7727         GfxAction[x][y] = ACTION_DEFAULT;
7728       }
7729       else
7730       {
7731         MovDir[x][y] = 0;       /* if element was moving, stop it */
7732       }
7733     }
7734   }
7735
7736   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7737   if (CAN_MOVE(element) && !started_moving)
7738   {
7739     int move_pattern = element_info[element].move_pattern;
7740     int newx, newy;
7741
7742     Moving2Blocked(x, y, &newx, &newy);
7743
7744     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7745       return;
7746
7747     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7748         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7749     {
7750       WasJustMoving[x][y] = 0;
7751       CheckCollision[x][y] = 0;
7752
7753       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7754
7755       if (Feld[x][y] != element)        /* element has changed */
7756         return;
7757     }
7758
7759     if (!MovDelay[x][y])        /* start new movement phase */
7760     {
7761       /* all objects that can change their move direction after each step
7762          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7763
7764       if (element != EL_YAMYAM &&
7765           element != EL_DARK_YAMYAM &&
7766           element != EL_PACMAN &&
7767           !(move_pattern & MV_ANY_DIRECTION) &&
7768           move_pattern != MV_TURNING_LEFT &&
7769           move_pattern != MV_TURNING_RIGHT &&
7770           move_pattern != MV_TURNING_LEFT_RIGHT &&
7771           move_pattern != MV_TURNING_RIGHT_LEFT &&
7772           move_pattern != MV_TURNING_RANDOM)
7773       {
7774         TurnRound(x, y);
7775
7776         if (MovDelay[x][y] && (element == EL_BUG ||
7777                                element == EL_SPACESHIP ||
7778                                element == EL_SP_SNIKSNAK ||
7779                                element == EL_SP_ELECTRON ||
7780                                element == EL_MOLE))
7781           TEST_DrawLevelField(x, y);
7782       }
7783     }
7784
7785     if (MovDelay[x][y])         /* wait some time before next movement */
7786     {
7787       MovDelay[x][y]--;
7788
7789       if (element == EL_ROBOT ||
7790           element == EL_YAMYAM ||
7791           element == EL_DARK_YAMYAM)
7792       {
7793         DrawLevelElementAnimationIfNeeded(x, y, element);
7794         PlayLevelSoundAction(x, y, ACTION_WAITING);
7795       }
7796       else if (element == EL_SP_ELECTRON)
7797         DrawLevelElementAnimationIfNeeded(x, y, element);
7798       else if (element == EL_DRAGON)
7799       {
7800         int i;
7801         int dir = MovDir[x][y];
7802         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7803         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7804         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7805                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7806                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7807                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7808         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7809
7810         GfxAction[x][y] = ACTION_ATTACKING;
7811
7812         if (IS_PLAYER(x, y))
7813           DrawPlayerField(x, y);
7814         else
7815           TEST_DrawLevelField(x, y);
7816
7817         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7818
7819         for (i = 1; i <= 3; i++)
7820         {
7821           int xx = x + i * dx;
7822           int yy = y + i * dy;
7823           int sx = SCREENX(xx);
7824           int sy = SCREENY(yy);
7825           int flame_graphic = graphic + (i - 1);
7826
7827           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7828             break;
7829
7830           if (MovDelay[x][y])
7831           {
7832             int flamed = MovingOrBlocked2Element(xx, yy);
7833
7834             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7835               Bang(xx, yy);
7836             else
7837               RemoveMovingField(xx, yy);
7838
7839             ChangeDelay[xx][yy] = 0;
7840
7841             Feld[xx][yy] = EL_FLAMES;
7842
7843             if (IN_SCR_FIELD(sx, sy))
7844             {
7845               TEST_DrawLevelFieldCrumbled(xx, yy);
7846               DrawGraphic(sx, sy, flame_graphic, frame);
7847             }
7848           }
7849           else
7850           {
7851             if (Feld[xx][yy] == EL_FLAMES)
7852               Feld[xx][yy] = EL_EMPTY;
7853             TEST_DrawLevelField(xx, yy);
7854           }
7855         }
7856       }
7857
7858       if (MovDelay[x][y])       /* element still has to wait some time */
7859       {
7860         PlayLevelSoundAction(x, y, ACTION_WAITING);
7861
7862         return;
7863       }
7864     }
7865
7866     /* now make next step */
7867
7868     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7869
7870     if (DONT_COLLIDE_WITH(element) &&
7871         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7872         !PLAYER_ENEMY_PROTECTED(newx, newy))
7873     {
7874       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7875
7876       return;
7877     }
7878
7879     else if (CAN_MOVE_INTO_ACID(element) &&
7880              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7881              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7882              (MovDir[x][y] == MV_DOWN ||
7883               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7884     {
7885       SplashAcid(newx, newy);
7886       Store[x][y] = EL_ACID;
7887     }
7888     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7889     {
7890       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7891           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7892           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7893           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7894       {
7895         RemoveField(x, y);
7896         TEST_DrawLevelField(x, y);
7897
7898         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7899         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7900           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7901
7902         local_player->friends_still_needed--;
7903         if (!local_player->friends_still_needed &&
7904             !local_player->GameOver && AllPlayersGone)
7905           PlayerWins(local_player);
7906
7907         return;
7908       }
7909       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7910       {
7911         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7912           TEST_DrawLevelField(newx, newy);
7913         else
7914           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7915       }
7916       else if (!IS_FREE(newx, newy))
7917       {
7918         GfxAction[x][y] = ACTION_WAITING;
7919
7920         if (IS_PLAYER(x, y))
7921           DrawPlayerField(x, y);
7922         else
7923           TEST_DrawLevelField(x, y);
7924
7925         return;
7926       }
7927     }
7928     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7929     {
7930       if (IS_FOOD_PIG(Feld[newx][newy]))
7931       {
7932         if (IS_MOVING(newx, newy))
7933           RemoveMovingField(newx, newy);
7934         else
7935         {
7936           Feld[newx][newy] = EL_EMPTY;
7937           TEST_DrawLevelField(newx, newy);
7938         }
7939
7940         PlayLevelSound(x, y, SND_PIG_DIGGING);
7941       }
7942       else if (!IS_FREE(newx, newy))
7943       {
7944         if (IS_PLAYER(x, y))
7945           DrawPlayerField(x, y);
7946         else
7947           TEST_DrawLevelField(x, y);
7948
7949         return;
7950       }
7951     }
7952     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7953     {
7954       if (Store[x][y] != EL_EMPTY)
7955       {
7956         boolean can_clone = FALSE;
7957         int xx, yy;
7958
7959         /* check if element to clone is still there */
7960         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7961         {
7962           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7963           {
7964             can_clone = TRUE;
7965
7966             break;
7967           }
7968         }
7969
7970         /* cannot clone or target field not free anymore -- do not clone */
7971         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7972           Store[x][y] = EL_EMPTY;
7973       }
7974
7975       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7976       {
7977         if (IS_MV_DIAGONAL(MovDir[x][y]))
7978         {
7979           int diagonal_move_dir = MovDir[x][y];
7980           int stored = Store[x][y];
7981           int change_delay = 8;
7982           int graphic;
7983
7984           /* android is moving diagonally */
7985
7986           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7987
7988           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7989           GfxElement[x][y] = EL_EMC_ANDROID;
7990           GfxAction[x][y] = ACTION_SHRINKING;
7991           GfxDir[x][y] = diagonal_move_dir;
7992           ChangeDelay[x][y] = change_delay;
7993
7994           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7995                                    GfxDir[x][y]);
7996
7997           DrawLevelGraphicAnimation(x, y, graphic);
7998           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7999
8000           if (Feld[newx][newy] == EL_ACID)
8001           {
8002             SplashAcid(newx, newy);
8003
8004             return;
8005           }
8006
8007           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8008
8009           Store[newx][newy] = EL_EMC_ANDROID;
8010           GfxElement[newx][newy] = EL_EMC_ANDROID;
8011           GfxAction[newx][newy] = ACTION_GROWING;
8012           GfxDir[newx][newy] = diagonal_move_dir;
8013           ChangeDelay[newx][newy] = change_delay;
8014
8015           graphic = el_act_dir2img(GfxElement[newx][newy],
8016                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8017
8018           DrawLevelGraphicAnimation(newx, newy, graphic);
8019           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8020
8021           return;
8022         }
8023         else
8024         {
8025           Feld[newx][newy] = EL_EMPTY;
8026           TEST_DrawLevelField(newx, newy);
8027
8028           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8029         }
8030       }
8031       else if (!IS_FREE(newx, newy))
8032       {
8033         return;
8034       }
8035     }
8036     else if (IS_CUSTOM_ELEMENT(element) &&
8037              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8038     {
8039       if (!DigFieldByCE(newx, newy, element))
8040         return;
8041
8042       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8043       {
8044         RunnerVisit[x][y] = FrameCounter;
8045         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8046       }
8047     }
8048     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8049     {
8050       if (!IS_FREE(newx, newy))
8051       {
8052         if (IS_PLAYER(x, y))
8053           DrawPlayerField(x, y);
8054         else
8055           TEST_DrawLevelField(x, y);
8056
8057         return;
8058       }
8059       else
8060       {
8061         boolean wanna_flame = !RND(10);
8062         int dx = newx - x, dy = newy - y;
8063         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8064         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8065         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8066                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8067         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8068                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8069
8070         if ((wanna_flame ||
8071              IS_CLASSIC_ENEMY(element1) ||
8072              IS_CLASSIC_ENEMY(element2)) &&
8073             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8074             element1 != EL_FLAMES && element2 != EL_FLAMES)
8075         {
8076           ResetGfxAnimation(x, y);
8077           GfxAction[x][y] = ACTION_ATTACKING;
8078
8079           if (IS_PLAYER(x, y))
8080             DrawPlayerField(x, y);
8081           else
8082             TEST_DrawLevelField(x, y);
8083
8084           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8085
8086           MovDelay[x][y] = 50;
8087
8088           Feld[newx][newy] = EL_FLAMES;
8089           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8090             Feld[newx1][newy1] = EL_FLAMES;
8091           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8092             Feld[newx2][newy2] = EL_FLAMES;
8093
8094           return;
8095         }
8096       }
8097     }
8098     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8099              Feld[newx][newy] == EL_DIAMOND)
8100     {
8101       if (IS_MOVING(newx, newy))
8102         RemoveMovingField(newx, newy);
8103       else
8104       {
8105         Feld[newx][newy] = EL_EMPTY;
8106         TEST_DrawLevelField(newx, newy);
8107       }
8108
8109       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8110     }
8111     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8112              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8113     {
8114       if (AmoebaNr[newx][newy])
8115       {
8116         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8117         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8118             Feld[newx][newy] == EL_BD_AMOEBA)
8119           AmoebaCnt[AmoebaNr[newx][newy]]--;
8120       }
8121
8122       if (IS_MOVING(newx, newy))
8123       {
8124         RemoveMovingField(newx, newy);
8125       }
8126       else
8127       {
8128         Feld[newx][newy] = EL_EMPTY;
8129         TEST_DrawLevelField(newx, newy);
8130       }
8131
8132       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8133     }
8134     else if ((element == EL_PACMAN || element == EL_MOLE)
8135              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8136     {
8137       if (AmoebaNr[newx][newy])
8138       {
8139         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141             Feld[newx][newy] == EL_BD_AMOEBA)
8142           AmoebaCnt[AmoebaNr[newx][newy]]--;
8143       }
8144
8145       if (element == EL_MOLE)
8146       {
8147         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8148         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8149
8150         ResetGfxAnimation(x, y);
8151         GfxAction[x][y] = ACTION_DIGGING;
8152         TEST_DrawLevelField(x, y);
8153
8154         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8155
8156         return;                         /* wait for shrinking amoeba */
8157       }
8158       else      /* element == EL_PACMAN */
8159       {
8160         Feld[newx][newy] = EL_EMPTY;
8161         TEST_DrawLevelField(newx, newy);
8162         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8163       }
8164     }
8165     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8166              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8167               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8168     {
8169       /* wait for shrinking amoeba to completely disappear */
8170       return;
8171     }
8172     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8173     {
8174       /* object was running against a wall */
8175
8176       TurnRound(x, y);
8177
8178       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8179         DrawLevelElementAnimation(x, y, element);
8180
8181       if (DONT_TOUCH(element))
8182         TestIfBadThingTouchesPlayer(x, y);
8183
8184       return;
8185     }
8186
8187     InitMovingField(x, y, MovDir[x][y]);
8188
8189     PlayLevelSoundAction(x, y, ACTION_MOVING);
8190   }
8191
8192   if (MovDir[x][y])
8193     ContinueMoving(x, y);
8194 }
8195
8196 void ContinueMoving(int x, int y)
8197 {
8198   int element = Feld[x][y];
8199   struct ElementInfo *ei = &element_info[element];
8200   int direction = MovDir[x][y];
8201   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8202   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8203   int newx = x + dx, newy = y + dy;
8204   int stored = Store[x][y];
8205   int stored_new = Store[newx][newy];
8206   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8207   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8208   boolean last_line = (newy == lev_fieldy - 1);
8209
8210   MovPos[x][y] += getElementMoveStepsize(x, y);
8211
8212   if (pushed_by_player) /* special case: moving object pushed by player */
8213     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8214
8215   if (ABS(MovPos[x][y]) < TILEX)
8216   {
8217     TEST_DrawLevelField(x, y);
8218
8219     return;     /* element is still moving */
8220   }
8221
8222   /* element reached destination field */
8223
8224   Feld[x][y] = EL_EMPTY;
8225   Feld[newx][newy] = element;
8226   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8227
8228   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8229   {
8230     element = Feld[newx][newy] = EL_ACID;
8231   }
8232   else if (element == EL_MOLE)
8233   {
8234     Feld[x][y] = EL_SAND;
8235
8236     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8237   }
8238   else if (element == EL_QUICKSAND_FILLING)
8239   {
8240     element = Feld[newx][newy] = get_next_element(element);
8241     Store[newx][newy] = Store[x][y];
8242   }
8243   else if (element == EL_QUICKSAND_EMPTYING)
8244   {
8245     Feld[x][y] = get_next_element(element);
8246     element = Feld[newx][newy] = Store[x][y];
8247   }
8248   else if (element == EL_QUICKSAND_FAST_FILLING)
8249   {
8250     element = Feld[newx][newy] = get_next_element(element);
8251     Store[newx][newy] = Store[x][y];
8252   }
8253   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8254   {
8255     Feld[x][y] = get_next_element(element);
8256     element = Feld[newx][newy] = Store[x][y];
8257   }
8258   else if (element == EL_MAGIC_WALL_FILLING)
8259   {
8260     element = Feld[newx][newy] = get_next_element(element);
8261     if (!game.magic_wall_active)
8262       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8263     Store[newx][newy] = Store[x][y];
8264   }
8265   else if (element == EL_MAGIC_WALL_EMPTYING)
8266   {
8267     Feld[x][y] = get_next_element(element);
8268     if (!game.magic_wall_active)
8269       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8270     element = Feld[newx][newy] = Store[x][y];
8271
8272     InitField(newx, newy, FALSE);
8273   }
8274   else if (element == EL_BD_MAGIC_WALL_FILLING)
8275   {
8276     element = Feld[newx][newy] = get_next_element(element);
8277     if (!game.magic_wall_active)
8278       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8279     Store[newx][newy] = Store[x][y];
8280   }
8281   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8282   {
8283     Feld[x][y] = get_next_element(element);
8284     if (!game.magic_wall_active)
8285       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8286     element = Feld[newx][newy] = Store[x][y];
8287
8288     InitField(newx, newy, FALSE);
8289   }
8290   else if (element == EL_DC_MAGIC_WALL_FILLING)
8291   {
8292     element = Feld[newx][newy] = get_next_element(element);
8293     if (!game.magic_wall_active)
8294       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8295     Store[newx][newy] = Store[x][y];
8296   }
8297   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8298   {
8299     Feld[x][y] = get_next_element(element);
8300     if (!game.magic_wall_active)
8301       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8302     element = Feld[newx][newy] = Store[x][y];
8303
8304     InitField(newx, newy, FALSE);
8305   }
8306   else if (element == EL_AMOEBA_DROPPING)
8307   {
8308     Feld[x][y] = get_next_element(element);
8309     element = Feld[newx][newy] = Store[x][y];
8310   }
8311   else if (element == EL_SOKOBAN_OBJECT)
8312   {
8313     if (Back[x][y])
8314       Feld[x][y] = Back[x][y];
8315
8316     if (Back[newx][newy])
8317       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8318
8319     Back[x][y] = Back[newx][newy] = 0;
8320   }
8321
8322   Store[x][y] = EL_EMPTY;
8323   MovPos[x][y] = 0;
8324   MovDir[x][y] = 0;
8325   MovDelay[x][y] = 0;
8326
8327   MovDelay[newx][newy] = 0;
8328
8329   if (CAN_CHANGE_OR_HAS_ACTION(element))
8330   {
8331     /* copy element change control values to new field */
8332     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8333     ChangePage[newx][newy]  = ChangePage[x][y];
8334     ChangeCount[newx][newy] = ChangeCount[x][y];
8335     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8336   }
8337
8338   CustomValue[newx][newy] = CustomValue[x][y];
8339
8340   ChangeDelay[x][y] = 0;
8341   ChangePage[x][y] = -1;
8342   ChangeCount[x][y] = 0;
8343   ChangeEvent[x][y] = -1;
8344
8345   CustomValue[x][y] = 0;
8346
8347   /* copy animation control values to new field */
8348   GfxFrame[newx][newy]  = GfxFrame[x][y];
8349   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8350   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8351   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8352
8353   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8354
8355   /* some elements can leave other elements behind after moving */
8356   if (ei->move_leave_element != EL_EMPTY &&
8357       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8358       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8359   {
8360     int move_leave_element = ei->move_leave_element;
8361
8362     /* this makes it possible to leave the removed element again */
8363     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8364       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8365
8366     Feld[x][y] = move_leave_element;
8367
8368     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8369       MovDir[x][y] = direction;
8370
8371     InitField(x, y, FALSE);
8372
8373     if (GFX_CRUMBLED(Feld[x][y]))
8374       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8375
8376     if (ELEM_IS_PLAYER(move_leave_element))
8377       RelocatePlayer(x, y, move_leave_element);
8378   }
8379
8380   /* do this after checking for left-behind element */
8381   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8382
8383   if (!CAN_MOVE(element) ||
8384       (CAN_FALL(element) && direction == MV_DOWN &&
8385        (element == EL_SPRING ||
8386         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8387         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8388     GfxDir[x][y] = MovDir[newx][newy] = 0;
8389
8390   TEST_DrawLevelField(x, y);
8391   TEST_DrawLevelField(newx, newy);
8392
8393   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8394
8395   /* prevent pushed element from moving on in pushed direction */
8396   if (pushed_by_player && CAN_MOVE(element) &&
8397       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8398       !(element_info[element].move_pattern & direction))
8399     TurnRound(newx, newy);
8400
8401   /* prevent elements on conveyor belt from moving on in last direction */
8402   if (pushed_by_conveyor && CAN_FALL(element) &&
8403       direction & MV_HORIZONTAL)
8404     MovDir[newx][newy] = 0;
8405
8406   if (!pushed_by_player)
8407   {
8408     int nextx = newx + dx, nexty = newy + dy;
8409     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8410
8411     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8412
8413     if (CAN_FALL(element) && direction == MV_DOWN)
8414       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8415
8416     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8417       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8418
8419     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8420       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8421   }
8422
8423   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8424   {
8425     TestIfBadThingTouchesPlayer(newx, newy);
8426     TestIfBadThingTouchesFriend(newx, newy);
8427
8428     if (!IS_CUSTOM_ELEMENT(element))
8429       TestIfBadThingTouchesOtherBadThing(newx, newy);
8430   }
8431   else if (element == EL_PENGUIN)
8432     TestIfFriendTouchesBadThing(newx, newy);
8433
8434   if (DONT_GET_HIT_BY(element))
8435   {
8436     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8437   }
8438
8439   /* give the player one last chance (one more frame) to move away */
8440   if (CAN_FALL(element) && direction == MV_DOWN &&
8441       (last_line || (!IS_FREE(x, newy + 1) &&
8442                      (!IS_PLAYER(x, newy + 1) ||
8443                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8444     Impact(x, newy);
8445
8446   if (pushed_by_player && !game.use_change_when_pushing_bug)
8447   {
8448     int push_side = MV_DIR_OPPOSITE(direction);
8449     struct PlayerInfo *player = PLAYERINFO(x, y);
8450
8451     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8452                                player->index_bit, push_side);
8453     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8454                                         player->index_bit, push_side);
8455   }
8456
8457   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8458     MovDelay[newx][newy] = 1;
8459
8460   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8461
8462   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8463   TestIfElementHitsCustomElement(newx, newy, direction);
8464   TestIfPlayerTouchesCustomElement(newx, newy);
8465   TestIfElementTouchesCustomElement(newx, newy);
8466
8467   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8468       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8469     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8470                              MV_DIR_OPPOSITE(direction));
8471 }
8472
8473 int AmoebeNachbarNr(int ax, int ay)
8474 {
8475   int i;
8476   int element = Feld[ax][ay];
8477   int group_nr = 0;
8478   static int xy[4][2] =
8479   {
8480     { 0, -1 },
8481     { -1, 0 },
8482     { +1, 0 },
8483     { 0, +1 }
8484   };
8485
8486   for (i = 0; i < NUM_DIRECTIONS; i++)
8487   {
8488     int x = ax + xy[i][0];
8489     int y = ay + xy[i][1];
8490
8491     if (!IN_LEV_FIELD(x, y))
8492       continue;
8493
8494     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8495       group_nr = AmoebaNr[x][y];
8496   }
8497
8498   return group_nr;
8499 }
8500
8501 void AmoebenVereinigen(int ax, int ay)
8502 {
8503   int i, x, y, xx, yy;
8504   int new_group_nr = AmoebaNr[ax][ay];
8505   static int xy[4][2] =
8506   {
8507     { 0, -1 },
8508     { -1, 0 },
8509     { +1, 0 },
8510     { 0, +1 }
8511   };
8512
8513   if (new_group_nr == 0)
8514     return;
8515
8516   for (i = 0; i < NUM_DIRECTIONS; i++)
8517   {
8518     x = ax + xy[i][0];
8519     y = ay + xy[i][1];
8520
8521     if (!IN_LEV_FIELD(x, y))
8522       continue;
8523
8524     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8525          Feld[x][y] == EL_BD_AMOEBA ||
8526          Feld[x][y] == EL_AMOEBA_DEAD) &&
8527         AmoebaNr[x][y] != new_group_nr)
8528     {
8529       int old_group_nr = AmoebaNr[x][y];
8530
8531       if (old_group_nr == 0)
8532         return;
8533
8534       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8535       AmoebaCnt[old_group_nr] = 0;
8536       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8537       AmoebaCnt2[old_group_nr] = 0;
8538
8539       SCAN_PLAYFIELD(xx, yy)
8540       {
8541         if (AmoebaNr[xx][yy] == old_group_nr)
8542           AmoebaNr[xx][yy] = new_group_nr;
8543       }
8544     }
8545   }
8546 }
8547
8548 void AmoebeUmwandeln(int ax, int ay)
8549 {
8550   int i, x, y;
8551
8552   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8553   {
8554     int group_nr = AmoebaNr[ax][ay];
8555
8556 #ifdef DEBUG
8557     if (group_nr == 0)
8558     {
8559       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8560       printf("AmoebeUmwandeln(): This should never happen!\n");
8561       return;
8562     }
8563 #endif
8564
8565     SCAN_PLAYFIELD(x, y)
8566     {
8567       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8568       {
8569         AmoebaNr[x][y] = 0;
8570         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8571       }
8572     }
8573
8574     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8575                             SND_AMOEBA_TURNING_TO_GEM :
8576                             SND_AMOEBA_TURNING_TO_ROCK));
8577     Bang(ax, ay);
8578   }
8579   else
8580   {
8581     static int xy[4][2] =
8582     {
8583       { 0, -1 },
8584       { -1, 0 },
8585       { +1, 0 },
8586       { 0, +1 }
8587     };
8588
8589     for (i = 0; i < NUM_DIRECTIONS; i++)
8590     {
8591       x = ax + xy[i][0];
8592       y = ay + xy[i][1];
8593
8594       if (!IN_LEV_FIELD(x, y))
8595         continue;
8596
8597       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8598       {
8599         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8600                               SND_AMOEBA_TURNING_TO_GEM :
8601                               SND_AMOEBA_TURNING_TO_ROCK));
8602         Bang(x, y);
8603       }
8604     }
8605   }
8606 }
8607
8608 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8609 {
8610   int x, y;
8611   int group_nr = AmoebaNr[ax][ay];
8612   boolean done = FALSE;
8613
8614 #ifdef DEBUG
8615   if (group_nr == 0)
8616   {
8617     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8618     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8619     return;
8620   }
8621 #endif
8622
8623   SCAN_PLAYFIELD(x, y)
8624   {
8625     if (AmoebaNr[x][y] == group_nr &&
8626         (Feld[x][y] == EL_AMOEBA_DEAD ||
8627          Feld[x][y] == EL_BD_AMOEBA ||
8628          Feld[x][y] == EL_AMOEBA_GROWING))
8629     {
8630       AmoebaNr[x][y] = 0;
8631       Feld[x][y] = new_element;
8632       InitField(x, y, FALSE);
8633       TEST_DrawLevelField(x, y);
8634       done = TRUE;
8635     }
8636   }
8637
8638   if (done)
8639     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8640                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8641                             SND_BD_AMOEBA_TURNING_TO_GEM));
8642 }
8643
8644 void AmoebeWaechst(int x, int y)
8645 {
8646   static unsigned int sound_delay = 0;
8647   static unsigned int sound_delay_value = 0;
8648
8649   if (!MovDelay[x][y])          /* start new growing cycle */
8650   {
8651     MovDelay[x][y] = 7;
8652
8653     if (DelayReached(&sound_delay, sound_delay_value))
8654     {
8655       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8656       sound_delay_value = 30;
8657     }
8658   }
8659
8660   if (MovDelay[x][y])           /* wait some time before growing bigger */
8661   {
8662     MovDelay[x][y]--;
8663     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8664     {
8665       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8666                                            6 - MovDelay[x][y]);
8667
8668       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8669     }
8670
8671     if (!MovDelay[x][y])
8672     {
8673       Feld[x][y] = Store[x][y];
8674       Store[x][y] = 0;
8675       TEST_DrawLevelField(x, y);
8676     }
8677   }
8678 }
8679
8680 void AmoebaDisappearing(int x, int y)
8681 {
8682   static unsigned int sound_delay = 0;
8683   static unsigned int sound_delay_value = 0;
8684
8685   if (!MovDelay[x][y])          /* start new shrinking cycle */
8686   {
8687     MovDelay[x][y] = 7;
8688
8689     if (DelayReached(&sound_delay, sound_delay_value))
8690       sound_delay_value = 30;
8691   }
8692
8693   if (MovDelay[x][y])           /* wait some time before shrinking */
8694   {
8695     MovDelay[x][y]--;
8696     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8697     {
8698       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8699                                            6 - MovDelay[x][y]);
8700
8701       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8702     }
8703
8704     if (!MovDelay[x][y])
8705     {
8706       Feld[x][y] = EL_EMPTY;
8707       TEST_DrawLevelField(x, y);
8708
8709       /* don't let mole enter this field in this cycle;
8710          (give priority to objects falling to this field from above) */
8711       Stop[x][y] = TRUE;
8712     }
8713   }
8714 }
8715
8716 void AmoebeAbleger(int ax, int ay)
8717 {
8718   int i;
8719   int element = Feld[ax][ay];
8720   int graphic = el2img(element);
8721   int newax = ax, neway = ay;
8722   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8723   static int xy[4][2] =
8724   {
8725     { 0, -1 },
8726     { -1, 0 },
8727     { +1, 0 },
8728     { 0, +1 }
8729   };
8730
8731   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8732   {
8733     Feld[ax][ay] = EL_AMOEBA_DEAD;
8734     TEST_DrawLevelField(ax, ay);
8735     return;
8736   }
8737
8738   if (IS_ANIMATED(graphic))
8739     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8740
8741   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8742     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8743
8744   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8745   {
8746     MovDelay[ax][ay]--;
8747     if (MovDelay[ax][ay])
8748       return;
8749   }
8750
8751   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8752   {
8753     int start = RND(4);
8754     int x = ax + xy[start][0];
8755     int y = ay + xy[start][1];
8756
8757     if (!IN_LEV_FIELD(x, y))
8758       return;
8759
8760     if (IS_FREE(x, y) ||
8761         CAN_GROW_INTO(Feld[x][y]) ||
8762         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8763         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8764     {
8765       newax = x;
8766       neway = y;
8767     }
8768
8769     if (newax == ax && neway == ay)
8770       return;
8771   }
8772   else                          /* normal or "filled" (BD style) amoeba */
8773   {
8774     int start = RND(4);
8775     boolean waiting_for_player = FALSE;
8776
8777     for (i = 0; i < NUM_DIRECTIONS; i++)
8778     {
8779       int j = (start + i) % 4;
8780       int x = ax + xy[j][0];
8781       int y = ay + xy[j][1];
8782
8783       if (!IN_LEV_FIELD(x, y))
8784         continue;
8785
8786       if (IS_FREE(x, y) ||
8787           CAN_GROW_INTO(Feld[x][y]) ||
8788           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8789           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8790       {
8791         newax = x;
8792         neway = y;
8793         break;
8794       }
8795       else if (IS_PLAYER(x, y))
8796         waiting_for_player = TRUE;
8797     }
8798
8799     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8800     {
8801       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8802       {
8803         Feld[ax][ay] = EL_AMOEBA_DEAD;
8804         TEST_DrawLevelField(ax, ay);
8805         AmoebaCnt[AmoebaNr[ax][ay]]--;
8806
8807         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8808         {
8809           if (element == EL_AMOEBA_FULL)
8810             AmoebeUmwandeln(ax, ay);
8811           else if (element == EL_BD_AMOEBA)
8812             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8813         }
8814       }
8815       return;
8816     }
8817     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8818     {
8819       /* amoeba gets larger by growing in some direction */
8820
8821       int new_group_nr = AmoebaNr[ax][ay];
8822
8823 #ifdef DEBUG
8824   if (new_group_nr == 0)
8825   {
8826     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8827     printf("AmoebeAbleger(): This should never happen!\n");
8828     return;
8829   }
8830 #endif
8831
8832       AmoebaNr[newax][neway] = new_group_nr;
8833       AmoebaCnt[new_group_nr]++;
8834       AmoebaCnt2[new_group_nr]++;
8835
8836       /* if amoeba touches other amoeba(s) after growing, unify them */
8837       AmoebenVereinigen(newax, neway);
8838
8839       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8840       {
8841         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8842         return;
8843       }
8844     }
8845   }
8846
8847   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8848       (neway == lev_fieldy - 1 && newax != ax))
8849   {
8850     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8851     Store[newax][neway] = element;
8852   }
8853   else if (neway == ay || element == EL_EMC_DRIPPER)
8854   {
8855     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8856
8857     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8858   }
8859   else
8860   {
8861     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8862     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8863     Store[ax][ay] = EL_AMOEBA_DROP;
8864     ContinueMoving(ax, ay);
8865     return;
8866   }
8867
8868   TEST_DrawLevelField(newax, neway);
8869 }
8870
8871 void Life(int ax, int ay)
8872 {
8873   int x1, y1, x2, y2;
8874   int life_time = 40;
8875   int element = Feld[ax][ay];
8876   int graphic = el2img(element);
8877   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8878                          level.biomaze);
8879   boolean changed = FALSE;
8880
8881   if (IS_ANIMATED(graphic))
8882     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8883
8884   if (Stop[ax][ay])
8885     return;
8886
8887   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8888     MovDelay[ax][ay] = life_time;
8889
8890   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8891   {
8892     MovDelay[ax][ay]--;
8893     if (MovDelay[ax][ay])
8894       return;
8895   }
8896
8897   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8898   {
8899     int xx = ax+x1, yy = ay+y1;
8900     int nachbarn = 0;
8901
8902     if (!IN_LEV_FIELD(xx, yy))
8903       continue;
8904
8905     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8906     {
8907       int x = xx+x2, y = yy+y2;
8908
8909       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8910         continue;
8911
8912       if (((Feld[x][y] == element ||
8913             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8914            !Stop[x][y]) ||
8915           (IS_FREE(x, y) && Stop[x][y]))
8916         nachbarn++;
8917     }
8918
8919     if (xx == ax && yy == ay)           /* field in the middle */
8920     {
8921       if (nachbarn < life_parameter[0] ||
8922           nachbarn > life_parameter[1])
8923       {
8924         Feld[xx][yy] = EL_EMPTY;
8925         if (!Stop[xx][yy])
8926           TEST_DrawLevelField(xx, yy);
8927         Stop[xx][yy] = TRUE;
8928         changed = TRUE;
8929       }
8930     }
8931     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8932     {                                   /* free border field */
8933       if (nachbarn >= life_parameter[2] &&
8934           nachbarn <= life_parameter[3])
8935       {
8936         Feld[xx][yy] = element;
8937         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8938         if (!Stop[xx][yy])
8939           TEST_DrawLevelField(xx, yy);
8940         Stop[xx][yy] = TRUE;
8941         changed = TRUE;
8942       }
8943     }
8944   }
8945
8946   if (changed)
8947     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8948                    SND_GAME_OF_LIFE_GROWING);
8949 }
8950
8951 static void InitRobotWheel(int x, int y)
8952 {
8953   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8954 }
8955
8956 static void RunRobotWheel(int x, int y)
8957 {
8958   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8959 }
8960
8961 static void StopRobotWheel(int x, int y)
8962 {
8963   if (ZX == x && ZY == y)
8964   {
8965     ZX = ZY = -1;
8966
8967     game.robot_wheel_active = FALSE;
8968   }
8969 }
8970
8971 static void InitTimegateWheel(int x, int y)
8972 {
8973   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8974 }
8975
8976 static void RunTimegateWheel(int x, int y)
8977 {
8978   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8979 }
8980
8981 static void InitMagicBallDelay(int x, int y)
8982 {
8983   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8984 }
8985
8986 static void ActivateMagicBall(int bx, int by)
8987 {
8988   int x, y;
8989
8990   if (level.ball_random)
8991   {
8992     int pos_border = RND(8);    /* select one of the eight border elements */
8993     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8994     int xx = pos_content % 3;
8995     int yy = pos_content / 3;
8996
8997     x = bx - 1 + xx;
8998     y = by - 1 + yy;
8999
9000     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9001       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9002   }
9003   else
9004   {
9005     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9006     {
9007       int xx = x - bx + 1;
9008       int yy = y - by + 1;
9009
9010       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9011         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9012     }
9013   }
9014
9015   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9016 }
9017
9018 void CheckExit(int x, int y)
9019 {
9020   if (local_player->gems_still_needed > 0 ||
9021       local_player->sokobanfields_still_needed > 0 ||
9022       local_player->lights_still_needed > 0)
9023   {
9024     int element = Feld[x][y];
9025     int graphic = el2img(element);
9026
9027     if (IS_ANIMATED(graphic))
9028       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9029
9030     return;
9031   }
9032
9033   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9034     return;
9035
9036   Feld[x][y] = EL_EXIT_OPENING;
9037
9038   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9039 }
9040
9041 void CheckExitEM(int x, int y)
9042 {
9043   if (local_player->gems_still_needed > 0 ||
9044       local_player->sokobanfields_still_needed > 0 ||
9045       local_player->lights_still_needed > 0)
9046   {
9047     int element = Feld[x][y];
9048     int graphic = el2img(element);
9049
9050     if (IS_ANIMATED(graphic))
9051       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9052
9053     return;
9054   }
9055
9056   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9057     return;
9058
9059   Feld[x][y] = EL_EM_EXIT_OPENING;
9060
9061   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9062 }
9063
9064 void CheckExitSteel(int x, int y)
9065 {
9066   if (local_player->gems_still_needed > 0 ||
9067       local_player->sokobanfields_still_needed > 0 ||
9068       local_player->lights_still_needed > 0)
9069   {
9070     int element = Feld[x][y];
9071     int graphic = el2img(element);
9072
9073     if (IS_ANIMATED(graphic))
9074       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9075
9076     return;
9077   }
9078
9079   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9080     return;
9081
9082   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9083
9084   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9085 }
9086
9087 void CheckExitSteelEM(int x, int y)
9088 {
9089   if (local_player->gems_still_needed > 0 ||
9090       local_player->sokobanfields_still_needed > 0 ||
9091       local_player->lights_still_needed > 0)
9092   {
9093     int element = Feld[x][y];
9094     int graphic = el2img(element);
9095
9096     if (IS_ANIMATED(graphic))
9097       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9098
9099     return;
9100   }
9101
9102   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9103     return;
9104
9105   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9106
9107   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9108 }
9109
9110 void CheckExitSP(int x, int y)
9111 {
9112   if (local_player->gems_still_needed > 0)
9113   {
9114     int element = Feld[x][y];
9115     int graphic = el2img(element);
9116
9117     if (IS_ANIMATED(graphic))
9118       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119
9120     return;
9121   }
9122
9123   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9124     return;
9125
9126   Feld[x][y] = EL_SP_EXIT_OPENING;
9127
9128   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9129 }
9130
9131 static void CloseAllOpenTimegates()
9132 {
9133   int x, y;
9134
9135   SCAN_PLAYFIELD(x, y)
9136   {
9137     int element = Feld[x][y];
9138
9139     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9140     {
9141       Feld[x][y] = EL_TIMEGATE_CLOSING;
9142
9143       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9144     }
9145   }
9146 }
9147
9148 void DrawTwinkleOnField(int x, int y)
9149 {
9150   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9151     return;
9152
9153   if (Feld[x][y] == EL_BD_DIAMOND)
9154     return;
9155
9156   if (MovDelay[x][y] == 0)      /* next animation frame */
9157     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9158
9159   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9160   {
9161     MovDelay[x][y]--;
9162
9163     DrawLevelElementAnimation(x, y, Feld[x][y]);
9164
9165     if (MovDelay[x][y] != 0)
9166     {
9167       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9168                                            10 - MovDelay[x][y]);
9169
9170       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9171     }
9172   }
9173 }
9174
9175 void MauerWaechst(int x, int y)
9176 {
9177   int delay = 6;
9178
9179   if (!MovDelay[x][y])          /* next animation frame */
9180     MovDelay[x][y] = 3 * delay;
9181
9182   if (MovDelay[x][y])           /* wait some time before next frame */
9183   {
9184     MovDelay[x][y]--;
9185
9186     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9187     {
9188       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9189       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9190
9191       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9192     }
9193
9194     if (!MovDelay[x][y])
9195     {
9196       if (MovDir[x][y] == MV_LEFT)
9197       {
9198         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9199           TEST_DrawLevelField(x - 1, y);
9200       }
9201       else if (MovDir[x][y] == MV_RIGHT)
9202       {
9203         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9204           TEST_DrawLevelField(x + 1, y);
9205       }
9206       else if (MovDir[x][y] == MV_UP)
9207       {
9208         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9209           TEST_DrawLevelField(x, y - 1);
9210       }
9211       else
9212       {
9213         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9214           TEST_DrawLevelField(x, y + 1);
9215       }
9216
9217       Feld[x][y] = Store[x][y];
9218       Store[x][y] = 0;
9219       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9220       TEST_DrawLevelField(x, y);
9221     }
9222   }
9223 }
9224
9225 void MauerAbleger(int ax, int ay)
9226 {
9227   int element = Feld[ax][ay];
9228   int graphic = el2img(element);
9229   boolean oben_frei = FALSE, unten_frei = FALSE;
9230   boolean links_frei = FALSE, rechts_frei = FALSE;
9231   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9232   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9233   boolean new_wall = FALSE;
9234
9235   if (IS_ANIMATED(graphic))
9236     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9237
9238   if (!MovDelay[ax][ay])        /* start building new wall */
9239     MovDelay[ax][ay] = 6;
9240
9241   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9242   {
9243     MovDelay[ax][ay]--;
9244     if (MovDelay[ax][ay])
9245       return;
9246   }
9247
9248   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9249     oben_frei = TRUE;
9250   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9251     unten_frei = TRUE;
9252   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9253     links_frei = TRUE;
9254   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9255     rechts_frei = TRUE;
9256
9257   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9258       element == EL_EXPANDABLE_WALL_ANY)
9259   {
9260     if (oben_frei)
9261     {
9262       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9263       Store[ax][ay-1] = element;
9264       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9265       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9266         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9267                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9268       new_wall = TRUE;
9269     }
9270     if (unten_frei)
9271     {
9272       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9273       Store[ax][ay+1] = element;
9274       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9275       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9276         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9277                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9278       new_wall = TRUE;
9279     }
9280   }
9281
9282   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9283       element == EL_EXPANDABLE_WALL_ANY ||
9284       element == EL_EXPANDABLE_WALL ||
9285       element == EL_BD_EXPANDABLE_WALL)
9286   {
9287     if (links_frei)
9288     {
9289       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9290       Store[ax-1][ay] = element;
9291       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9292       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9293         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9294                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9295       new_wall = TRUE;
9296     }
9297
9298     if (rechts_frei)
9299     {
9300       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9301       Store[ax+1][ay] = element;
9302       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9303       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9304         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9305                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9306       new_wall = TRUE;
9307     }
9308   }
9309
9310   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9311     TEST_DrawLevelField(ax, ay);
9312
9313   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9314     oben_massiv = TRUE;
9315   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9316     unten_massiv = TRUE;
9317   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9318     links_massiv = TRUE;
9319   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9320     rechts_massiv = TRUE;
9321
9322   if (((oben_massiv && unten_massiv) ||
9323        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9324        element == EL_EXPANDABLE_WALL) &&
9325       ((links_massiv && rechts_massiv) ||
9326        element == EL_EXPANDABLE_WALL_VERTICAL))
9327     Feld[ax][ay] = EL_WALL;
9328
9329   if (new_wall)
9330     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9331 }
9332
9333 void MauerAblegerStahl(int ax, int ay)
9334 {
9335   int element = Feld[ax][ay];
9336   int graphic = el2img(element);
9337   boolean oben_frei = FALSE, unten_frei = FALSE;
9338   boolean links_frei = FALSE, rechts_frei = FALSE;
9339   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9340   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9341   boolean new_wall = FALSE;
9342
9343   if (IS_ANIMATED(graphic))
9344     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9345
9346   if (!MovDelay[ax][ay])        /* start building new wall */
9347     MovDelay[ax][ay] = 6;
9348
9349   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9350   {
9351     MovDelay[ax][ay]--;
9352     if (MovDelay[ax][ay])
9353       return;
9354   }
9355
9356   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9357     oben_frei = TRUE;
9358   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9359     unten_frei = TRUE;
9360   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9361     links_frei = TRUE;
9362   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9363     rechts_frei = TRUE;
9364
9365   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9366       element == EL_EXPANDABLE_STEELWALL_ANY)
9367   {
9368     if (oben_frei)
9369     {
9370       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9371       Store[ax][ay-1] = element;
9372       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9373       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9374         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9375                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9376       new_wall = TRUE;
9377     }
9378     if (unten_frei)
9379     {
9380       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9381       Store[ax][ay+1] = element;
9382       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9383       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9384         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9385                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9386       new_wall = TRUE;
9387     }
9388   }
9389
9390   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9391       element == EL_EXPANDABLE_STEELWALL_ANY)
9392   {
9393     if (links_frei)
9394     {
9395       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9396       Store[ax-1][ay] = element;
9397       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9398       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9399         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9400                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9401       new_wall = TRUE;
9402     }
9403
9404     if (rechts_frei)
9405     {
9406       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9407       Store[ax+1][ay] = element;
9408       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9409       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9410         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9411                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9412       new_wall = TRUE;
9413     }
9414   }
9415
9416   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9417     oben_massiv = TRUE;
9418   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419     unten_massiv = TRUE;
9420   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421     links_massiv = TRUE;
9422   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423     rechts_massiv = TRUE;
9424
9425   if (((oben_massiv && unten_massiv) ||
9426        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9427       ((links_massiv && rechts_massiv) ||
9428        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9429     Feld[ax][ay] = EL_STEELWALL;
9430
9431   if (new_wall)
9432     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9433 }
9434
9435 void CheckForDragon(int x, int y)
9436 {
9437   int i, j;
9438   boolean dragon_found = FALSE;
9439   static int xy[4][2] =
9440   {
9441     { 0, -1 },
9442     { -1, 0 },
9443     { +1, 0 },
9444     { 0, +1 }
9445   };
9446
9447   for (i = 0; i < NUM_DIRECTIONS; i++)
9448   {
9449     for (j = 0; j < 4; j++)
9450     {
9451       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9452
9453       if (IN_LEV_FIELD(xx, yy) &&
9454           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9455       {
9456         if (Feld[xx][yy] == EL_DRAGON)
9457           dragon_found = TRUE;
9458       }
9459       else
9460         break;
9461     }
9462   }
9463
9464   if (!dragon_found)
9465   {
9466     for (i = 0; i < NUM_DIRECTIONS; i++)
9467     {
9468       for (j = 0; j < 3; j++)
9469       {
9470         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9471   
9472         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9473         {
9474           Feld[xx][yy] = EL_EMPTY;
9475           TEST_DrawLevelField(xx, yy);
9476         }
9477         else
9478           break;
9479       }
9480     }
9481   }
9482 }
9483
9484 static void InitBuggyBase(int x, int y)
9485 {
9486   int element = Feld[x][y];
9487   int activating_delay = FRAMES_PER_SECOND / 4;
9488
9489   ChangeDelay[x][y] =
9490     (element == EL_SP_BUGGY_BASE ?
9491      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9492      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9493      activating_delay :
9494      element == EL_SP_BUGGY_BASE_ACTIVE ?
9495      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9496 }
9497
9498 static void WarnBuggyBase(int x, int y)
9499 {
9500   int i;
9501   static int xy[4][2] =
9502   {
9503     { 0, -1 },
9504     { -1, 0 },
9505     { +1, 0 },
9506     { 0, +1 }
9507   };
9508
9509   for (i = 0; i < NUM_DIRECTIONS; i++)
9510   {
9511     int xx = x + xy[i][0];
9512     int yy = y + xy[i][1];
9513
9514     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9515     {
9516       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9517
9518       break;
9519     }
9520   }
9521 }
9522
9523 static void InitTrap(int x, int y)
9524 {
9525   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9526 }
9527
9528 static void ActivateTrap(int x, int y)
9529 {
9530   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9531 }
9532
9533 static void ChangeActiveTrap(int x, int y)
9534 {
9535   int graphic = IMG_TRAP_ACTIVE;
9536
9537   /* if new animation frame was drawn, correct crumbled sand border */
9538   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9539     TEST_DrawLevelFieldCrumbled(x, y);
9540 }
9541
9542 static int getSpecialActionElement(int element, int number, int base_element)
9543 {
9544   return (element != EL_EMPTY ? element :
9545           number != -1 ? base_element + number - 1 :
9546           EL_EMPTY);
9547 }
9548
9549 static int getModifiedActionNumber(int value_old, int operator, int operand,
9550                                    int value_min, int value_max)
9551 {
9552   int value_new = (operator == CA_MODE_SET      ? operand :
9553                    operator == CA_MODE_ADD      ? value_old + operand :
9554                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9555                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9556                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9557                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9558                    value_old);
9559
9560   return (value_new < value_min ? value_min :
9561           value_new > value_max ? value_max :
9562           value_new);
9563 }
9564
9565 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9566 {
9567   struct ElementInfo *ei = &element_info[element];
9568   struct ElementChangeInfo *change = &ei->change_page[page];
9569   int target_element = change->target_element;
9570   int action_type = change->action_type;
9571   int action_mode = change->action_mode;
9572   int action_arg = change->action_arg;
9573   int action_element = change->action_element;
9574   int i;
9575
9576   if (!change->has_action)
9577     return;
9578
9579   /* ---------- determine action paramater values -------------------------- */
9580
9581   int level_time_value =
9582     (level.time > 0 ? TimeLeft :
9583      TimePlayed);
9584
9585   int action_arg_element_raw =
9586     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9587      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9588      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9589      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9590      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9591      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9592      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9593      EL_EMPTY);
9594   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9595
9596   int action_arg_direction =
9597     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9598      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9599      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9600      change->actual_trigger_side :
9601      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9602      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9603      MV_NONE);
9604
9605   int action_arg_number_min =
9606     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9607      CA_ARG_MIN);
9608
9609   int action_arg_number_max =
9610     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9611      action_type == CA_SET_LEVEL_GEMS ? 999 :
9612      action_type == CA_SET_LEVEL_TIME ? 9999 :
9613      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9614      action_type == CA_SET_CE_VALUE ? 9999 :
9615      action_type == CA_SET_CE_SCORE ? 9999 :
9616      CA_ARG_MAX);
9617
9618   int action_arg_number_reset =
9619     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9620      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9621      action_type == CA_SET_LEVEL_TIME ? level.time :
9622      action_type == CA_SET_LEVEL_SCORE ? 0 :
9623      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9624      action_type == CA_SET_CE_SCORE ? 0 :
9625      0);
9626
9627   int action_arg_number =
9628     (action_arg <= CA_ARG_MAX ? action_arg :
9629      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9630      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9631      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9632      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9633      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9634      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9635      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9636      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9637      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9638      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9639      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9640      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9641      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9642      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9643      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9644      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9645      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9646      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9647      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9648      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9649      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9650      -1);
9651
9652   int action_arg_number_old =
9653     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9654      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9655      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9656      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9657      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9658      0);
9659
9660   int action_arg_number_new =
9661     getModifiedActionNumber(action_arg_number_old,
9662                             action_mode, action_arg_number,
9663                             action_arg_number_min, action_arg_number_max);
9664
9665   int trigger_player_bits =
9666     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9667      change->actual_trigger_player_bits : change->trigger_player);
9668
9669   int action_arg_player_bits =
9670     (action_arg >= CA_ARG_PLAYER_1 &&
9671      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9672      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9673      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9674      PLAYER_BITS_ANY);
9675
9676   /* ---------- execute action  -------------------------------------------- */
9677
9678   switch (action_type)
9679   {
9680     case CA_NO_ACTION:
9681     {
9682       return;
9683     }
9684
9685     /* ---------- level actions  ------------------------------------------- */
9686
9687     case CA_RESTART_LEVEL:
9688     {
9689       game.restart_level = TRUE;
9690
9691       break;
9692     }
9693
9694     case CA_SHOW_ENVELOPE:
9695     {
9696       int element = getSpecialActionElement(action_arg_element,
9697                                             action_arg_number, EL_ENVELOPE_1);
9698
9699       if (IS_ENVELOPE(element))
9700         local_player->show_envelope = element;
9701
9702       break;
9703     }
9704
9705     case CA_SET_LEVEL_TIME:
9706     {
9707       if (level.time > 0)       /* only modify limited time value */
9708       {
9709         TimeLeft = action_arg_number_new;
9710
9711         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9712
9713         DisplayGameControlValues();
9714
9715         if (!TimeLeft && setup.time_limit)
9716           for (i = 0; i < MAX_PLAYERS; i++)
9717             KillPlayer(&stored_player[i]);
9718       }
9719
9720       break;
9721     }
9722
9723     case CA_SET_LEVEL_SCORE:
9724     {
9725       local_player->score = action_arg_number_new;
9726
9727       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9728
9729       DisplayGameControlValues();
9730
9731       break;
9732     }
9733
9734     case CA_SET_LEVEL_GEMS:
9735     {
9736       local_player->gems_still_needed = action_arg_number_new;
9737
9738       game.snapshot.collected_item = TRUE;
9739
9740       game_panel_controls[GAME_PANEL_GEMS].value =
9741         local_player->gems_still_needed;
9742
9743       DisplayGameControlValues();
9744
9745       break;
9746     }
9747
9748     case CA_SET_LEVEL_WIND:
9749     {
9750       game.wind_direction = action_arg_direction;
9751
9752       break;
9753     }
9754
9755     case CA_SET_LEVEL_RANDOM_SEED:
9756     {
9757       /* ensure that setting a new random seed while playing is predictable */
9758       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9759
9760       break;
9761     }
9762
9763     /* ---------- player actions  ------------------------------------------ */
9764
9765     case CA_MOVE_PLAYER:
9766     {
9767       /* automatically move to the next field in specified direction */
9768       for (i = 0; i < MAX_PLAYERS; i++)
9769         if (trigger_player_bits & (1 << i))
9770           stored_player[i].programmed_action = action_arg_direction;
9771
9772       break;
9773     }
9774
9775     case CA_EXIT_PLAYER:
9776     {
9777       for (i = 0; i < MAX_PLAYERS; i++)
9778         if (action_arg_player_bits & (1 << i))
9779           PlayerWins(&stored_player[i]);
9780
9781       break;
9782     }
9783
9784     case CA_KILL_PLAYER:
9785     {
9786       for (i = 0; i < MAX_PLAYERS; i++)
9787         if (action_arg_player_bits & (1 << i))
9788           KillPlayer(&stored_player[i]);
9789
9790       break;
9791     }
9792
9793     case CA_SET_PLAYER_KEYS:
9794     {
9795       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9796       int element = getSpecialActionElement(action_arg_element,
9797                                             action_arg_number, EL_KEY_1);
9798
9799       if (IS_KEY(element))
9800       {
9801         for (i = 0; i < MAX_PLAYERS; i++)
9802         {
9803           if (trigger_player_bits & (1 << i))
9804           {
9805             stored_player[i].key[KEY_NR(element)] = key_state;
9806
9807             DrawGameDoorValues();
9808           }
9809         }
9810       }
9811
9812       break;
9813     }
9814
9815     case CA_SET_PLAYER_SPEED:
9816     {
9817       for (i = 0; i < MAX_PLAYERS; i++)
9818       {
9819         if (trigger_player_bits & (1 << i))
9820         {
9821           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9822
9823           if (action_arg == CA_ARG_SPEED_FASTER &&
9824               stored_player[i].cannot_move)
9825           {
9826             action_arg_number = STEPSIZE_VERY_SLOW;
9827           }
9828           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9829                    action_arg == CA_ARG_SPEED_FASTER)
9830           {
9831             action_arg_number = 2;
9832             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9833                            CA_MODE_MULTIPLY);
9834           }
9835           else if (action_arg == CA_ARG_NUMBER_RESET)
9836           {
9837             action_arg_number = level.initial_player_stepsize[i];
9838           }
9839
9840           move_stepsize =
9841             getModifiedActionNumber(move_stepsize,
9842                                     action_mode,
9843                                     action_arg_number,
9844                                     action_arg_number_min,
9845                                     action_arg_number_max);
9846
9847           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9848         }
9849       }
9850
9851       break;
9852     }
9853
9854     case CA_SET_PLAYER_SHIELD:
9855     {
9856       for (i = 0; i < MAX_PLAYERS; i++)
9857       {
9858         if (trigger_player_bits & (1 << i))
9859         {
9860           if (action_arg == CA_ARG_SHIELD_OFF)
9861           {
9862             stored_player[i].shield_normal_time_left = 0;
9863             stored_player[i].shield_deadly_time_left = 0;
9864           }
9865           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9866           {
9867             stored_player[i].shield_normal_time_left = 999999;
9868           }
9869           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9870           {
9871             stored_player[i].shield_normal_time_left = 999999;
9872             stored_player[i].shield_deadly_time_left = 999999;
9873           }
9874         }
9875       }
9876
9877       break;
9878     }
9879
9880     case CA_SET_PLAYER_GRAVITY:
9881     {
9882       for (i = 0; i < MAX_PLAYERS; i++)
9883       {
9884         if (trigger_player_bits & (1 << i))
9885         {
9886           stored_player[i].gravity =
9887             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9888              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9889              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9890              stored_player[i].gravity);
9891         }
9892       }
9893
9894       break;
9895     }
9896
9897     case CA_SET_PLAYER_ARTWORK:
9898     {
9899       for (i = 0; i < MAX_PLAYERS; i++)
9900       {
9901         if (trigger_player_bits & (1 << i))
9902         {
9903           int artwork_element = action_arg_element;
9904
9905           if (action_arg == CA_ARG_ELEMENT_RESET)
9906             artwork_element =
9907               (level.use_artwork_element[i] ? level.artwork_element[i] :
9908                stored_player[i].element_nr);
9909
9910           if (stored_player[i].artwork_element != artwork_element)
9911             stored_player[i].Frame = 0;
9912
9913           stored_player[i].artwork_element = artwork_element;
9914
9915           SetPlayerWaiting(&stored_player[i], FALSE);
9916
9917           /* set number of special actions for bored and sleeping animation */
9918           stored_player[i].num_special_action_bored =
9919             get_num_special_action(artwork_element,
9920                                    ACTION_BORING_1, ACTION_BORING_LAST);
9921           stored_player[i].num_special_action_sleeping =
9922             get_num_special_action(artwork_element,
9923                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9924         }
9925       }
9926
9927       break;
9928     }
9929
9930     case CA_SET_PLAYER_INVENTORY:
9931     {
9932       for (i = 0; i < MAX_PLAYERS; i++)
9933       {
9934         struct PlayerInfo *player = &stored_player[i];
9935         int j, k;
9936
9937         if (trigger_player_bits & (1 << i))
9938         {
9939           int inventory_element = action_arg_element;
9940
9941           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9942               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9943               action_arg == CA_ARG_ELEMENT_ACTION)
9944           {
9945             int element = inventory_element;
9946             int collect_count = element_info[element].collect_count_initial;
9947
9948             if (!IS_CUSTOM_ELEMENT(element))
9949               collect_count = 1;
9950
9951             if (collect_count == 0)
9952               player->inventory_infinite_element = element;
9953             else
9954               for (k = 0; k < collect_count; k++)
9955                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9956                   player->inventory_element[player->inventory_size++] =
9957                     element;
9958           }
9959           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9960                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9961                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9962           {
9963             if (player->inventory_infinite_element != EL_UNDEFINED &&
9964                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9965                                      action_arg_element_raw))
9966               player->inventory_infinite_element = EL_UNDEFINED;
9967
9968             for (k = 0, j = 0; j < player->inventory_size; j++)
9969             {
9970               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9971                                         action_arg_element_raw))
9972                 player->inventory_element[k++] = player->inventory_element[j];
9973             }
9974
9975             player->inventory_size = k;
9976           }
9977           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9978           {
9979             if (player->inventory_size > 0)
9980             {
9981               for (j = 0; j < player->inventory_size - 1; j++)
9982                 player->inventory_element[j] = player->inventory_element[j + 1];
9983
9984               player->inventory_size--;
9985             }
9986           }
9987           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9988           {
9989             if (player->inventory_size > 0)
9990               player->inventory_size--;
9991           }
9992           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9993           {
9994             player->inventory_infinite_element = EL_UNDEFINED;
9995             player->inventory_size = 0;
9996           }
9997           else if (action_arg == CA_ARG_INVENTORY_RESET)
9998           {
9999             player->inventory_infinite_element = EL_UNDEFINED;
10000             player->inventory_size = 0;
10001
10002             if (level.use_initial_inventory[i])
10003             {
10004               for (j = 0; j < level.initial_inventory_size[i]; j++)
10005               {
10006                 int element = level.initial_inventory_content[i][j];
10007                 int collect_count = element_info[element].collect_count_initial;
10008
10009                 if (!IS_CUSTOM_ELEMENT(element))
10010                   collect_count = 1;
10011
10012                 if (collect_count == 0)
10013                   player->inventory_infinite_element = element;
10014                 else
10015                   for (k = 0; k < collect_count; k++)
10016                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10017                       player->inventory_element[player->inventory_size++] =
10018                         element;
10019               }
10020             }
10021           }
10022         }
10023       }
10024
10025       break;
10026     }
10027
10028     /* ---------- CE actions  ---------------------------------------------- */
10029
10030     case CA_SET_CE_VALUE:
10031     {
10032       int last_ce_value = CustomValue[x][y];
10033
10034       CustomValue[x][y] = action_arg_number_new;
10035
10036       if (CustomValue[x][y] != last_ce_value)
10037       {
10038         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10039         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10040
10041         if (CustomValue[x][y] == 0)
10042         {
10043           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10044           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10045         }
10046       }
10047
10048       break;
10049     }
10050
10051     case CA_SET_CE_SCORE:
10052     {
10053       int last_ce_score = ei->collect_score;
10054
10055       ei->collect_score = action_arg_number_new;
10056
10057       if (ei->collect_score != last_ce_score)
10058       {
10059         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10060         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10061
10062         if (ei->collect_score == 0)
10063         {
10064           int xx, yy;
10065
10066           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10067           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10068
10069           /*
10070             This is a very special case that seems to be a mixture between
10071             CheckElementChange() and CheckTriggeredElementChange(): while
10072             the first one only affects single elements that are triggered
10073             directly, the second one affects multiple elements in the playfield
10074             that are triggered indirectly by another element. This is a third
10075             case: Changing the CE score always affects multiple identical CEs,
10076             so every affected CE must be checked, not only the single CE for
10077             which the CE score was changed in the first place (as every instance
10078             of that CE shares the same CE score, and therefore also can change)!
10079           */
10080           SCAN_PLAYFIELD(xx, yy)
10081           {
10082             if (Feld[xx][yy] == element)
10083               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10084                                  CE_SCORE_GETS_ZERO);
10085           }
10086         }
10087       }
10088
10089       break;
10090     }
10091
10092     case CA_SET_CE_ARTWORK:
10093     {
10094       int artwork_element = action_arg_element;
10095       boolean reset_frame = FALSE;
10096       int xx, yy;
10097
10098       if (action_arg == CA_ARG_ELEMENT_RESET)
10099         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10100                            element);
10101
10102       if (ei->gfx_element != artwork_element)
10103         reset_frame = TRUE;
10104
10105       ei->gfx_element = artwork_element;
10106
10107       SCAN_PLAYFIELD(xx, yy)
10108       {
10109         if (Feld[xx][yy] == element)
10110         {
10111           if (reset_frame)
10112           {
10113             ResetGfxAnimation(xx, yy);
10114             ResetRandomAnimationValue(xx, yy);
10115           }
10116
10117           TEST_DrawLevelField(xx, yy);
10118         }
10119       }
10120
10121       break;
10122     }
10123
10124     /* ---------- engine actions  ------------------------------------------ */
10125
10126     case CA_SET_ENGINE_SCAN_MODE:
10127     {
10128       InitPlayfieldScanMode(action_arg);
10129
10130       break;
10131     }
10132
10133     default:
10134       break;
10135   }
10136 }
10137
10138 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10139 {
10140   int old_element = Feld[x][y];
10141   int new_element = GetElementFromGroupElement(element);
10142   int previous_move_direction = MovDir[x][y];
10143   int last_ce_value = CustomValue[x][y];
10144   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10145   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10146   boolean add_player_onto_element = (new_element_is_player &&
10147                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10148                                      IS_WALKABLE(old_element));
10149
10150   if (!add_player_onto_element)
10151   {
10152     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10153       RemoveMovingField(x, y);
10154     else
10155       RemoveField(x, y);
10156
10157     Feld[x][y] = new_element;
10158
10159     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10160       MovDir[x][y] = previous_move_direction;
10161
10162     if (element_info[new_element].use_last_ce_value)
10163       CustomValue[x][y] = last_ce_value;
10164
10165     InitField_WithBug1(x, y, FALSE);
10166
10167     new_element = Feld[x][y];   /* element may have changed */
10168
10169     ResetGfxAnimation(x, y);
10170     ResetRandomAnimationValue(x, y);
10171
10172     TEST_DrawLevelField(x, y);
10173
10174     if (GFX_CRUMBLED(new_element))
10175       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10176   }
10177
10178   /* check if element under the player changes from accessible to unaccessible
10179      (needed for special case of dropping element which then changes) */
10180   /* (must be checked after creating new element for walkable group elements) */
10181   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10182       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10183   {
10184     Bang(x, y);
10185
10186     return;
10187   }
10188
10189   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10190   if (new_element_is_player)
10191     RelocatePlayer(x, y, new_element);
10192
10193   if (is_change)
10194     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10195
10196   TestIfBadThingTouchesPlayer(x, y);
10197   TestIfPlayerTouchesCustomElement(x, y);
10198   TestIfElementTouchesCustomElement(x, y);
10199 }
10200
10201 static void CreateField(int x, int y, int element)
10202 {
10203   CreateFieldExt(x, y, element, FALSE);
10204 }
10205
10206 static void CreateElementFromChange(int x, int y, int element)
10207 {
10208   element = GET_VALID_RUNTIME_ELEMENT(element);
10209
10210   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10211   {
10212     int old_element = Feld[x][y];
10213
10214     /* prevent changed element from moving in same engine frame
10215        unless both old and new element can either fall or move */
10216     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10217         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10218       Stop[x][y] = TRUE;
10219   }
10220
10221   CreateFieldExt(x, y, element, TRUE);
10222 }
10223
10224 static boolean ChangeElement(int x, int y, int element, int page)
10225 {
10226   struct ElementInfo *ei = &element_info[element];
10227   struct ElementChangeInfo *change = &ei->change_page[page];
10228   int ce_value = CustomValue[x][y];
10229   int ce_score = ei->collect_score;
10230   int target_element;
10231   int old_element = Feld[x][y];
10232
10233   /* always use default change event to prevent running into a loop */
10234   if (ChangeEvent[x][y] == -1)
10235     ChangeEvent[x][y] = CE_DELAY;
10236
10237   if (ChangeEvent[x][y] == CE_DELAY)
10238   {
10239     /* reset actual trigger element, trigger player and action element */
10240     change->actual_trigger_element = EL_EMPTY;
10241     change->actual_trigger_player = EL_EMPTY;
10242     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10243     change->actual_trigger_side = CH_SIDE_NONE;
10244     change->actual_trigger_ce_value = 0;
10245     change->actual_trigger_ce_score = 0;
10246   }
10247
10248   /* do not change elements more than a specified maximum number of changes */
10249   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10250     return FALSE;
10251
10252   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10253
10254   if (change->explode)
10255   {
10256     Bang(x, y);
10257
10258     return TRUE;
10259   }
10260
10261   if (change->use_target_content)
10262   {
10263     boolean complete_replace = TRUE;
10264     boolean can_replace[3][3];
10265     int xx, yy;
10266
10267     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10268     {
10269       boolean is_empty;
10270       boolean is_walkable;
10271       boolean is_diggable;
10272       boolean is_collectible;
10273       boolean is_removable;
10274       boolean is_destructible;
10275       int ex = x + xx - 1;
10276       int ey = y + yy - 1;
10277       int content_element = change->target_content.e[xx][yy];
10278       int e;
10279
10280       can_replace[xx][yy] = TRUE;
10281
10282       if (ex == x && ey == y)   /* do not check changing element itself */
10283         continue;
10284
10285       if (content_element == EL_EMPTY_SPACE)
10286       {
10287         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10288
10289         continue;
10290       }
10291
10292       if (!IN_LEV_FIELD(ex, ey))
10293       {
10294         can_replace[xx][yy] = FALSE;
10295         complete_replace = FALSE;
10296
10297         continue;
10298       }
10299
10300       e = Feld[ex][ey];
10301
10302       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10303         e = MovingOrBlocked2Element(ex, ey);
10304
10305       is_empty = (IS_FREE(ex, ey) ||
10306                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10307
10308       is_walkable     = (is_empty || IS_WALKABLE(e));
10309       is_diggable     = (is_empty || IS_DIGGABLE(e));
10310       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10311       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10312       is_removable    = (is_diggable || is_collectible);
10313
10314       can_replace[xx][yy] =
10315         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10316           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10317           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10318           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10319           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10320           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10321          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10322
10323       if (!can_replace[xx][yy])
10324         complete_replace = FALSE;
10325     }
10326
10327     if (!change->only_if_complete || complete_replace)
10328     {
10329       boolean something_has_changed = FALSE;
10330
10331       if (change->only_if_complete && change->use_random_replace &&
10332           RND(100) < change->random_percentage)
10333         return FALSE;
10334
10335       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10336       {
10337         int ex = x + xx - 1;
10338         int ey = y + yy - 1;
10339         int content_element;
10340
10341         if (can_replace[xx][yy] && (!change->use_random_replace ||
10342                                     RND(100) < change->random_percentage))
10343         {
10344           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10345             RemoveMovingField(ex, ey);
10346
10347           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10348
10349           content_element = change->target_content.e[xx][yy];
10350           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10351                                               ce_value, ce_score);
10352
10353           CreateElementFromChange(ex, ey, target_element);
10354
10355           something_has_changed = TRUE;
10356
10357           /* for symmetry reasons, freeze newly created border elements */
10358           if (ex != x || ey != y)
10359             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10360         }
10361       }
10362
10363       if (something_has_changed)
10364       {
10365         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10366         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10367       }
10368     }
10369   }
10370   else
10371   {
10372     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10373                                         ce_value, ce_score);
10374
10375     if (element == EL_DIAGONAL_GROWING ||
10376         element == EL_DIAGONAL_SHRINKING)
10377     {
10378       target_element = Store[x][y];
10379
10380       Store[x][y] = EL_EMPTY;
10381     }
10382
10383     CreateElementFromChange(x, y, target_element);
10384
10385     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10386     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10387   }
10388
10389   /* this uses direct change before indirect change */
10390   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10391
10392   return TRUE;
10393 }
10394
10395 static void HandleElementChange(int x, int y, int page)
10396 {
10397   int element = MovingOrBlocked2Element(x, y);
10398   struct ElementInfo *ei = &element_info[element];
10399   struct ElementChangeInfo *change = &ei->change_page[page];
10400   boolean handle_action_before_change = FALSE;
10401
10402 #ifdef DEBUG
10403   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10404       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10405   {
10406     printf("\n\n");
10407     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10408            x, y, element, element_info[element].token_name);
10409     printf("HandleElementChange(): This should never happen!\n");
10410     printf("\n\n");
10411   }
10412 #endif
10413
10414   /* this can happen with classic bombs on walkable, changing elements */
10415   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10416   {
10417     return;
10418   }
10419
10420   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10421   {
10422     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10423
10424     if (change->can_change)
10425     {
10426       /* !!! not clear why graphic animation should be reset at all here !!! */
10427       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10428       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10429
10430       /*
10431         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10432
10433         When using an animation frame delay of 1 (this only happens with
10434         "sp_zonk.moving.left/right" in the classic graphics), the default
10435         (non-moving) animation shows wrong animation frames (while the
10436         moving animation, like "sp_zonk.moving.left/right", is correct,
10437         so this graphical bug never shows up with the classic graphics).
10438         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10439         be drawn instead of the correct frames 0,1,2,3. This is caused by
10440         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10441         an element change: First when the change delay ("ChangeDelay[][]")
10442         counter has reached zero after decrementing, then a second time in
10443         the next frame (after "GfxFrame[][]" was already incremented) when
10444         "ChangeDelay[][]" is reset to the initial delay value again.
10445
10446         This causes frame 0 to be drawn twice, while the last frame won't
10447         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10448
10449         As some animations may already be cleverly designed around this bug
10450         (at least the "Snake Bite" snake tail animation does this), it cannot
10451         simply be fixed here without breaking such existing animations.
10452         Unfortunately, it cannot easily be detected if a graphics set was
10453         designed "before" or "after" the bug was fixed. As a workaround,
10454         a new graphics set option "game.graphics_engine_version" was added
10455         to be able to specify the game's major release version for which the
10456         graphics set was designed, which can then be used to decide if the
10457         bugfix should be used (version 4 and above) or not (version 3 or
10458         below, or if no version was specified at all, as with old sets).
10459
10460         (The wrong/fixed animation frames can be tested with the test level set
10461         "test_gfxframe" and level "000", which contains a specially prepared
10462         custom element at level position (x/y) == (11/9) which uses the zonk
10463         animation mentioned above. Using "game.graphics_engine_version: 4"
10464         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10465         This can also be seen from the debug output for this test element.)
10466       */
10467
10468       /* when a custom element is about to change (for example by change delay),
10469          do not reset graphic animation when the custom element is moving */
10470       if (game.graphics_engine_version < 4 &&
10471           !IS_MOVING(x, y))
10472       {
10473         ResetGfxAnimation(x, y);
10474         ResetRandomAnimationValue(x, y);
10475       }
10476
10477       if (change->pre_change_function)
10478         change->pre_change_function(x, y);
10479     }
10480   }
10481
10482   ChangeDelay[x][y]--;
10483
10484   if (ChangeDelay[x][y] != 0)           /* continue element change */
10485   {
10486     if (change->can_change)
10487     {
10488       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10489
10490       if (IS_ANIMATED(graphic))
10491         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10492
10493       if (change->change_function)
10494         change->change_function(x, y);
10495     }
10496   }
10497   else                                  /* finish element change */
10498   {
10499     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10500     {
10501       page = ChangePage[x][y];
10502       ChangePage[x][y] = -1;
10503
10504       change = &ei->change_page[page];
10505     }
10506
10507     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10508     {
10509       ChangeDelay[x][y] = 1;            /* try change after next move step */
10510       ChangePage[x][y] = page;          /* remember page to use for change */
10511
10512       return;
10513     }
10514
10515     /* special case: set new level random seed before changing element */
10516     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10517       handle_action_before_change = TRUE;
10518
10519     if (change->has_action && handle_action_before_change)
10520       ExecuteCustomElementAction(x, y, element, page);
10521
10522     if (change->can_change)
10523     {
10524       if (ChangeElement(x, y, element, page))
10525       {
10526         if (change->post_change_function)
10527           change->post_change_function(x, y);
10528       }
10529     }
10530
10531     if (change->has_action && !handle_action_before_change)
10532       ExecuteCustomElementAction(x, y, element, page);
10533   }
10534 }
10535
10536 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10537                                               int trigger_element,
10538                                               int trigger_event,
10539                                               int trigger_player,
10540                                               int trigger_side,
10541                                               int trigger_page)
10542 {
10543   boolean change_done_any = FALSE;
10544   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10545   int i;
10546
10547   if (!(trigger_events[trigger_element][trigger_event]))
10548     return FALSE;
10549
10550   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10551
10552   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10553   {
10554     int element = EL_CUSTOM_START + i;
10555     boolean change_done = FALSE;
10556     int p;
10557
10558     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10559         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10560       continue;
10561
10562     for (p = 0; p < element_info[element].num_change_pages; p++)
10563     {
10564       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10565
10566       if (change->can_change_or_has_action &&
10567           change->has_event[trigger_event] &&
10568           change->trigger_side & trigger_side &&
10569           change->trigger_player & trigger_player &&
10570           change->trigger_page & trigger_page_bits &&
10571           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10572       {
10573         change->actual_trigger_element = trigger_element;
10574         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10575         change->actual_trigger_player_bits = trigger_player;
10576         change->actual_trigger_side = trigger_side;
10577         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10578         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10579
10580         if ((change->can_change && !change_done) || change->has_action)
10581         {
10582           int x, y;
10583
10584           SCAN_PLAYFIELD(x, y)
10585           {
10586             if (Feld[x][y] == element)
10587             {
10588               if (change->can_change && !change_done)
10589               {
10590                 /* if element already changed in this frame, not only prevent
10591                    another element change (checked in ChangeElement()), but
10592                    also prevent additional element actions for this element */
10593
10594                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10595                     !level.use_action_after_change_bug)
10596                   continue;
10597
10598                 ChangeDelay[x][y] = 1;
10599                 ChangeEvent[x][y] = trigger_event;
10600
10601                 HandleElementChange(x, y, p);
10602               }
10603               else if (change->has_action)
10604               {
10605                 /* if element already changed in this frame, not only prevent
10606                    another element change (checked in ChangeElement()), but
10607                    also prevent additional element actions for this element */
10608
10609                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10610                     !level.use_action_after_change_bug)
10611                   continue;
10612
10613                 ExecuteCustomElementAction(x, y, element, p);
10614                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10615               }
10616             }
10617           }
10618
10619           if (change->can_change)
10620           {
10621             change_done = TRUE;
10622             change_done_any = TRUE;
10623           }
10624         }
10625       }
10626     }
10627   }
10628
10629   RECURSION_LOOP_DETECTION_END();
10630
10631   return change_done_any;
10632 }
10633
10634 static boolean CheckElementChangeExt(int x, int y,
10635                                      int element,
10636                                      int trigger_element,
10637                                      int trigger_event,
10638                                      int trigger_player,
10639                                      int trigger_side)
10640 {
10641   boolean change_done = FALSE;
10642   int p;
10643
10644   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10645       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10646     return FALSE;
10647
10648   if (Feld[x][y] == EL_BLOCKED)
10649   {
10650     Blocked2Moving(x, y, &x, &y);
10651     element = Feld[x][y];
10652   }
10653
10654   /* check if element has already changed or is about to change after moving */
10655   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10656        Feld[x][y] != element) ||
10657
10658       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10659        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10660         ChangePage[x][y] != -1)))
10661     return FALSE;
10662
10663   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10664
10665   for (p = 0; p < element_info[element].num_change_pages; p++)
10666   {
10667     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10668
10669     /* check trigger element for all events where the element that is checked
10670        for changing interacts with a directly adjacent element -- this is
10671        different to element changes that affect other elements to change on the
10672        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10673     boolean check_trigger_element =
10674       (trigger_event == CE_TOUCHING_X ||
10675        trigger_event == CE_HITTING_X ||
10676        trigger_event == CE_HIT_BY_X ||
10677        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10678
10679     if (change->can_change_or_has_action &&
10680         change->has_event[trigger_event] &&
10681         change->trigger_side & trigger_side &&
10682         change->trigger_player & trigger_player &&
10683         (!check_trigger_element ||
10684          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10685     {
10686       change->actual_trigger_element = trigger_element;
10687       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10688       change->actual_trigger_player_bits = trigger_player;
10689       change->actual_trigger_side = trigger_side;
10690       change->actual_trigger_ce_value = CustomValue[x][y];
10691       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10692
10693       /* special case: trigger element not at (x,y) position for some events */
10694       if (check_trigger_element)
10695       {
10696         static struct
10697         {
10698           int dx, dy;
10699         } move_xy[] =
10700           {
10701             {  0,  0 },
10702             { -1,  0 },
10703             { +1,  0 },
10704             {  0,  0 },
10705             {  0, -1 },
10706             {  0,  0 }, { 0, 0 }, { 0, 0 },
10707             {  0, +1 }
10708           };
10709
10710         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10711         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10712
10713         change->actual_trigger_ce_value = CustomValue[xx][yy];
10714         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10715       }
10716
10717       if (change->can_change && !change_done)
10718       {
10719         ChangeDelay[x][y] = 1;
10720         ChangeEvent[x][y] = trigger_event;
10721
10722         HandleElementChange(x, y, p);
10723
10724         change_done = TRUE;
10725       }
10726       else if (change->has_action)
10727       {
10728         ExecuteCustomElementAction(x, y, element, p);
10729         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10730       }
10731     }
10732   }
10733
10734   RECURSION_LOOP_DETECTION_END();
10735
10736   return change_done;
10737 }
10738
10739 static void PlayPlayerSound(struct PlayerInfo *player)
10740 {
10741   int jx = player->jx, jy = player->jy;
10742   int sound_element = player->artwork_element;
10743   int last_action = player->last_action_waiting;
10744   int action = player->action_waiting;
10745
10746   if (player->is_waiting)
10747   {
10748     if (action != last_action)
10749       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10750     else
10751       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10752   }
10753   else
10754   {
10755     if (action != last_action)
10756       StopSound(element_info[sound_element].sound[last_action]);
10757
10758     if (last_action == ACTION_SLEEPING)
10759       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10760   }
10761 }
10762
10763 static void PlayAllPlayersSound()
10764 {
10765   int i;
10766
10767   for (i = 0; i < MAX_PLAYERS; i++)
10768     if (stored_player[i].active)
10769       PlayPlayerSound(&stored_player[i]);
10770 }
10771
10772 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10773 {
10774   boolean last_waiting = player->is_waiting;
10775   int move_dir = player->MovDir;
10776
10777   player->dir_waiting = move_dir;
10778   player->last_action_waiting = player->action_waiting;
10779
10780   if (is_waiting)
10781   {
10782     if (!last_waiting)          /* not waiting -> waiting */
10783     {
10784       player->is_waiting = TRUE;
10785
10786       player->frame_counter_bored =
10787         FrameCounter +
10788         game.player_boring_delay_fixed +
10789         GetSimpleRandom(game.player_boring_delay_random);
10790       player->frame_counter_sleeping =
10791         FrameCounter +
10792         game.player_sleeping_delay_fixed +
10793         GetSimpleRandom(game.player_sleeping_delay_random);
10794
10795       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10796     }
10797
10798     if (game.player_sleeping_delay_fixed +
10799         game.player_sleeping_delay_random > 0 &&
10800         player->anim_delay_counter == 0 &&
10801         player->post_delay_counter == 0 &&
10802         FrameCounter >= player->frame_counter_sleeping)
10803       player->is_sleeping = TRUE;
10804     else if (game.player_boring_delay_fixed +
10805              game.player_boring_delay_random > 0 &&
10806              FrameCounter >= player->frame_counter_bored)
10807       player->is_bored = TRUE;
10808
10809     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10810                               player->is_bored ? ACTION_BORING :
10811                               ACTION_WAITING);
10812
10813     if (player->is_sleeping && player->use_murphy)
10814     {
10815       /* special case for sleeping Murphy when leaning against non-free tile */
10816
10817       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10818           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10819            !IS_MOVING(player->jx - 1, player->jy)))
10820         move_dir = MV_LEFT;
10821       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10822                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10823                 !IS_MOVING(player->jx + 1, player->jy)))
10824         move_dir = MV_RIGHT;
10825       else
10826         player->is_sleeping = FALSE;
10827
10828       player->dir_waiting = move_dir;
10829     }
10830
10831     if (player->is_sleeping)
10832     {
10833       if (player->num_special_action_sleeping > 0)
10834       {
10835         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10836         {
10837           int last_special_action = player->special_action_sleeping;
10838           int num_special_action = player->num_special_action_sleeping;
10839           int special_action =
10840             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10841              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10842              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10843              last_special_action + 1 : ACTION_SLEEPING);
10844           int special_graphic =
10845             el_act_dir2img(player->artwork_element, special_action, move_dir);
10846
10847           player->anim_delay_counter =
10848             graphic_info[special_graphic].anim_delay_fixed +
10849             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10850           player->post_delay_counter =
10851             graphic_info[special_graphic].post_delay_fixed +
10852             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10853
10854           player->special_action_sleeping = special_action;
10855         }
10856
10857         if (player->anim_delay_counter > 0)
10858         {
10859           player->action_waiting = player->special_action_sleeping;
10860           player->anim_delay_counter--;
10861         }
10862         else if (player->post_delay_counter > 0)
10863         {
10864           player->post_delay_counter--;
10865         }
10866       }
10867     }
10868     else if (player->is_bored)
10869     {
10870       if (player->num_special_action_bored > 0)
10871       {
10872         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10873         {
10874           int special_action =
10875             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10876           int special_graphic =
10877             el_act_dir2img(player->artwork_element, special_action, move_dir);
10878
10879           player->anim_delay_counter =
10880             graphic_info[special_graphic].anim_delay_fixed +
10881             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10882           player->post_delay_counter =
10883             graphic_info[special_graphic].post_delay_fixed +
10884             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10885
10886           player->special_action_bored = special_action;
10887         }
10888
10889         if (player->anim_delay_counter > 0)
10890         {
10891           player->action_waiting = player->special_action_bored;
10892           player->anim_delay_counter--;
10893         }
10894         else if (player->post_delay_counter > 0)
10895         {
10896           player->post_delay_counter--;
10897         }
10898       }
10899     }
10900   }
10901   else if (last_waiting)        /* waiting -> not waiting */
10902   {
10903     player->is_waiting = FALSE;
10904     player->is_bored = FALSE;
10905     player->is_sleeping = FALSE;
10906
10907     player->frame_counter_bored = -1;
10908     player->frame_counter_sleeping = -1;
10909
10910     player->anim_delay_counter = 0;
10911     player->post_delay_counter = 0;
10912
10913     player->dir_waiting = player->MovDir;
10914     player->action_waiting = ACTION_DEFAULT;
10915
10916     player->special_action_bored = ACTION_DEFAULT;
10917     player->special_action_sleeping = ACTION_DEFAULT;
10918   }
10919 }
10920
10921 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10922 {
10923   if ((!player->is_moving  && player->was_moving) ||
10924       (player->MovPos == 0 && player->was_moving) ||
10925       (player->is_snapping && !player->was_snapping) ||
10926       (player->is_dropping && !player->was_dropping))
10927   {
10928     if (!CheckSaveEngineSnapshotToList())
10929       return;
10930
10931     player->was_moving = FALSE;
10932     player->was_snapping = TRUE;
10933     player->was_dropping = TRUE;
10934   }
10935   else
10936   {
10937     if (player->is_moving)
10938       player->was_moving = TRUE;
10939
10940     if (!player->is_snapping)
10941       player->was_snapping = FALSE;
10942
10943     if (!player->is_dropping)
10944       player->was_dropping = FALSE;
10945   }
10946 }
10947
10948 static void CheckSingleStepMode(struct PlayerInfo *player)
10949 {
10950   if (tape.single_step && tape.recording && !tape.pausing)
10951   {
10952     /* as it is called "single step mode", just return to pause mode when the
10953        player stopped moving after one tile (or never starts moving at all) */
10954     if (!player->is_moving &&
10955         !player->is_pushing &&
10956         !player->is_dropping_pressed)
10957     {
10958       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10959       SnapField(player, 0, 0);                  /* stop snapping */
10960     }
10961   }
10962
10963   CheckSaveEngineSnapshot(player);
10964 }
10965
10966 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10967 {
10968   int left      = player_action & JOY_LEFT;
10969   int right     = player_action & JOY_RIGHT;
10970   int up        = player_action & JOY_UP;
10971   int down      = player_action & JOY_DOWN;
10972   int button1   = player_action & JOY_BUTTON_1;
10973   int button2   = player_action & JOY_BUTTON_2;
10974   int dx        = (left ? -1 : right ? 1 : 0);
10975   int dy        = (up   ? -1 : down  ? 1 : 0);
10976
10977   if (!player->active || tape.pausing)
10978     return 0;
10979
10980   if (player_action)
10981   {
10982     if (button1)
10983       SnapField(player, dx, dy);
10984     else
10985     {
10986       if (button2)
10987         DropElement(player);
10988
10989       MovePlayer(player, dx, dy);
10990     }
10991
10992     CheckSingleStepMode(player);
10993
10994     SetPlayerWaiting(player, FALSE);
10995
10996     return player_action;
10997   }
10998   else
10999   {
11000     /* no actions for this player (no input at player's configured device) */
11001
11002     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11003     SnapField(player, 0, 0);
11004     CheckGravityMovementWhenNotMoving(player);
11005
11006     if (player->MovPos == 0)
11007       SetPlayerWaiting(player, TRUE);
11008
11009     if (player->MovPos == 0)    /* needed for tape.playing */
11010       player->is_moving = FALSE;
11011
11012     player->is_dropping = FALSE;
11013     player->is_dropping_pressed = FALSE;
11014     player->drop_pressed_delay = 0;
11015
11016     CheckSingleStepMode(player);
11017
11018     return 0;
11019   }
11020 }
11021
11022 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11023                                          byte *tape_action)
11024 {
11025   if (!tape.use_mouse)
11026     return;
11027
11028   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11029   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11030   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11031 }
11032
11033 static void SetTapeActionFromMouseAction(byte *tape_action,
11034                                          struct MouseActionInfo *mouse_action)
11035 {
11036   if (!tape.use_mouse)
11037     return;
11038
11039   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11040   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11041   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11042 }
11043
11044 static void CheckLevelTime()
11045 {
11046   int i;
11047
11048   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11049   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11050   {
11051     if (level.native_em_level->lev->home == 0)  /* all players at home */
11052     {
11053       PlayerWins(local_player);
11054
11055       AllPlayersGone = TRUE;
11056
11057       level.native_em_level->lev->home = -1;
11058     }
11059
11060     if (level.native_em_level->ply[0]->alive == 0 &&
11061         level.native_em_level->ply[1]->alive == 0 &&
11062         level.native_em_level->ply[2]->alive == 0 &&
11063         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11064       AllPlayersGone = TRUE;
11065   }
11066   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11067   {
11068     if (game_sp.LevelSolved &&
11069         !game_sp.GameOver)                              /* game won */
11070     {
11071       PlayerWins(local_player);
11072
11073       game_sp.GameOver = TRUE;
11074
11075       AllPlayersGone = TRUE;
11076     }
11077
11078     if (game_sp.GameOver)                               /* game lost */
11079       AllPlayersGone = TRUE;
11080   }
11081   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11082   {
11083     if (game_mm.level_solved &&
11084         !game_mm.game_over)                             /* game won */
11085     {
11086       PlayerWins(local_player);
11087
11088       game_mm.game_over = TRUE;
11089
11090       AllPlayersGone = TRUE;
11091     }
11092
11093     if (game_mm.game_over)                              /* game lost */
11094       AllPlayersGone = TRUE;
11095   }
11096
11097   if (TimeFrames >= FRAMES_PER_SECOND)
11098   {
11099     TimeFrames = 0;
11100     TapeTime++;
11101
11102     for (i = 0; i < MAX_PLAYERS; i++)
11103     {
11104       struct PlayerInfo *player = &stored_player[i];
11105
11106       if (SHIELD_ON(player))
11107       {
11108         player->shield_normal_time_left--;
11109
11110         if (player->shield_deadly_time_left > 0)
11111           player->shield_deadly_time_left--;
11112       }
11113     }
11114
11115     if (!local_player->LevelSolved && !level.use_step_counter)
11116     {
11117       TimePlayed++;
11118
11119       if (TimeLeft > 0)
11120       {
11121         TimeLeft--;
11122
11123         if (TimeLeft <= 10 && setup.time_limit)
11124           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11125
11126         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11127            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11128
11129         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11130
11131         if (!TimeLeft && setup.time_limit)
11132         {
11133           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11134             level.native_em_level->lev->killed_out_of_time = TRUE;
11135           else
11136             for (i = 0; i < MAX_PLAYERS; i++)
11137               KillPlayer(&stored_player[i]);
11138         }
11139       }
11140       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11141       {
11142         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11143       }
11144
11145       level.native_em_level->lev->time =
11146         (game.no_time_limit ? TimePlayed : TimeLeft);
11147     }
11148
11149     if (tape.recording || tape.playing)
11150       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11151   }
11152
11153   if (tape.recording || tape.playing)
11154     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11155
11156   UpdateAndDisplayGameControlValues();
11157 }
11158
11159 void AdvanceFrameAndPlayerCounters(int player_nr)
11160 {
11161   int i;
11162
11163   /* advance frame counters (global frame counter and time frame counter) */
11164   FrameCounter++;
11165   TimeFrames++;
11166
11167   /* advance player counters (counters for move delay, move animation etc.) */
11168   for (i = 0; i < MAX_PLAYERS; i++)
11169   {
11170     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11171     int move_delay_value = stored_player[i].move_delay_value;
11172     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11173
11174     if (!advance_player_counters)       /* not all players may be affected */
11175       continue;
11176
11177     if (move_frames == 0)       /* less than one move per game frame */
11178     {
11179       int stepsize = TILEX / move_delay_value;
11180       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11181       int count = (stored_player[i].is_moving ?
11182                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11183
11184       if (count % delay == 0)
11185         move_frames = 1;
11186     }
11187
11188     stored_player[i].Frame += move_frames;
11189
11190     if (stored_player[i].MovPos != 0)
11191       stored_player[i].StepFrame += move_frames;
11192
11193     if (stored_player[i].move_delay > 0)
11194       stored_player[i].move_delay--;
11195
11196     /* due to bugs in previous versions, counter must count up, not down */
11197     if (stored_player[i].push_delay != -1)
11198       stored_player[i].push_delay++;
11199
11200     if (stored_player[i].drop_delay > 0)
11201       stored_player[i].drop_delay--;
11202
11203     if (stored_player[i].is_dropping_pressed)
11204       stored_player[i].drop_pressed_delay++;
11205   }
11206 }
11207
11208 void StartGameActions(boolean init_network_game, boolean record_tape,
11209                       int random_seed)
11210 {
11211   unsigned int new_random_seed = InitRND(random_seed);
11212
11213   if (record_tape)
11214     TapeStartRecording(new_random_seed);
11215
11216   if (init_network_game)
11217   {
11218     SendToServer_StartPlaying();
11219
11220     return;
11221   }
11222
11223   InitGame();
11224 }
11225
11226 void GameActionsExt()
11227 {
11228 #if 0
11229   static unsigned int game_frame_delay = 0;
11230 #endif
11231   unsigned int game_frame_delay_value;
11232   byte *recorded_player_action;
11233   byte summarized_player_action = 0;
11234   byte tape_action[MAX_PLAYERS];
11235   int i;
11236
11237   /* detect endless loops, caused by custom element programming */
11238   if (recursion_loop_detected && recursion_loop_depth == 0)
11239   {
11240     char *message = getStringCat3("Internal Error! Element ",
11241                                   EL_NAME(recursion_loop_element),
11242                                   " caused endless loop! Quit the game?");
11243
11244     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11245           EL_NAME(recursion_loop_element));
11246
11247     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11248
11249     recursion_loop_detected = FALSE;    /* if game should be continued */
11250
11251     free(message);
11252
11253     return;
11254   }
11255
11256   if (game.restart_level)
11257     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11258
11259   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11260   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11261   {
11262     if (level.native_em_level->lev->home == 0)  /* all players at home */
11263     {
11264       PlayerWins(local_player);
11265
11266       AllPlayersGone = TRUE;
11267
11268       level.native_em_level->lev->home = -1;
11269     }
11270
11271     if (level.native_em_level->ply[0]->alive == 0 &&
11272         level.native_em_level->ply[1]->alive == 0 &&
11273         level.native_em_level->ply[2]->alive == 0 &&
11274         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11275       AllPlayersGone = TRUE;
11276   }
11277   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11278   {
11279     if (game_sp.LevelSolved &&
11280         !game_sp.GameOver)                              /* game won */
11281     {
11282       PlayerWins(local_player);
11283
11284       game_sp.GameOver = TRUE;
11285
11286       AllPlayersGone = TRUE;
11287     }
11288
11289     if (game_sp.GameOver)                               /* game lost */
11290       AllPlayersGone = TRUE;
11291   }
11292   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11293   {
11294     if (game_mm.level_solved &&
11295         !game_mm.game_over)                             /* game won */
11296     {
11297       PlayerWins(local_player);
11298
11299       game_mm.game_over = TRUE;
11300
11301       AllPlayersGone = TRUE;
11302     }
11303
11304     if (game_mm.game_over)                              /* game lost */
11305       AllPlayersGone = TRUE;
11306   }
11307
11308   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11309     GameWon();
11310
11311   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11312     TapeStop();
11313
11314   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11315     return;
11316
11317   game_frame_delay_value =
11318     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11319
11320   if (tape.playing && tape.warp_forward && !tape.pausing)
11321     game_frame_delay_value = 0;
11322
11323   SetVideoFrameDelay(game_frame_delay_value);
11324
11325 #if 0
11326 #if 0
11327   /* ---------- main game synchronization point ---------- */
11328
11329   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11330
11331   printf("::: skip == %d\n", skip);
11332
11333 #else
11334   /* ---------- main game synchronization point ---------- */
11335
11336   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11337 #endif
11338 #endif
11339
11340   if (network_playing && !network_player_action_received)
11341   {
11342     /* try to get network player actions in time */
11343
11344     /* last chance to get network player actions without main loop delay */
11345     HandleNetworking();
11346
11347     /* game was quit by network peer */
11348     if (game_status != GAME_MODE_PLAYING)
11349       return;
11350
11351     if (!network_player_action_received)
11352       return;           /* failed to get network player actions in time */
11353
11354     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11355   }
11356
11357   if (tape.pausing)
11358     return;
11359
11360   /* at this point we know that we really continue executing the game */
11361
11362   network_player_action_received = FALSE;
11363
11364   /* when playing tape, read previously recorded player input from tape data */
11365   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11366
11367   local_player->effective_mouse_action = local_player->mouse_action;
11368
11369   if (recorded_player_action != NULL)
11370     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11371                                  recorded_player_action);
11372
11373   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11374   if (tape.pausing)
11375     return;
11376
11377   if (tape.set_centered_player)
11378   {
11379     game.centered_player_nr_next = tape.centered_player_nr_next;
11380     game.set_centered_player = TRUE;
11381   }
11382
11383   for (i = 0; i < MAX_PLAYERS; i++)
11384   {
11385     summarized_player_action |= stored_player[i].action;
11386
11387     if (!network_playing && (game.team_mode || tape.playing))
11388       stored_player[i].effective_action = stored_player[i].action;
11389   }
11390
11391   if (network_playing)
11392     SendToServer_MovePlayer(summarized_player_action);
11393
11394   // summarize all actions at local players mapped input device position
11395   // (this allows using different input devices in single player mode)
11396   if (!network.enabled && !game.team_mode)
11397     stored_player[map_player_action[local_player->index_nr]].effective_action =
11398       summarized_player_action;
11399
11400   if (tape.recording &&
11401       setup.team_mode &&
11402       setup.input_on_focus &&
11403       game.centered_player_nr != -1)
11404   {
11405     for (i = 0; i < MAX_PLAYERS; i++)
11406       stored_player[i].effective_action =
11407         (i == game.centered_player_nr ? summarized_player_action : 0);
11408   }
11409
11410   if (recorded_player_action != NULL)
11411     for (i = 0; i < MAX_PLAYERS; i++)
11412       stored_player[i].effective_action = recorded_player_action[i];
11413
11414   for (i = 0; i < MAX_PLAYERS; i++)
11415   {
11416     tape_action[i] = stored_player[i].effective_action;
11417
11418     /* (this may happen in the RND game engine if a player was not present on
11419        the playfield on level start, but appeared later from a custom element */
11420     if (setup.team_mode &&
11421         tape.recording &&
11422         tape_action[i] &&
11423         !tape.player_participates[i])
11424       tape.player_participates[i] = TRUE;
11425   }
11426
11427   SetTapeActionFromMouseAction(tape_action,
11428                                &local_player->effective_mouse_action);
11429
11430   /* only record actions from input devices, but not programmed actions */
11431   if (tape.recording)
11432     TapeRecordAction(tape_action);
11433
11434 #if USE_NEW_PLAYER_ASSIGNMENTS
11435   // !!! also map player actions in single player mode !!!
11436   // if (game.team_mode)
11437   if (1)
11438   {
11439     byte mapped_action[MAX_PLAYERS];
11440
11441 #if DEBUG_PLAYER_ACTIONS
11442     printf(":::");
11443     for (i = 0; i < MAX_PLAYERS; i++)
11444       printf(" %d, ", stored_player[i].effective_action);
11445 #endif
11446
11447     for (i = 0; i < MAX_PLAYERS; i++)
11448       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11449
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451       stored_player[i].effective_action = mapped_action[i];
11452
11453 #if DEBUG_PLAYER_ACTIONS
11454     printf(" =>");
11455     for (i = 0; i < MAX_PLAYERS; i++)
11456       printf(" %d, ", stored_player[i].effective_action);
11457     printf("\n");
11458 #endif
11459   }
11460 #if DEBUG_PLAYER_ACTIONS
11461   else
11462   {
11463     printf(":::");
11464     for (i = 0; i < MAX_PLAYERS; i++)
11465       printf(" %d, ", stored_player[i].effective_action);
11466     printf("\n");
11467   }
11468 #endif
11469 #endif
11470
11471   for (i = 0; i < MAX_PLAYERS; i++)
11472   {
11473     // allow engine snapshot in case of changed movement attempt
11474     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11475         (stored_player[i].effective_action & KEY_MOTION))
11476       game.snapshot.changed_action = TRUE;
11477
11478     // allow engine snapshot in case of snapping/dropping attempt
11479     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11480         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11481       game.snapshot.changed_action = TRUE;
11482
11483     game.snapshot.last_action[i] = stored_player[i].effective_action;
11484   }
11485
11486   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11487   {
11488     GameActions_EM_Main();
11489   }
11490   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11491   {
11492     GameActions_SP_Main();
11493   }
11494   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11495   {
11496     GameActions_MM_Main();
11497   }
11498   else
11499   {
11500     GameActions_RND_Main();
11501   }
11502
11503   BlitScreenToBitmap(backbuffer);
11504
11505   CheckLevelTime();
11506
11507   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11508
11509   if (global.show_frames_per_second)
11510   {
11511     static unsigned int fps_counter = 0;
11512     static int fps_frames = 0;
11513     unsigned int fps_delay_ms = Counter() - fps_counter;
11514
11515     fps_frames++;
11516
11517     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11518     {
11519       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11520
11521       fps_frames = 0;
11522       fps_counter = Counter();
11523
11524       /* always draw FPS to screen after FPS value was updated */
11525       redraw_mask |= REDRAW_FPS;
11526     }
11527
11528     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11529     if (GetDrawDeactivationMask() == REDRAW_NONE)
11530       redraw_mask |= REDRAW_FPS;
11531   }
11532 }
11533
11534 static void GameActions_CheckSaveEngineSnapshot()
11535 {
11536   if (!game.snapshot.save_snapshot)
11537     return;
11538
11539   // clear flag for saving snapshot _before_ saving snapshot
11540   game.snapshot.save_snapshot = FALSE;
11541
11542   SaveEngineSnapshotToList();
11543 }
11544
11545 void GameActions()
11546 {
11547   GameActionsExt();
11548
11549   GameActions_CheckSaveEngineSnapshot();
11550 }
11551
11552 void GameActions_EM_Main()
11553 {
11554   byte effective_action[MAX_PLAYERS];
11555   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11556   int i;
11557
11558   for (i = 0; i < MAX_PLAYERS; i++)
11559     effective_action[i] = stored_player[i].effective_action;
11560
11561   GameActions_EM(effective_action, warp_mode);
11562 }
11563
11564 void GameActions_SP_Main()
11565 {
11566   byte effective_action[MAX_PLAYERS];
11567   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11568   int i;
11569
11570   for (i = 0; i < MAX_PLAYERS; i++)
11571     effective_action[i] = stored_player[i].effective_action;
11572
11573   GameActions_SP(effective_action, warp_mode);
11574
11575   for (i = 0; i < MAX_PLAYERS; i++)
11576   {
11577     if (stored_player[i].force_dropping)
11578       stored_player[i].action |= KEY_BUTTON_DROP;
11579
11580     stored_player[i].force_dropping = FALSE;
11581   }
11582 }
11583
11584 void GameActions_MM_Main()
11585 {
11586   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11587
11588   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11589 }
11590
11591 void GameActions_RND_Main()
11592 {
11593   GameActions_RND();
11594 }
11595
11596 void GameActions_RND()
11597 {
11598   int magic_wall_x = 0, magic_wall_y = 0;
11599   int i, x, y, element, graphic, last_gfx_frame;
11600
11601   InitPlayfieldScanModeVars();
11602
11603   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11604   {
11605     SCAN_PLAYFIELD(x, y)
11606     {
11607       ChangeCount[x][y] = 0;
11608       ChangeEvent[x][y] = -1;
11609     }
11610   }
11611
11612   if (game.set_centered_player)
11613   {
11614     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11615
11616     /* switching to "all players" only possible if all players fit to screen */
11617     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11618     {
11619       game.centered_player_nr_next = game.centered_player_nr;
11620       game.set_centered_player = FALSE;
11621     }
11622
11623     /* do not switch focus to non-existing (or non-active) player */
11624     if (game.centered_player_nr_next >= 0 &&
11625         !stored_player[game.centered_player_nr_next].active)
11626     {
11627       game.centered_player_nr_next = game.centered_player_nr;
11628       game.set_centered_player = FALSE;
11629     }
11630   }
11631
11632   if (game.set_centered_player &&
11633       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11634   {
11635     int sx, sy;
11636
11637     if (game.centered_player_nr_next == -1)
11638     {
11639       setScreenCenteredToAllPlayers(&sx, &sy);
11640     }
11641     else
11642     {
11643       sx = stored_player[game.centered_player_nr_next].jx;
11644       sy = stored_player[game.centered_player_nr_next].jy;
11645     }
11646
11647     game.centered_player_nr = game.centered_player_nr_next;
11648     game.set_centered_player = FALSE;
11649
11650     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11651     DrawGameDoorValues();
11652   }
11653
11654   for (i = 0; i < MAX_PLAYERS; i++)
11655   {
11656     int actual_player_action = stored_player[i].effective_action;
11657
11658 #if 1
11659     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11660        - rnd_equinox_tetrachloride 048
11661        - rnd_equinox_tetrachloride_ii 096
11662        - rnd_emanuel_schmieg 002
11663        - doctor_sloan_ww 001, 020
11664     */
11665     if (stored_player[i].MovPos == 0)
11666       CheckGravityMovement(&stored_player[i]);
11667 #endif
11668
11669     /* overwrite programmed action with tape action */
11670     if (stored_player[i].programmed_action)
11671       actual_player_action = stored_player[i].programmed_action;
11672
11673     PlayerActions(&stored_player[i], actual_player_action);
11674
11675     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11676   }
11677
11678   ScrollScreen(NULL, SCROLL_GO_ON);
11679
11680   /* for backwards compatibility, the following code emulates a fixed bug that
11681      occured when pushing elements (causing elements that just made their last
11682      pushing step to already (if possible) make their first falling step in the
11683      same game frame, which is bad); this code is also needed to use the famous
11684      "spring push bug" which is used in older levels and might be wanted to be
11685      used also in newer levels, but in this case the buggy pushing code is only
11686      affecting the "spring" element and no other elements */
11687
11688   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11689   {
11690     for (i = 0; i < MAX_PLAYERS; i++)
11691     {
11692       struct PlayerInfo *player = &stored_player[i];
11693       int x = player->jx;
11694       int y = player->jy;
11695
11696       if (player->active && player->is_pushing && player->is_moving &&
11697           IS_MOVING(x, y) &&
11698           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11699            Feld[x][y] == EL_SPRING))
11700       {
11701         ContinueMoving(x, y);
11702
11703         /* continue moving after pushing (this is actually a bug) */
11704         if (!IS_MOVING(x, y))
11705           Stop[x][y] = FALSE;
11706       }
11707     }
11708   }
11709
11710   SCAN_PLAYFIELD(x, y)
11711   {
11712     ChangeCount[x][y] = 0;
11713     ChangeEvent[x][y] = -1;
11714
11715     /* this must be handled before main playfield loop */
11716     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11717     {
11718       MovDelay[x][y]--;
11719       if (MovDelay[x][y] <= 0)
11720         RemoveField(x, y);
11721     }
11722
11723     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11724     {
11725       MovDelay[x][y]--;
11726       if (MovDelay[x][y] <= 0)
11727       {
11728         RemoveField(x, y);
11729         TEST_DrawLevelField(x, y);
11730
11731         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11732       }
11733     }
11734
11735 #if DEBUG
11736     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11737     {
11738       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11739       printf("GameActions(): This should never happen!\n");
11740
11741       ChangePage[x][y] = -1;
11742     }
11743 #endif
11744
11745     Stop[x][y] = FALSE;
11746     if (WasJustMoving[x][y] > 0)
11747       WasJustMoving[x][y]--;
11748     if (WasJustFalling[x][y] > 0)
11749       WasJustFalling[x][y]--;
11750     if (CheckCollision[x][y] > 0)
11751       CheckCollision[x][y]--;
11752     if (CheckImpact[x][y] > 0)
11753       CheckImpact[x][y]--;
11754
11755     GfxFrame[x][y]++;
11756
11757     /* reset finished pushing action (not done in ContinueMoving() to allow
11758        continuous pushing animation for elements with zero push delay) */
11759     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11760     {
11761       ResetGfxAnimation(x, y);
11762       TEST_DrawLevelField(x, y);
11763     }
11764
11765 #if DEBUG
11766     if (IS_BLOCKED(x, y))
11767     {
11768       int oldx, oldy;
11769
11770       Blocked2Moving(x, y, &oldx, &oldy);
11771       if (!IS_MOVING(oldx, oldy))
11772       {
11773         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11774         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11775         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11776         printf("GameActions(): This should never happen!\n");
11777       }
11778     }
11779 #endif
11780   }
11781
11782   SCAN_PLAYFIELD(x, y)
11783   {
11784     element = Feld[x][y];
11785     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11786     last_gfx_frame = GfxFrame[x][y];
11787
11788     ResetGfxFrame(x, y);
11789
11790     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11791       DrawLevelGraphicAnimation(x, y, graphic);
11792
11793     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11794         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11795       ResetRandomAnimationValue(x, y);
11796
11797     SetRandomAnimationValue(x, y);
11798
11799     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11800
11801     if (IS_INACTIVE(element))
11802     {
11803       if (IS_ANIMATED(graphic))
11804         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11805
11806       continue;
11807     }
11808
11809     /* this may take place after moving, so 'element' may have changed */
11810     if (IS_CHANGING(x, y) &&
11811         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11812     {
11813       int page = element_info[element].event_page_nr[CE_DELAY];
11814
11815       HandleElementChange(x, y, page);
11816
11817       element = Feld[x][y];
11818       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11819     }
11820
11821     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11822     {
11823       StartMoving(x, y);
11824
11825       element = Feld[x][y];
11826       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827
11828       if (IS_ANIMATED(graphic) &&
11829           !IS_MOVING(x, y) &&
11830           !Stop[x][y])
11831         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11832
11833       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11834         TEST_DrawTwinkleOnField(x, y);
11835     }
11836     else if (element == EL_ACID)
11837     {
11838       if (!Stop[x][y])
11839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840     }
11841     else if ((element == EL_EXIT_OPEN ||
11842               element == EL_EM_EXIT_OPEN ||
11843               element == EL_SP_EXIT_OPEN ||
11844               element == EL_STEEL_EXIT_OPEN ||
11845               element == EL_EM_STEEL_EXIT_OPEN ||
11846               element == EL_SP_TERMINAL ||
11847               element == EL_SP_TERMINAL_ACTIVE ||
11848               element == EL_EXTRA_TIME ||
11849               element == EL_SHIELD_NORMAL ||
11850               element == EL_SHIELD_DEADLY) &&
11851              IS_ANIMATED(graphic))
11852       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11853     else if (IS_MOVING(x, y))
11854       ContinueMoving(x, y);
11855     else if (IS_ACTIVE_BOMB(element))
11856       CheckDynamite(x, y);
11857     else if (element == EL_AMOEBA_GROWING)
11858       AmoebeWaechst(x, y);
11859     else if (element == EL_AMOEBA_SHRINKING)
11860       AmoebaDisappearing(x, y);
11861
11862 #if !USE_NEW_AMOEBA_CODE
11863     else if (IS_AMOEBALIVE(element))
11864       AmoebeAbleger(x, y);
11865 #endif
11866
11867     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11868       Life(x, y);
11869     else if (element == EL_EXIT_CLOSED)
11870       CheckExit(x, y);
11871     else if (element == EL_EM_EXIT_CLOSED)
11872       CheckExitEM(x, y);
11873     else if (element == EL_STEEL_EXIT_CLOSED)
11874       CheckExitSteel(x, y);
11875     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11876       CheckExitSteelEM(x, y);
11877     else if (element == EL_SP_EXIT_CLOSED)
11878       CheckExitSP(x, y);
11879     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11880              element == EL_EXPANDABLE_STEELWALL_GROWING)
11881       MauerWaechst(x, y);
11882     else if (element == EL_EXPANDABLE_WALL ||
11883              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11884              element == EL_EXPANDABLE_WALL_VERTICAL ||
11885              element == EL_EXPANDABLE_WALL_ANY ||
11886              element == EL_BD_EXPANDABLE_WALL)
11887       MauerAbleger(x, y);
11888     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11889              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11890              element == EL_EXPANDABLE_STEELWALL_ANY)
11891       MauerAblegerStahl(x, y);
11892     else if (element == EL_FLAMES)
11893       CheckForDragon(x, y);
11894     else if (element == EL_EXPLOSION)
11895       ; /* drawing of correct explosion animation is handled separately */
11896     else if (element == EL_ELEMENT_SNAPPING ||
11897              element == EL_DIAGONAL_SHRINKING ||
11898              element == EL_DIAGONAL_GROWING)
11899     {
11900       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11901
11902       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11903     }
11904     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11905       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11906
11907     if (IS_BELT_ACTIVE(element))
11908       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11909
11910     if (game.magic_wall_active)
11911     {
11912       int jx = local_player->jx, jy = local_player->jy;
11913
11914       /* play the element sound at the position nearest to the player */
11915       if ((element == EL_MAGIC_WALL_FULL ||
11916            element == EL_MAGIC_WALL_ACTIVE ||
11917            element == EL_MAGIC_WALL_EMPTYING ||
11918            element == EL_BD_MAGIC_WALL_FULL ||
11919            element == EL_BD_MAGIC_WALL_ACTIVE ||
11920            element == EL_BD_MAGIC_WALL_EMPTYING ||
11921            element == EL_DC_MAGIC_WALL_FULL ||
11922            element == EL_DC_MAGIC_WALL_ACTIVE ||
11923            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11924           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11925       {
11926         magic_wall_x = x;
11927         magic_wall_y = y;
11928       }
11929     }
11930   }
11931
11932 #if USE_NEW_AMOEBA_CODE
11933   /* new experimental amoeba growth stuff */
11934   if (!(FrameCounter % 8))
11935   {
11936     static unsigned int random = 1684108901;
11937
11938     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11939     {
11940       x = RND(lev_fieldx);
11941       y = RND(lev_fieldy);
11942       element = Feld[x][y];
11943
11944       if (!IS_PLAYER(x,y) &&
11945           (element == EL_EMPTY ||
11946            CAN_GROW_INTO(element) ||
11947            element == EL_QUICKSAND_EMPTY ||
11948            element == EL_QUICKSAND_FAST_EMPTY ||
11949            element == EL_ACID_SPLASH_LEFT ||
11950            element == EL_ACID_SPLASH_RIGHT))
11951       {
11952         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11953             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11954             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11955             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11956           Feld[x][y] = EL_AMOEBA_DROP;
11957       }
11958
11959       random = random * 129 + 1;
11960     }
11961   }
11962 #endif
11963
11964   game.explosions_delayed = FALSE;
11965
11966   SCAN_PLAYFIELD(x, y)
11967   {
11968     element = Feld[x][y];
11969
11970     if (ExplodeField[x][y])
11971       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11972     else if (element == EL_EXPLOSION)
11973       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11974
11975     ExplodeField[x][y] = EX_TYPE_NONE;
11976   }
11977
11978   game.explosions_delayed = TRUE;
11979
11980   if (game.magic_wall_active)
11981   {
11982     if (!(game.magic_wall_time_left % 4))
11983     {
11984       int element = Feld[magic_wall_x][magic_wall_y];
11985
11986       if (element == EL_BD_MAGIC_WALL_FULL ||
11987           element == EL_BD_MAGIC_WALL_ACTIVE ||
11988           element == EL_BD_MAGIC_WALL_EMPTYING)
11989         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11990       else if (element == EL_DC_MAGIC_WALL_FULL ||
11991                element == EL_DC_MAGIC_WALL_ACTIVE ||
11992                element == EL_DC_MAGIC_WALL_EMPTYING)
11993         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11994       else
11995         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11996     }
11997
11998     if (game.magic_wall_time_left > 0)
11999     {
12000       game.magic_wall_time_left--;
12001
12002       if (!game.magic_wall_time_left)
12003       {
12004         SCAN_PLAYFIELD(x, y)
12005         {
12006           element = Feld[x][y];
12007
12008           if (element == EL_MAGIC_WALL_ACTIVE ||
12009               element == EL_MAGIC_WALL_FULL)
12010           {
12011             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12012             TEST_DrawLevelField(x, y);
12013           }
12014           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12015                    element == EL_BD_MAGIC_WALL_FULL)
12016           {
12017             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12018             TEST_DrawLevelField(x, y);
12019           }
12020           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12021                    element == EL_DC_MAGIC_WALL_FULL)
12022           {
12023             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12024             TEST_DrawLevelField(x, y);
12025           }
12026         }
12027
12028         game.magic_wall_active = FALSE;
12029       }
12030     }
12031   }
12032
12033   if (game.light_time_left > 0)
12034   {
12035     game.light_time_left--;
12036
12037     if (game.light_time_left == 0)
12038       RedrawAllLightSwitchesAndInvisibleElements();
12039   }
12040
12041   if (game.timegate_time_left > 0)
12042   {
12043     game.timegate_time_left--;
12044
12045     if (game.timegate_time_left == 0)
12046       CloseAllOpenTimegates();
12047   }
12048
12049   if (game.lenses_time_left > 0)
12050   {
12051     game.lenses_time_left--;
12052
12053     if (game.lenses_time_left == 0)
12054       RedrawAllInvisibleElementsForLenses();
12055   }
12056
12057   if (game.magnify_time_left > 0)
12058   {
12059     game.magnify_time_left--;
12060
12061     if (game.magnify_time_left == 0)
12062       RedrawAllInvisibleElementsForMagnifier();
12063   }
12064
12065   for (i = 0; i < MAX_PLAYERS; i++)
12066   {
12067     struct PlayerInfo *player = &stored_player[i];
12068
12069     if (SHIELD_ON(player))
12070     {
12071       if (player->shield_deadly_time_left)
12072         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12073       else if (player->shield_normal_time_left)
12074         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12075     }
12076   }
12077
12078 #if USE_DELAYED_GFX_REDRAW
12079   SCAN_PLAYFIELD(x, y)
12080   {
12081     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12082     {
12083       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12084          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12085
12086       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12087         DrawLevelField(x, y);
12088
12089       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12090         DrawLevelFieldCrumbled(x, y);
12091
12092       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12093         DrawLevelFieldCrumbledNeighbours(x, y);
12094
12095       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12096         DrawTwinkleOnField(x, y);
12097     }
12098
12099     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12100   }
12101 #endif
12102
12103   DrawAllPlayers();
12104   PlayAllPlayersSound();
12105
12106   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12107   {
12108     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12109
12110     local_player->show_envelope = 0;
12111   }
12112
12113   /* use random number generator in every frame to make it less predictable */
12114   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12115     RND(1);
12116 }
12117
12118 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12119 {
12120   int min_x = x, min_y = y, max_x = x, max_y = y;
12121   int i;
12122
12123   for (i = 0; i < MAX_PLAYERS; i++)
12124   {
12125     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12126
12127     if (!stored_player[i].active || &stored_player[i] == player)
12128       continue;
12129
12130     min_x = MIN(min_x, jx);
12131     min_y = MIN(min_y, jy);
12132     max_x = MAX(max_x, jx);
12133     max_y = MAX(max_y, jy);
12134   }
12135
12136   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12137 }
12138
12139 static boolean AllPlayersInVisibleScreen()
12140 {
12141   int i;
12142
12143   for (i = 0; i < MAX_PLAYERS; i++)
12144   {
12145     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12146
12147     if (!stored_player[i].active)
12148       continue;
12149
12150     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12151       return FALSE;
12152   }
12153
12154   return TRUE;
12155 }
12156
12157 void ScrollLevel(int dx, int dy)
12158 {
12159   int scroll_offset = 2 * TILEX_VAR;
12160   int x, y;
12161
12162   BlitBitmap(drawto_field, drawto_field,
12163              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12164              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12165              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12166              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12167              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12168              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12169
12170   if (dx != 0)
12171   {
12172     x = (dx == 1 ? BX1 : BX2);
12173     for (y = BY1; y <= BY2; y++)
12174       DrawScreenField(x, y);
12175   }
12176
12177   if (dy != 0)
12178   {
12179     y = (dy == 1 ? BY1 : BY2);
12180     for (x = BX1; x <= BX2; x++)
12181       DrawScreenField(x, y);
12182   }
12183
12184   redraw_mask |= REDRAW_FIELD;
12185 }
12186
12187 static boolean canFallDown(struct PlayerInfo *player)
12188 {
12189   int jx = player->jx, jy = player->jy;
12190
12191   return (IN_LEV_FIELD(jx, jy + 1) &&
12192           (IS_FREE(jx, jy + 1) ||
12193            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12194           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12195           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12196 }
12197
12198 static boolean canPassField(int x, int y, int move_dir)
12199 {
12200   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12201   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12202   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12203   int nextx = x + dx;
12204   int nexty = y + dy;
12205   int element = Feld[x][y];
12206
12207   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12208           !CAN_MOVE(element) &&
12209           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12210           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12211           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12212 }
12213
12214 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12215 {
12216   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12217   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12218   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12219   int newx = x + dx;
12220   int newy = y + dy;
12221
12222   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12223           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12224           (IS_DIGGABLE(Feld[newx][newy]) ||
12225            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12226            canPassField(newx, newy, move_dir)));
12227 }
12228
12229 static void CheckGravityMovement(struct PlayerInfo *player)
12230 {
12231   if (player->gravity && !player->programmed_action)
12232   {
12233     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12234     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12235     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12236     int jx = player->jx, jy = player->jy;
12237     boolean player_is_moving_to_valid_field =
12238       (!player_is_snapping &&
12239        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12240         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12241     boolean player_can_fall_down = canFallDown(player);
12242
12243     if (player_can_fall_down &&
12244         !player_is_moving_to_valid_field)
12245       player->programmed_action = MV_DOWN;
12246   }
12247 }
12248
12249 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12250 {
12251   return CheckGravityMovement(player);
12252
12253   if (player->gravity && !player->programmed_action)
12254   {
12255     int jx = player->jx, jy = player->jy;
12256     boolean field_under_player_is_free =
12257       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12258     boolean player_is_standing_on_valid_field =
12259       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12260        (IS_WALKABLE(Feld[jx][jy]) &&
12261         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12262
12263     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12264       player->programmed_action = MV_DOWN;
12265   }
12266 }
12267
12268 /*
12269   MovePlayerOneStep()
12270   -----------------------------------------------------------------------------
12271   dx, dy:               direction (non-diagonal) to try to move the player to
12272   real_dx, real_dy:     direction as read from input device (can be diagonal)
12273 */
12274
12275 boolean MovePlayerOneStep(struct PlayerInfo *player,
12276                           int dx, int dy, int real_dx, int real_dy)
12277 {
12278   int jx = player->jx, jy = player->jy;
12279   int new_jx = jx + dx, new_jy = jy + dy;
12280   int can_move;
12281   boolean player_can_move = !player->cannot_move;
12282
12283   if (!player->active || (!dx && !dy))
12284     return MP_NO_ACTION;
12285
12286   player->MovDir = (dx < 0 ? MV_LEFT :
12287                     dx > 0 ? MV_RIGHT :
12288                     dy < 0 ? MV_UP :
12289                     dy > 0 ? MV_DOWN :  MV_NONE);
12290
12291   if (!IN_LEV_FIELD(new_jx, new_jy))
12292     return MP_NO_ACTION;
12293
12294   if (!player_can_move)
12295   {
12296     if (player->MovPos == 0)
12297     {
12298       player->is_moving = FALSE;
12299       player->is_digging = FALSE;
12300       player->is_collecting = FALSE;
12301       player->is_snapping = FALSE;
12302       player->is_pushing = FALSE;
12303     }
12304   }
12305
12306   if (!network.enabled && game.centered_player_nr == -1 &&
12307       !AllPlayersInSight(player, new_jx, new_jy))
12308     return MP_NO_ACTION;
12309
12310   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12311   if (can_move != MP_MOVING)
12312     return can_move;
12313
12314   /* check if DigField() has caused relocation of the player */
12315   if (player->jx != jx || player->jy != jy)
12316     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12317
12318   StorePlayer[jx][jy] = 0;
12319   player->last_jx = jx;
12320   player->last_jy = jy;
12321   player->jx = new_jx;
12322   player->jy = new_jy;
12323   StorePlayer[new_jx][new_jy] = player->element_nr;
12324
12325   if (player->move_delay_value_next != -1)
12326   {
12327     player->move_delay_value = player->move_delay_value_next;
12328     player->move_delay_value_next = -1;
12329   }
12330
12331   player->MovPos =
12332     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12333
12334   player->step_counter++;
12335
12336   PlayerVisit[jx][jy] = FrameCounter;
12337
12338   player->is_moving = TRUE;
12339
12340 #if 1
12341   /* should better be called in MovePlayer(), but this breaks some tapes */
12342   ScrollPlayer(player, SCROLL_INIT);
12343 #endif
12344
12345   return MP_MOVING;
12346 }
12347
12348 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12349 {
12350   int jx = player->jx, jy = player->jy;
12351   int old_jx = jx, old_jy = jy;
12352   int moved = MP_NO_ACTION;
12353
12354   if (!player->active)
12355     return FALSE;
12356
12357   if (!dx && !dy)
12358   {
12359     if (player->MovPos == 0)
12360     {
12361       player->is_moving = FALSE;
12362       player->is_digging = FALSE;
12363       player->is_collecting = FALSE;
12364       player->is_snapping = FALSE;
12365       player->is_pushing = FALSE;
12366     }
12367
12368     return FALSE;
12369   }
12370
12371   if (player->move_delay > 0)
12372     return FALSE;
12373
12374   player->move_delay = -1;              /* set to "uninitialized" value */
12375
12376   /* store if player is automatically moved to next field */
12377   player->is_auto_moving = (player->programmed_action != MV_NONE);
12378
12379   /* remove the last programmed player action */
12380   player->programmed_action = 0;
12381
12382   if (player->MovPos)
12383   {
12384     /* should only happen if pre-1.2 tape recordings are played */
12385     /* this is only for backward compatibility */
12386
12387     int original_move_delay_value = player->move_delay_value;
12388
12389 #if DEBUG
12390     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12391            tape.counter);
12392 #endif
12393
12394     /* scroll remaining steps with finest movement resolution */
12395     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12396
12397     while (player->MovPos)
12398     {
12399       ScrollPlayer(player, SCROLL_GO_ON);
12400       ScrollScreen(NULL, SCROLL_GO_ON);
12401
12402       AdvanceFrameAndPlayerCounters(player->index_nr);
12403
12404       DrawAllPlayers();
12405       BackToFront_WithFrameDelay(0);
12406     }
12407
12408     player->move_delay_value = original_move_delay_value;
12409   }
12410
12411   player->is_active = FALSE;
12412
12413   if (player->last_move_dir & MV_HORIZONTAL)
12414   {
12415     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12416       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12417   }
12418   else
12419   {
12420     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12421       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12422   }
12423
12424   if (!moved && !player->is_active)
12425   {
12426     player->is_moving = FALSE;
12427     player->is_digging = FALSE;
12428     player->is_collecting = FALSE;
12429     player->is_snapping = FALSE;
12430     player->is_pushing = FALSE;
12431   }
12432
12433   jx = player->jx;
12434   jy = player->jy;
12435
12436   if (moved & MP_MOVING && !ScreenMovPos &&
12437       (player->index_nr == game.centered_player_nr ||
12438        game.centered_player_nr == -1))
12439   {
12440     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12441     int offset = game.scroll_delay_value;
12442
12443     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12444     {
12445       /* actual player has left the screen -- scroll in that direction */
12446       if (jx != old_jx)         /* player has moved horizontally */
12447         scroll_x += (jx - old_jx);
12448       else                      /* player has moved vertically */
12449         scroll_y += (jy - old_jy);
12450     }
12451     else
12452     {
12453       if (jx != old_jx)         /* player has moved horizontally */
12454       {
12455         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12456             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12457           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12458
12459         /* don't scroll over playfield boundaries */
12460         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12461           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12462
12463         /* don't scroll more than one field at a time */
12464         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12465
12466         /* don't scroll against the player's moving direction */
12467         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12468             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12469           scroll_x = old_scroll_x;
12470       }
12471       else                      /* player has moved vertically */
12472       {
12473         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12474             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12475           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12476
12477         /* don't scroll over playfield boundaries */
12478         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12479           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12480
12481         /* don't scroll more than one field at a time */
12482         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12483
12484         /* don't scroll against the player's moving direction */
12485         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12486             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12487           scroll_y = old_scroll_y;
12488       }
12489     }
12490
12491     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12492     {
12493       if (!network.enabled && game.centered_player_nr == -1 &&
12494           !AllPlayersInVisibleScreen())
12495       {
12496         scroll_x = old_scroll_x;
12497         scroll_y = old_scroll_y;
12498       }
12499       else
12500       {
12501         ScrollScreen(player, SCROLL_INIT);
12502         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12503       }
12504     }
12505   }
12506
12507   player->StepFrame = 0;
12508
12509   if (moved & MP_MOVING)
12510   {
12511     if (old_jx != jx && old_jy == jy)
12512       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12513     else if (old_jx == jx && old_jy != jy)
12514       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12515
12516     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12517
12518     player->last_move_dir = player->MovDir;
12519     player->is_moving = TRUE;
12520     player->is_snapping = FALSE;
12521     player->is_switching = FALSE;
12522     player->is_dropping = FALSE;
12523     player->is_dropping_pressed = FALSE;
12524     player->drop_pressed_delay = 0;
12525
12526 #if 0
12527     /* should better be called here than above, but this breaks some tapes */
12528     ScrollPlayer(player, SCROLL_INIT);
12529 #endif
12530   }
12531   else
12532   {
12533     CheckGravityMovementWhenNotMoving(player);
12534
12535     player->is_moving = FALSE;
12536
12537     /* at this point, the player is allowed to move, but cannot move right now
12538        (e.g. because of something blocking the way) -- ensure that the player
12539        is also allowed to move in the next frame (in old versions before 3.1.1,
12540        the player was forced to wait again for eight frames before next try) */
12541
12542     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12543       player->move_delay = 0;   /* allow direct movement in the next frame */
12544   }
12545
12546   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12547     player->move_delay = player->move_delay_value;
12548
12549   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12550   {
12551     TestIfPlayerTouchesBadThing(jx, jy);
12552     TestIfPlayerTouchesCustomElement(jx, jy);
12553   }
12554
12555   if (!player->active)
12556     RemovePlayer(player);
12557
12558   return moved;
12559 }
12560
12561 void ScrollPlayer(struct PlayerInfo *player, int mode)
12562 {
12563   int jx = player->jx, jy = player->jy;
12564   int last_jx = player->last_jx, last_jy = player->last_jy;
12565   int move_stepsize = TILEX / player->move_delay_value;
12566
12567   if (!player->active)
12568     return;
12569
12570   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12571     return;
12572
12573   if (mode == SCROLL_INIT)
12574   {
12575     player->actual_frame_counter = FrameCounter;
12576     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12577
12578     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12579         Feld[last_jx][last_jy] == EL_EMPTY)
12580     {
12581       int last_field_block_delay = 0;   /* start with no blocking at all */
12582       int block_delay_adjustment = player->block_delay_adjustment;
12583
12584       /* if player blocks last field, add delay for exactly one move */
12585       if (player->block_last_field)
12586       {
12587         last_field_block_delay += player->move_delay_value;
12588
12589         /* when blocking enabled, prevent moving up despite gravity */
12590         if (player->gravity && player->MovDir == MV_UP)
12591           block_delay_adjustment = -1;
12592       }
12593
12594       /* add block delay adjustment (also possible when not blocking) */
12595       last_field_block_delay += block_delay_adjustment;
12596
12597       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12598       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12599     }
12600
12601     if (player->MovPos != 0)    /* player has not yet reached destination */
12602       return;
12603   }
12604   else if (!FrameReached(&player->actual_frame_counter, 1))
12605     return;
12606
12607   if (player->MovPos != 0)
12608   {
12609     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12610     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12611
12612     /* before DrawPlayer() to draw correct player graphic for this case */
12613     if (player->MovPos == 0)
12614       CheckGravityMovement(player);
12615   }
12616
12617   if (player->MovPos == 0)      /* player reached destination field */
12618   {
12619     if (player->move_delay_reset_counter > 0)
12620     {
12621       player->move_delay_reset_counter--;
12622
12623       if (player->move_delay_reset_counter == 0)
12624       {
12625         /* continue with normal speed after quickly moving through gate */
12626         HALVE_PLAYER_SPEED(player);
12627
12628         /* be able to make the next move without delay */
12629         player->move_delay = 0;
12630       }
12631     }
12632
12633     player->last_jx = jx;
12634     player->last_jy = jy;
12635
12636     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12637         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12638         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12639         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12640         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12641         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12642         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12643         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12644     {
12645       RemovePlayerWithCleanup(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 void RemovePlayerWithCleanup(struct PlayerInfo *player)
13406 {
13407   DrawPlayer(player);   /* needed here only to cleanup last field */
13408   RemovePlayer(player);
13409 }
13410
13411 static void setFieldForSnapping(int x, int y, int element, int direction)
13412 {
13413   struct ElementInfo *ei = &element_info[element];
13414   int direction_bit = MV_DIR_TO_BIT(direction);
13415   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13416   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13417                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13418
13419   Feld[x][y] = EL_ELEMENT_SNAPPING;
13420   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13421
13422   ResetGfxAnimation(x, y);
13423
13424   GfxElement[x][y] = element;
13425   GfxAction[x][y] = action;
13426   GfxDir[x][y] = direction;
13427   GfxFrame[x][y] = -1;
13428 }
13429
13430 /*
13431   =============================================================================
13432   checkDiagonalPushing()
13433   -----------------------------------------------------------------------------
13434   check if diagonal input device direction results in pushing of object
13435   (by checking if the alternative direction is walkable, diggable, ...)
13436   =============================================================================
13437 */
13438
13439 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13440                                     int x, int y, int real_dx, int real_dy)
13441 {
13442   int jx, jy, dx, dy, xx, yy;
13443
13444   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13445     return TRUE;
13446
13447   /* diagonal direction: check alternative direction */
13448   jx = player->jx;
13449   jy = player->jy;
13450   dx = x - jx;
13451   dy = y - jy;
13452   xx = jx + (dx == 0 ? real_dx : 0);
13453   yy = jy + (dy == 0 ? real_dy : 0);
13454
13455   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13456 }
13457
13458 /*
13459   =============================================================================
13460   DigField()
13461   -----------------------------------------------------------------------------
13462   x, y:                 field next to player (non-diagonal) to try to dig to
13463   real_dx, real_dy:     direction as read from input device (can be diagonal)
13464   =============================================================================
13465 */
13466
13467 static int DigField(struct PlayerInfo *player,
13468                     int oldx, int oldy, int x, int y,
13469                     int real_dx, int real_dy, int mode)
13470 {
13471   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13472   boolean player_was_pushing = player->is_pushing;
13473   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13474   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13475   int jx = oldx, jy = oldy;
13476   int dx = x - jx, dy = y - jy;
13477   int nextx = x + dx, nexty = y + dy;
13478   int move_direction = (dx == -1 ? MV_LEFT  :
13479                         dx == +1 ? MV_RIGHT :
13480                         dy == -1 ? MV_UP    :
13481                         dy == +1 ? MV_DOWN  : MV_NONE);
13482   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13483   int dig_side = MV_DIR_OPPOSITE(move_direction);
13484   int old_element = Feld[jx][jy];
13485   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13486   int collect_count;
13487
13488   if (is_player)                /* function can also be called by EL_PENGUIN */
13489   {
13490     if (player->MovPos == 0)
13491     {
13492       player->is_digging = FALSE;
13493       player->is_collecting = FALSE;
13494     }
13495
13496     if (player->MovPos == 0)    /* last pushing move finished */
13497       player->is_pushing = FALSE;
13498
13499     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13500     {
13501       player->is_switching = FALSE;
13502       player->push_delay = -1;
13503
13504       return MP_NO_ACTION;
13505     }
13506   }
13507
13508   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13509     old_element = Back[jx][jy];
13510
13511   /* in case of element dropped at player position, check background */
13512   else if (Back[jx][jy] != EL_EMPTY &&
13513            game.engine_version >= VERSION_IDENT(2,2,0,0))
13514     old_element = Back[jx][jy];
13515
13516   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13517     return MP_NO_ACTION;        /* field has no opening in this direction */
13518
13519   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13520     return MP_NO_ACTION;        /* field has no opening in this direction */
13521
13522   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13523   {
13524     SplashAcid(x, y);
13525
13526     Feld[jx][jy] = player->artwork_element;
13527     InitMovingField(jx, jy, MV_DOWN);
13528     Store[jx][jy] = EL_ACID;
13529     ContinueMoving(jx, jy);
13530     BuryPlayer(player);
13531
13532     return MP_DONT_RUN_INTO;
13533   }
13534
13535   if (player_can_move && DONT_RUN_INTO(element))
13536   {
13537     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13538
13539     return MP_DONT_RUN_INTO;
13540   }
13541
13542   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13543     return MP_NO_ACTION;
13544
13545   collect_count = element_info[element].collect_count_initial;
13546
13547   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13548     return MP_NO_ACTION;
13549
13550   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13551     player_can_move = player_can_move_or_snap;
13552
13553   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13554       game.engine_version >= VERSION_IDENT(2,2,0,0))
13555   {
13556     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13557                                player->index_bit, dig_side);
13558     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13559                                         player->index_bit, dig_side);
13560
13561     if (element == EL_DC_LANDMINE)
13562       Bang(x, y);
13563
13564     if (Feld[x][y] != element)          /* field changed by snapping */
13565       return MP_ACTION;
13566
13567     return MP_NO_ACTION;
13568   }
13569
13570   if (player->gravity && is_player && !player->is_auto_moving &&
13571       canFallDown(player) && move_direction != MV_DOWN &&
13572       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13573     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13574
13575   if (player_can_move &&
13576       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13577   {
13578     int sound_element = SND_ELEMENT(element);
13579     int sound_action = ACTION_WALKING;
13580
13581     if (IS_RND_GATE(element))
13582     {
13583       if (!player->key[RND_GATE_NR(element)])
13584         return MP_NO_ACTION;
13585     }
13586     else if (IS_RND_GATE_GRAY(element))
13587     {
13588       if (!player->key[RND_GATE_GRAY_NR(element)])
13589         return MP_NO_ACTION;
13590     }
13591     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13592     {
13593       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13594         return MP_NO_ACTION;
13595     }
13596     else if (element == EL_EXIT_OPEN ||
13597              element == EL_EM_EXIT_OPEN ||
13598              element == EL_EM_EXIT_OPENING ||
13599              element == EL_STEEL_EXIT_OPEN ||
13600              element == EL_EM_STEEL_EXIT_OPEN ||
13601              element == EL_EM_STEEL_EXIT_OPENING ||
13602              element == EL_SP_EXIT_OPEN ||
13603              element == EL_SP_EXIT_OPENING)
13604     {
13605       sound_action = ACTION_PASSING;    /* player is passing exit */
13606     }
13607     else if (element == EL_EMPTY)
13608     {
13609       sound_action = ACTION_MOVING;             /* nothing to walk on */
13610     }
13611
13612     /* play sound from background or player, whatever is available */
13613     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13614       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13615     else
13616       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13617   }
13618   else if (player_can_move &&
13619            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13620   {
13621     if (!ACCESS_FROM(element, opposite_direction))
13622       return MP_NO_ACTION;      /* field not accessible from this direction */
13623
13624     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13625       return MP_NO_ACTION;
13626
13627     if (IS_EM_GATE(element))
13628     {
13629       if (!player->key[EM_GATE_NR(element)])
13630         return MP_NO_ACTION;
13631     }
13632     else if (IS_EM_GATE_GRAY(element))
13633     {
13634       if (!player->key[EM_GATE_GRAY_NR(element)])
13635         return MP_NO_ACTION;
13636     }
13637     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13638     {
13639       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13640         return MP_NO_ACTION;
13641     }
13642     else if (IS_EMC_GATE(element))
13643     {
13644       if (!player->key[EMC_GATE_NR(element)])
13645         return MP_NO_ACTION;
13646     }
13647     else if (IS_EMC_GATE_GRAY(element))
13648     {
13649       if (!player->key[EMC_GATE_GRAY_NR(element)])
13650         return MP_NO_ACTION;
13651     }
13652     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13653     {
13654       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13655         return MP_NO_ACTION;
13656     }
13657     else if (element == EL_DC_GATE_WHITE ||
13658              element == EL_DC_GATE_WHITE_GRAY ||
13659              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13660     {
13661       if (player->num_white_keys == 0)
13662         return MP_NO_ACTION;
13663
13664       player->num_white_keys--;
13665     }
13666     else if (IS_SP_PORT(element))
13667     {
13668       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13669           element == EL_SP_GRAVITY_PORT_RIGHT ||
13670           element == EL_SP_GRAVITY_PORT_UP ||
13671           element == EL_SP_GRAVITY_PORT_DOWN)
13672         player->gravity = !player->gravity;
13673       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13674                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13675                element == EL_SP_GRAVITY_ON_PORT_UP ||
13676                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13677         player->gravity = TRUE;
13678       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13679                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13680                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13681                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13682         player->gravity = FALSE;
13683     }
13684
13685     /* automatically move to the next field with double speed */
13686     player->programmed_action = move_direction;
13687
13688     if (player->move_delay_reset_counter == 0)
13689     {
13690       player->move_delay_reset_counter = 2;     /* two double speed steps */
13691
13692       DOUBLE_PLAYER_SPEED(player);
13693     }
13694
13695     PlayLevelSoundAction(x, y, ACTION_PASSING);
13696   }
13697   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13698   {
13699     RemoveField(x, y);
13700
13701     if (mode != DF_SNAP)
13702     {
13703       GfxElement[x][y] = GFX_ELEMENT(element);
13704       player->is_digging = TRUE;
13705     }
13706
13707     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13708
13709     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13710                                         player->index_bit, dig_side);
13711
13712     if (mode == DF_SNAP)
13713     {
13714       if (level.block_snap_field)
13715         setFieldForSnapping(x, y, element, move_direction);
13716       else
13717         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13718
13719       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13720                                           player->index_bit, dig_side);
13721     }
13722   }
13723   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13724   {
13725     RemoveField(x, y);
13726
13727     if (is_player && mode != DF_SNAP)
13728     {
13729       GfxElement[x][y] = element;
13730       player->is_collecting = TRUE;
13731     }
13732
13733     if (element == EL_SPEED_PILL)
13734     {
13735       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13736     }
13737     else if (element == EL_EXTRA_TIME && level.time > 0)
13738     {
13739       TimeLeft += level.extra_time;
13740
13741       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13742
13743       DisplayGameControlValues();
13744     }
13745     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13746     {
13747       player->shield_normal_time_left += level.shield_normal_time;
13748       if (element == EL_SHIELD_DEADLY)
13749         player->shield_deadly_time_left += level.shield_deadly_time;
13750     }
13751     else if (element == EL_DYNAMITE ||
13752              element == EL_EM_DYNAMITE ||
13753              element == EL_SP_DISK_RED)
13754     {
13755       if (player->inventory_size < MAX_INVENTORY_SIZE)
13756         player->inventory_element[player->inventory_size++] = element;
13757
13758       DrawGameDoorValues();
13759     }
13760     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13761     {
13762       player->dynabomb_count++;
13763       player->dynabombs_left++;
13764     }
13765     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13766     {
13767       player->dynabomb_size++;
13768     }
13769     else if (element == EL_DYNABOMB_INCREASE_POWER)
13770     {
13771       player->dynabomb_xl = TRUE;
13772     }
13773     else if (IS_KEY(element))
13774     {
13775       player->key[KEY_NR(element)] = TRUE;
13776
13777       DrawGameDoorValues();
13778     }
13779     else if (element == EL_DC_KEY_WHITE)
13780     {
13781       player->num_white_keys++;
13782
13783       /* display white keys? */
13784       /* DrawGameDoorValues(); */
13785     }
13786     else if (IS_ENVELOPE(element))
13787     {
13788       player->show_envelope = element;
13789     }
13790     else if (element == EL_EMC_LENSES)
13791     {
13792       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13793
13794       RedrawAllInvisibleElementsForLenses();
13795     }
13796     else if (element == EL_EMC_MAGNIFIER)
13797     {
13798       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13799
13800       RedrawAllInvisibleElementsForMagnifier();
13801     }
13802     else if (IS_DROPPABLE(element) ||
13803              IS_THROWABLE(element))     /* can be collected and dropped */
13804     {
13805       int i;
13806
13807       if (collect_count == 0)
13808         player->inventory_infinite_element = element;
13809       else
13810         for (i = 0; i < collect_count; i++)
13811           if (player->inventory_size < MAX_INVENTORY_SIZE)
13812             player->inventory_element[player->inventory_size++] = element;
13813
13814       DrawGameDoorValues();
13815     }
13816     else if (collect_count > 0)
13817     {
13818       local_player->gems_still_needed -= collect_count;
13819       if (local_player->gems_still_needed < 0)
13820         local_player->gems_still_needed = 0;
13821
13822       game.snapshot.collected_item = TRUE;
13823
13824       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13825
13826       DisplayGameControlValues();
13827     }
13828
13829     RaiseScoreElement(element);
13830     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13831
13832     if (is_player)
13833       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13834                                           player->index_bit, dig_side);
13835
13836     if (mode == DF_SNAP)
13837     {
13838       if (level.block_snap_field)
13839         setFieldForSnapping(x, y, element, move_direction);
13840       else
13841         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13842
13843       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13844                                           player->index_bit, dig_side);
13845     }
13846   }
13847   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13848   {
13849     if (mode == DF_SNAP && element != EL_BD_ROCK)
13850       return MP_NO_ACTION;
13851
13852     if (CAN_FALL(element) && dy)
13853       return MP_NO_ACTION;
13854
13855     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13856         !(element == EL_SPRING && level.use_spring_bug))
13857       return MP_NO_ACTION;
13858
13859     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13860         ((move_direction & MV_VERTICAL &&
13861           ((element_info[element].move_pattern & MV_LEFT &&
13862             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13863            (element_info[element].move_pattern & MV_RIGHT &&
13864             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13865          (move_direction & MV_HORIZONTAL &&
13866           ((element_info[element].move_pattern & MV_UP &&
13867             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13868            (element_info[element].move_pattern & MV_DOWN &&
13869             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13870       return MP_NO_ACTION;
13871
13872     /* do not push elements already moving away faster than player */
13873     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13874         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13875       return MP_NO_ACTION;
13876
13877     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13878     {
13879       if (player->push_delay_value == -1 || !player_was_pushing)
13880         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13881     }
13882     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13883     {
13884       if (player->push_delay_value == -1)
13885         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13886     }
13887     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13888     {
13889       if (!player->is_pushing)
13890         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13891     }
13892
13893     player->is_pushing = TRUE;
13894     player->is_active = TRUE;
13895
13896     if (!(IN_LEV_FIELD(nextx, nexty) &&
13897           (IS_FREE(nextx, nexty) ||
13898            (IS_SB_ELEMENT(element) &&
13899             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13900            (IS_CUSTOM_ELEMENT(element) &&
13901             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13902       return MP_NO_ACTION;
13903
13904     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13905       return MP_NO_ACTION;
13906
13907     if (player->push_delay == -1)       /* new pushing; restart delay */
13908       player->push_delay = 0;
13909
13910     if (player->push_delay < player->push_delay_value &&
13911         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13912         element != EL_SPRING && element != EL_BALLOON)
13913     {
13914       /* make sure that there is no move delay before next try to push */
13915       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13916         player->move_delay = 0;
13917
13918       return MP_NO_ACTION;
13919     }
13920
13921     if (IS_CUSTOM_ELEMENT(element) &&
13922         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13923     {
13924       if (!DigFieldByCE(nextx, nexty, element))
13925         return MP_NO_ACTION;
13926     }
13927
13928     if (IS_SB_ELEMENT(element))
13929     {
13930       if (element == EL_SOKOBAN_FIELD_FULL)
13931       {
13932         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13933         local_player->sokobanfields_still_needed++;
13934       }
13935
13936       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13937       {
13938         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13939         local_player->sokobanfields_still_needed--;
13940       }
13941
13942       Feld[x][y] = EL_SOKOBAN_OBJECT;
13943
13944       if (Back[x][y] == Back[nextx][nexty])
13945         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13946       else if (Back[x][y] != 0)
13947         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13948                                     ACTION_EMPTYING);
13949       else
13950         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13951                                     ACTION_FILLING);
13952
13953       if (local_player->sokobanfields_still_needed == 0 &&
13954           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13955       {
13956         PlayerWins(player);
13957
13958         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13959       }
13960     }
13961     else
13962       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13963
13964     InitMovingField(x, y, move_direction);
13965     GfxAction[x][y] = ACTION_PUSHING;
13966
13967     if (mode == DF_SNAP)
13968       ContinueMoving(x, y);
13969     else
13970       MovPos[x][y] = (dx != 0 ? dx : dy);
13971
13972     Pushed[x][y] = TRUE;
13973     Pushed[nextx][nexty] = TRUE;
13974
13975     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13976       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13977     else
13978       player->push_delay_value = -1;    /* get new value later */
13979
13980     /* check for element change _after_ element has been pushed */
13981     if (game.use_change_when_pushing_bug)
13982     {
13983       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13984                                  player->index_bit, dig_side);
13985       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13986                                           player->index_bit, dig_side);
13987     }
13988   }
13989   else if (IS_SWITCHABLE(element))
13990   {
13991     if (PLAYER_SWITCHING(player, x, y))
13992     {
13993       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13994                                           player->index_bit, dig_side);
13995
13996       return MP_ACTION;
13997     }
13998
13999     player->is_switching = TRUE;
14000     player->switch_x = x;
14001     player->switch_y = y;
14002
14003     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14004
14005     if (element == EL_ROBOT_WHEEL)
14006     {
14007       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14008       ZX = x;
14009       ZY = y;
14010
14011       game.robot_wheel_active = TRUE;
14012
14013       TEST_DrawLevelField(x, y);
14014     }
14015     else if (element == EL_SP_TERMINAL)
14016     {
14017       int xx, yy;
14018
14019       SCAN_PLAYFIELD(xx, yy)
14020       {
14021         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14022         {
14023           Bang(xx, yy);
14024         }
14025         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14026         {
14027           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14028
14029           ResetGfxAnimation(xx, yy);
14030           TEST_DrawLevelField(xx, yy);
14031         }
14032       }
14033     }
14034     else if (IS_BELT_SWITCH(element))
14035     {
14036       ToggleBeltSwitch(x, y);
14037     }
14038     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14039              element == EL_SWITCHGATE_SWITCH_DOWN ||
14040              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14041              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14042     {
14043       ToggleSwitchgateSwitch(x, y);
14044     }
14045     else if (element == EL_LIGHT_SWITCH ||
14046              element == EL_LIGHT_SWITCH_ACTIVE)
14047     {
14048       ToggleLightSwitch(x, y);
14049     }
14050     else if (element == EL_TIMEGATE_SWITCH ||
14051              element == EL_DC_TIMEGATE_SWITCH)
14052     {
14053       ActivateTimegateSwitch(x, y);
14054     }
14055     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14056              element == EL_BALLOON_SWITCH_RIGHT ||
14057              element == EL_BALLOON_SWITCH_UP    ||
14058              element == EL_BALLOON_SWITCH_DOWN  ||
14059              element == EL_BALLOON_SWITCH_NONE  ||
14060              element == EL_BALLOON_SWITCH_ANY)
14061     {
14062       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14063                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14064                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14065                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14066                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14067                              move_direction);
14068     }
14069     else if (element == EL_LAMP)
14070     {
14071       Feld[x][y] = EL_LAMP_ACTIVE;
14072       local_player->lights_still_needed--;
14073
14074       ResetGfxAnimation(x, y);
14075       TEST_DrawLevelField(x, y);
14076     }
14077     else if (element == EL_TIME_ORB_FULL)
14078     {
14079       Feld[x][y] = EL_TIME_ORB_EMPTY;
14080
14081       if (level.time > 0 || level.use_time_orb_bug)
14082       {
14083         TimeLeft += level.time_orb_time;
14084         game.no_time_limit = FALSE;
14085
14086         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14087
14088         DisplayGameControlValues();
14089       }
14090
14091       ResetGfxAnimation(x, y);
14092       TEST_DrawLevelField(x, y);
14093     }
14094     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14095              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14096     {
14097       int xx, yy;
14098
14099       game.ball_state = !game.ball_state;
14100
14101       SCAN_PLAYFIELD(xx, yy)
14102       {
14103         int e = Feld[xx][yy];
14104
14105         if (game.ball_state)
14106         {
14107           if (e == EL_EMC_MAGIC_BALL)
14108             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14109           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14110             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14111         }
14112         else
14113         {
14114           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14115             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14116           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14117             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14118         }
14119       }
14120     }
14121
14122     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14123                                         player->index_bit, dig_side);
14124
14125     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14126                                         player->index_bit, dig_side);
14127
14128     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14129                                         player->index_bit, dig_side);
14130
14131     return MP_ACTION;
14132   }
14133   else
14134   {
14135     if (!PLAYER_SWITCHING(player, x, y))
14136     {
14137       player->is_switching = TRUE;
14138       player->switch_x = x;
14139       player->switch_y = y;
14140
14141       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14142                                  player->index_bit, dig_side);
14143       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14144                                           player->index_bit, dig_side);
14145
14146       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14147                                  player->index_bit, dig_side);
14148       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14149                                           player->index_bit, dig_side);
14150     }
14151
14152     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14153                                player->index_bit, dig_side);
14154     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14155                                         player->index_bit, dig_side);
14156
14157     return MP_NO_ACTION;
14158   }
14159
14160   player->push_delay = -1;
14161
14162   if (is_player)                /* function can also be called by EL_PENGUIN */
14163   {
14164     if (Feld[x][y] != element)          /* really digged/collected something */
14165     {
14166       player->is_collecting = !player->is_digging;
14167       player->is_active = TRUE;
14168     }
14169   }
14170
14171   return MP_MOVING;
14172 }
14173
14174 static boolean DigFieldByCE(int x, int y, int digging_element)
14175 {
14176   int element = Feld[x][y];
14177
14178   if (!IS_FREE(x, y))
14179   {
14180     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14181                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14182                   ACTION_BREAKING);
14183
14184     /* no element can dig solid indestructible elements */
14185     if (IS_INDESTRUCTIBLE(element) &&
14186         !IS_DIGGABLE(element) &&
14187         !IS_COLLECTIBLE(element))
14188       return FALSE;
14189
14190     if (AmoebaNr[x][y] &&
14191         (element == EL_AMOEBA_FULL ||
14192          element == EL_BD_AMOEBA ||
14193          element == EL_AMOEBA_GROWING))
14194     {
14195       AmoebaCnt[AmoebaNr[x][y]]--;
14196       AmoebaCnt2[AmoebaNr[x][y]]--;
14197     }
14198
14199     if (IS_MOVING(x, y))
14200       RemoveMovingField(x, y);
14201     else
14202     {
14203       RemoveField(x, y);
14204       TEST_DrawLevelField(x, y);
14205     }
14206
14207     /* if digged element was about to explode, prevent the explosion */
14208     ExplodeField[x][y] = EX_TYPE_NONE;
14209
14210     PlayLevelSoundAction(x, y, action);
14211   }
14212
14213   Store[x][y] = EL_EMPTY;
14214
14215   /* this makes it possible to leave the removed element again */
14216   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14217     Store[x][y] = element;
14218
14219   return TRUE;
14220 }
14221
14222 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14223 {
14224   int jx = player->jx, jy = player->jy;
14225   int x = jx + dx, y = jy + dy;
14226   int snap_direction = (dx == -1 ? MV_LEFT  :
14227                         dx == +1 ? MV_RIGHT :
14228                         dy == -1 ? MV_UP    :
14229                         dy == +1 ? MV_DOWN  : MV_NONE);
14230   boolean can_continue_snapping = (level.continuous_snapping &&
14231                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14232
14233   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14234     return FALSE;
14235
14236   if (!player->active || !IN_LEV_FIELD(x, y))
14237     return FALSE;
14238
14239   if (dx && dy)
14240     return FALSE;
14241
14242   if (!dx && !dy)
14243   {
14244     if (player->MovPos == 0)
14245       player->is_pushing = FALSE;
14246
14247     player->is_snapping = FALSE;
14248
14249     if (player->MovPos == 0)
14250     {
14251       player->is_moving = FALSE;
14252       player->is_digging = FALSE;
14253       player->is_collecting = FALSE;
14254     }
14255
14256     return FALSE;
14257   }
14258
14259   /* prevent snapping with already pressed snap key when not allowed */
14260   if (player->is_snapping && !can_continue_snapping)
14261     return FALSE;
14262
14263   player->MovDir = snap_direction;
14264
14265   if (player->MovPos == 0)
14266   {
14267     player->is_moving = FALSE;
14268     player->is_digging = FALSE;
14269     player->is_collecting = FALSE;
14270   }
14271
14272   player->is_dropping = FALSE;
14273   player->is_dropping_pressed = FALSE;
14274   player->drop_pressed_delay = 0;
14275
14276   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14277     return FALSE;
14278
14279   player->is_snapping = TRUE;
14280   player->is_active = TRUE;
14281
14282   if (player->MovPos == 0)
14283   {
14284     player->is_moving = FALSE;
14285     player->is_digging = FALSE;
14286     player->is_collecting = FALSE;
14287   }
14288
14289   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14290     TEST_DrawLevelField(player->last_jx, player->last_jy);
14291
14292   TEST_DrawLevelField(x, y);
14293
14294   return TRUE;
14295 }
14296
14297 static boolean DropElement(struct PlayerInfo *player)
14298 {
14299   int old_element, new_element;
14300   int dropx = player->jx, dropy = player->jy;
14301   int drop_direction = player->MovDir;
14302   int drop_side = drop_direction;
14303   int drop_element = get_next_dropped_element(player);
14304
14305   /* do not drop an element on top of another element; when holding drop key
14306      pressed without moving, dropped element must move away before the next
14307      element can be dropped (this is especially important if the next element
14308      is dynamite, which can be placed on background for historical reasons) */
14309   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14310     return MP_ACTION;
14311
14312   if (IS_THROWABLE(drop_element))
14313   {
14314     dropx += GET_DX_FROM_DIR(drop_direction);
14315     dropy += GET_DY_FROM_DIR(drop_direction);
14316
14317     if (!IN_LEV_FIELD(dropx, dropy))
14318       return FALSE;
14319   }
14320
14321   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14322   new_element = drop_element;           /* default: no change when dropping */
14323
14324   /* check if player is active, not moving and ready to drop */
14325   if (!player->active || player->MovPos || player->drop_delay > 0)
14326     return FALSE;
14327
14328   /* check if player has anything that can be dropped */
14329   if (new_element == EL_UNDEFINED)
14330     return FALSE;
14331
14332   /* only set if player has anything that can be dropped */
14333   player->is_dropping_pressed = TRUE;
14334
14335   /* check if drop key was pressed long enough for EM style dynamite */
14336   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14337     return FALSE;
14338
14339   /* check if anything can be dropped at the current position */
14340   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14341     return FALSE;
14342
14343   /* collected custom elements can only be dropped on empty fields */
14344   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14345     return FALSE;
14346
14347   if (old_element != EL_EMPTY)
14348     Back[dropx][dropy] = old_element;   /* store old element on this field */
14349
14350   ResetGfxAnimation(dropx, dropy);
14351   ResetRandomAnimationValue(dropx, dropy);
14352
14353   if (player->inventory_size > 0 ||
14354       player->inventory_infinite_element != EL_UNDEFINED)
14355   {
14356     if (player->inventory_size > 0)
14357     {
14358       player->inventory_size--;
14359
14360       DrawGameDoorValues();
14361
14362       if (new_element == EL_DYNAMITE)
14363         new_element = EL_DYNAMITE_ACTIVE;
14364       else if (new_element == EL_EM_DYNAMITE)
14365         new_element = EL_EM_DYNAMITE_ACTIVE;
14366       else if (new_element == EL_SP_DISK_RED)
14367         new_element = EL_SP_DISK_RED_ACTIVE;
14368     }
14369
14370     Feld[dropx][dropy] = new_element;
14371
14372     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14373       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14374                           el2img(Feld[dropx][dropy]), 0);
14375
14376     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14377
14378     /* needed if previous element just changed to "empty" in the last frame */
14379     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14380
14381     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14382                                player->index_bit, drop_side);
14383     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14384                                         CE_PLAYER_DROPS_X,
14385                                         player->index_bit, drop_side);
14386
14387     TestIfElementTouchesCustomElement(dropx, dropy);
14388   }
14389   else          /* player is dropping a dyna bomb */
14390   {
14391     player->dynabombs_left--;
14392
14393     Feld[dropx][dropy] = new_element;
14394
14395     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14396       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14397                           el2img(Feld[dropx][dropy]), 0);
14398
14399     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14400   }
14401
14402   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14403     InitField_WithBug1(dropx, dropy, FALSE);
14404
14405   new_element = Feld[dropx][dropy];     /* element might have changed */
14406
14407   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14408       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14409   {
14410     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14411       MovDir[dropx][dropy] = drop_direction;
14412
14413     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14414
14415     /* do not cause impact style collision by dropping elements that can fall */
14416     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14417   }
14418
14419   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14420   player->is_dropping = TRUE;
14421
14422   player->drop_pressed_delay = 0;
14423   player->is_dropping_pressed = FALSE;
14424
14425   player->drop_x = dropx;
14426   player->drop_y = dropy;
14427
14428   return TRUE;
14429 }
14430
14431 /* ------------------------------------------------------------------------- */
14432 /* game sound playing functions                                              */
14433 /* ------------------------------------------------------------------------- */
14434
14435 static int *loop_sound_frame = NULL;
14436 static int *loop_sound_volume = NULL;
14437
14438 void InitPlayLevelSound()
14439 {
14440   int num_sounds = getSoundListSize();
14441
14442   checked_free(loop_sound_frame);
14443   checked_free(loop_sound_volume);
14444
14445   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14446   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14447 }
14448
14449 static void PlayLevelSound(int x, int y, int nr)
14450 {
14451   int sx = SCREENX(x), sy = SCREENY(y);
14452   int volume, stereo_position;
14453   int max_distance = 8;
14454   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14455
14456   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14457       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14458     return;
14459
14460   if (!IN_LEV_FIELD(x, y) ||
14461       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14462       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14463     return;
14464
14465   volume = SOUND_MAX_VOLUME;
14466
14467   if (!IN_SCR_FIELD(sx, sy))
14468   {
14469     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14470     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14471
14472     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14473   }
14474
14475   stereo_position = (SOUND_MAX_LEFT +
14476                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14477                      (SCR_FIELDX + 2 * max_distance));
14478
14479   if (IS_LOOP_SOUND(nr))
14480   {
14481     /* This assures that quieter loop sounds do not overwrite louder ones,
14482        while restarting sound volume comparison with each new game frame. */
14483
14484     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14485       return;
14486
14487     loop_sound_volume[nr] = volume;
14488     loop_sound_frame[nr] = FrameCounter;
14489   }
14490
14491   PlaySoundExt(nr, volume, stereo_position, type);
14492 }
14493
14494 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14495 {
14496   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14497                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14498                  y < LEVELY(BY1) ? LEVELY(BY1) :
14499                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14500                  sound_action);
14501 }
14502
14503 static void PlayLevelSoundAction(int x, int y, int action)
14504 {
14505   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14506 }
14507
14508 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14509 {
14510   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14511
14512   if (sound_effect != SND_UNDEFINED)
14513     PlayLevelSound(x, y, sound_effect);
14514 }
14515
14516 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14517                                               int action)
14518 {
14519   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14520
14521   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14522     PlayLevelSound(x, y, sound_effect);
14523 }
14524
14525 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14526 {
14527   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14528
14529   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14530     PlayLevelSound(x, y, sound_effect);
14531 }
14532
14533 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14534 {
14535   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14536
14537   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14538     StopSound(sound_effect);
14539 }
14540
14541 static int getLevelMusicNr()
14542 {
14543   if (levelset.music[level_nr] != MUS_UNDEFINED)
14544     return levelset.music[level_nr];            /* from config file */
14545   else
14546     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14547 }
14548
14549 static void FadeLevelSounds()
14550 {
14551   FadeSounds();
14552 }
14553
14554 static void FadeLevelMusic()
14555 {
14556   int music_nr = getLevelMusicNr();
14557   char *curr_music = getCurrentlyPlayingMusicFilename();
14558   char *next_music = getMusicInfoEntryFilename(music_nr);
14559
14560   if (!strEqual(curr_music, next_music))
14561     FadeMusic();
14562 }
14563
14564 void FadeLevelSoundsAndMusic()
14565 {
14566   FadeLevelSounds();
14567   FadeLevelMusic();
14568 }
14569
14570 static void PlayLevelMusic()
14571 {
14572   int music_nr = getLevelMusicNr();
14573   char *curr_music = getCurrentlyPlayingMusicFilename();
14574   char *next_music = getMusicInfoEntryFilename(music_nr);
14575
14576   if (!strEqual(curr_music, next_music))
14577     PlayMusic(music_nr);
14578 }
14579
14580 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14581 {
14582   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14583   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14584   int x = xx - 1 - offset;
14585   int y = yy - 1 - offset;
14586
14587   switch (sample)
14588   {
14589     case SAMPLE_blank:
14590       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14591       break;
14592
14593     case SAMPLE_roll:
14594       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14595       break;
14596
14597     case SAMPLE_stone:
14598       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14599       break;
14600
14601     case SAMPLE_nut:
14602       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14603       break;
14604
14605     case SAMPLE_crack:
14606       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14607       break;
14608
14609     case SAMPLE_bug:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14611       break;
14612
14613     case SAMPLE_tank:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14615       break;
14616
14617     case SAMPLE_android_clone:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14619       break;
14620
14621     case SAMPLE_android_move:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14623       break;
14624
14625     case SAMPLE_spring:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14627       break;
14628
14629     case SAMPLE_slurp:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14631       break;
14632
14633     case SAMPLE_eater:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14635       break;
14636
14637     case SAMPLE_eater_eat:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14639       break;
14640
14641     case SAMPLE_alien:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14643       break;
14644
14645     case SAMPLE_collect:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14647       break;
14648
14649     case SAMPLE_diamond:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14651       break;
14652
14653     case SAMPLE_squash:
14654       /* !!! CHECK THIS !!! */
14655 #if 1
14656       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14657 #else
14658       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14659 #endif
14660       break;
14661
14662     case SAMPLE_wonderfall:
14663       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14664       break;
14665
14666     case SAMPLE_drip:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14668       break;
14669
14670     case SAMPLE_push:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14672       break;
14673
14674     case SAMPLE_dirt:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14676       break;
14677
14678     case SAMPLE_acid:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14680       break;
14681
14682     case SAMPLE_ball:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14684       break;
14685
14686     case SAMPLE_grow:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14688       break;
14689
14690     case SAMPLE_wonder:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14692       break;
14693
14694     case SAMPLE_door:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14696       break;
14697
14698     case SAMPLE_exit_open:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14700       break;
14701
14702     case SAMPLE_exit_leave:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14704       break;
14705
14706     case SAMPLE_dynamite:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14708       break;
14709
14710     case SAMPLE_tick:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14712       break;
14713
14714     case SAMPLE_press:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14716       break;
14717
14718     case SAMPLE_wheel:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14720       break;
14721
14722     case SAMPLE_boom:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14724       break;
14725
14726     case SAMPLE_die:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14728       break;
14729
14730     case SAMPLE_time:
14731       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14732       break;
14733
14734     default:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14736       break;
14737   }
14738 }
14739
14740 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14741 {
14742   int element = map_element_SP_to_RND(element_sp);
14743   int action = map_action_SP_to_RND(action_sp);
14744   int offset = (setup.sp_show_border_elements ? 0 : 1);
14745   int x = xx - offset;
14746   int y = yy - offset;
14747
14748   PlayLevelSoundElementAction(x, y, element, action);
14749 }
14750
14751 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14752 {
14753   int element = map_element_MM_to_RND(element_mm);
14754   int action = map_action_MM_to_RND(action_mm);
14755   int offset = 0;
14756   int x = xx - offset;
14757   int y = yy - offset;
14758
14759   if (!IS_MM_ELEMENT(element))
14760     element = EL_MM_DEFAULT;
14761
14762   PlayLevelSoundElementAction(x, y, element, action);
14763 }
14764
14765 void PlaySound_MM(int sound_mm)
14766 {
14767   int sound = map_sound_MM_to_RND(sound_mm);
14768
14769   if (sound == SND_UNDEFINED)
14770     return;
14771
14772   PlaySound(sound);
14773 }
14774
14775 void PlaySoundLoop_MM(int sound_mm)
14776 {
14777   int sound = map_sound_MM_to_RND(sound_mm);
14778
14779   if (sound == SND_UNDEFINED)
14780     return;
14781
14782   PlaySoundLoop(sound);
14783 }
14784
14785 void StopSound_MM(int sound_mm)
14786 {
14787   int sound = map_sound_MM_to_RND(sound_mm);
14788
14789   if (sound == SND_UNDEFINED)
14790     return;
14791
14792   StopSound(sound);
14793 }
14794
14795 void RaiseScore(int value)
14796 {
14797   local_player->score += value;
14798
14799   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14800
14801   DisplayGameControlValues();
14802 }
14803
14804 void RaiseScoreElement(int element)
14805 {
14806   switch (element)
14807   {
14808     case EL_EMERALD:
14809     case EL_BD_DIAMOND:
14810     case EL_EMERALD_YELLOW:
14811     case EL_EMERALD_RED:
14812     case EL_EMERALD_PURPLE:
14813     case EL_SP_INFOTRON:
14814       RaiseScore(level.score[SC_EMERALD]);
14815       break;
14816     case EL_DIAMOND:
14817       RaiseScore(level.score[SC_DIAMOND]);
14818       break;
14819     case EL_CRYSTAL:
14820       RaiseScore(level.score[SC_CRYSTAL]);
14821       break;
14822     case EL_PEARL:
14823       RaiseScore(level.score[SC_PEARL]);
14824       break;
14825     case EL_BUG:
14826     case EL_BD_BUTTERFLY:
14827     case EL_SP_ELECTRON:
14828       RaiseScore(level.score[SC_BUG]);
14829       break;
14830     case EL_SPACESHIP:
14831     case EL_BD_FIREFLY:
14832     case EL_SP_SNIKSNAK:
14833       RaiseScore(level.score[SC_SPACESHIP]);
14834       break;
14835     case EL_YAMYAM:
14836     case EL_DARK_YAMYAM:
14837       RaiseScore(level.score[SC_YAMYAM]);
14838       break;
14839     case EL_ROBOT:
14840       RaiseScore(level.score[SC_ROBOT]);
14841       break;
14842     case EL_PACMAN:
14843       RaiseScore(level.score[SC_PACMAN]);
14844       break;
14845     case EL_NUT:
14846       RaiseScore(level.score[SC_NUT]);
14847       break;
14848     case EL_DYNAMITE:
14849     case EL_EM_DYNAMITE:
14850     case EL_SP_DISK_RED:
14851     case EL_DYNABOMB_INCREASE_NUMBER:
14852     case EL_DYNABOMB_INCREASE_SIZE:
14853     case EL_DYNABOMB_INCREASE_POWER:
14854       RaiseScore(level.score[SC_DYNAMITE]);
14855       break;
14856     case EL_SHIELD_NORMAL:
14857     case EL_SHIELD_DEADLY:
14858       RaiseScore(level.score[SC_SHIELD]);
14859       break;
14860     case EL_EXTRA_TIME:
14861       RaiseScore(level.extra_time_score);
14862       break;
14863     case EL_KEY_1:
14864     case EL_KEY_2:
14865     case EL_KEY_3:
14866     case EL_KEY_4:
14867     case EL_EM_KEY_1:
14868     case EL_EM_KEY_2:
14869     case EL_EM_KEY_3:
14870     case EL_EM_KEY_4:
14871     case EL_EMC_KEY_5:
14872     case EL_EMC_KEY_6:
14873     case EL_EMC_KEY_7:
14874     case EL_EMC_KEY_8:
14875     case EL_DC_KEY_WHITE:
14876       RaiseScore(level.score[SC_KEY]);
14877       break;
14878     default:
14879       RaiseScore(element_info[element].collect_score);
14880       break;
14881   }
14882 }
14883
14884 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14885 {
14886   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14887   {
14888     /* closing door required in case of envelope style request dialogs */
14889     if (!skip_request)
14890       CloseDoor(DOOR_CLOSE_1);
14891
14892     if (network.enabled)
14893       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14894     else
14895     {
14896       if (quick_quit)
14897         FadeSkipNextFadeIn();
14898
14899       SetGameStatus(GAME_MODE_MAIN);
14900
14901       DrawMainMenu();
14902     }
14903   }
14904   else          /* continue playing the game */
14905   {
14906     if (tape.playing && tape.deactivate_display)
14907       TapeDeactivateDisplayOff(TRUE);
14908
14909     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14910
14911     if (tape.playing && tape.deactivate_display)
14912       TapeDeactivateDisplayOn();
14913   }
14914 }
14915
14916 void RequestQuitGame(boolean ask_if_really_quit)
14917 {
14918   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14919   boolean skip_request = AllPlayersGone || quick_quit;
14920
14921   RequestQuitGameExt(skip_request, quick_quit,
14922                      "Do you really want to quit the game?");
14923 }
14924
14925 void RequestRestartGame(char *message)
14926 {
14927   game.restart_game_message = NULL;
14928
14929   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14930   {
14931     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14932   }
14933   else
14934   {
14935     SetGameStatus(GAME_MODE_MAIN);
14936
14937     DrawMainMenu();
14938   }
14939 }
14940
14941
14942 /* ------------------------------------------------------------------------- */
14943 /* random generator functions                                                */
14944 /* ------------------------------------------------------------------------- */
14945
14946 unsigned int InitEngineRandom_RND(int seed)
14947 {
14948   game.num_random_calls = 0;
14949
14950   return InitEngineRandom(seed);
14951 }
14952
14953 unsigned int RND(int max)
14954 {
14955   if (max > 0)
14956   {
14957     game.num_random_calls++;
14958
14959     return GetEngineRandom(max);
14960   }
14961
14962   return 0;
14963 }
14964
14965
14966 /* ------------------------------------------------------------------------- */
14967 /* game engine snapshot handling functions                                   */
14968 /* ------------------------------------------------------------------------- */
14969
14970 struct EngineSnapshotInfo
14971 {
14972   /* runtime values for custom element collect score */
14973   int collect_score[NUM_CUSTOM_ELEMENTS];
14974
14975   /* runtime values for group element choice position */
14976   int choice_pos[NUM_GROUP_ELEMENTS];
14977
14978   /* runtime values for belt position animations */
14979   int belt_graphic[4][NUM_BELT_PARTS];
14980   int belt_anim_mode[4][NUM_BELT_PARTS];
14981 };
14982
14983 static struct EngineSnapshotInfo engine_snapshot_rnd;
14984 static char *snapshot_level_identifier = NULL;
14985 static int snapshot_level_nr = -1;
14986
14987 static void SaveEngineSnapshotValues_RND()
14988 {
14989   static int belt_base_active_element[4] =
14990   {
14991     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14992     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14993     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14994     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14995   };
14996   int i, j;
14997
14998   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14999   {
15000     int element = EL_CUSTOM_START + i;
15001
15002     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15003   }
15004
15005   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15006   {
15007     int element = EL_GROUP_START + i;
15008
15009     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15010   }
15011
15012   for (i = 0; i < 4; i++)
15013   {
15014     for (j = 0; j < NUM_BELT_PARTS; j++)
15015     {
15016       int element = belt_base_active_element[i] + j;
15017       int graphic = el2img(element);
15018       int anim_mode = graphic_info[graphic].anim_mode;
15019
15020       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15021       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15022     }
15023   }
15024 }
15025
15026 static void LoadEngineSnapshotValues_RND()
15027 {
15028   unsigned int num_random_calls = game.num_random_calls;
15029   int i, j;
15030
15031   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15032   {
15033     int element = EL_CUSTOM_START + i;
15034
15035     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15036   }
15037
15038   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15039   {
15040     int element = EL_GROUP_START + i;
15041
15042     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15043   }
15044
15045   for (i = 0; i < 4; i++)
15046   {
15047     for (j = 0; j < NUM_BELT_PARTS; j++)
15048     {
15049       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15050       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15051
15052       graphic_info[graphic].anim_mode = anim_mode;
15053     }
15054   }
15055
15056   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15057   {
15058     InitRND(tape.random_seed);
15059     for (i = 0; i < num_random_calls; i++)
15060       RND(1);
15061   }
15062
15063   if (game.num_random_calls != num_random_calls)
15064   {
15065     Error(ERR_INFO, "number of random calls out of sync");
15066     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15067     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15068     Error(ERR_EXIT, "this should not happen -- please debug");
15069   }
15070 }
15071
15072 void FreeEngineSnapshotSingle()
15073 {
15074   FreeSnapshotSingle();
15075
15076   setString(&snapshot_level_identifier, NULL);
15077   snapshot_level_nr = -1;
15078 }
15079
15080 void FreeEngineSnapshotList()
15081 {
15082   FreeSnapshotList();
15083 }
15084
15085 ListNode *SaveEngineSnapshotBuffers()
15086 {
15087   ListNode *buffers = NULL;
15088
15089   /* copy some special values to a structure better suited for the snapshot */
15090
15091   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15092     SaveEngineSnapshotValues_RND();
15093   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15094     SaveEngineSnapshotValues_EM();
15095   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15096     SaveEngineSnapshotValues_SP(&buffers);
15097   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15098     SaveEngineSnapshotValues_MM(&buffers);
15099
15100   /* save values stored in special snapshot structure */
15101
15102   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15103     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15104   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15105     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15106   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15107     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15108   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15109     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15110
15111   /* save further RND engine values */
15112
15113   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15114   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15115   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15116
15117   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15118   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15120   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15121
15122   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15125   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15127
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15131
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15133
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15135
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15138
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15157
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15160
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15167
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15176
15177 #if 0
15178   ListNode *node = engine_snapshot_list_rnd;
15179   int num_bytes = 0;
15180
15181   while (node != NULL)
15182   {
15183     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15184
15185     node = node->next;
15186   }
15187
15188   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15189 #endif
15190
15191   return buffers;
15192 }
15193
15194 void SaveEngineSnapshotSingle()
15195 {
15196   ListNode *buffers = SaveEngineSnapshotBuffers();
15197
15198   /* finally save all snapshot buffers to single snapshot */
15199   SaveSnapshotSingle(buffers);
15200
15201   /* save level identification information */
15202   setString(&snapshot_level_identifier, leveldir_current->identifier);
15203   snapshot_level_nr = level_nr;
15204 }
15205
15206 boolean CheckSaveEngineSnapshotToList()
15207 {
15208   boolean save_snapshot =
15209     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15210      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15211       game.snapshot.changed_action) ||
15212      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15213       game.snapshot.collected_item));
15214
15215   game.snapshot.changed_action = FALSE;
15216   game.snapshot.collected_item = FALSE;
15217   game.snapshot.save_snapshot = save_snapshot;
15218
15219   return save_snapshot;
15220 }
15221
15222 void SaveEngineSnapshotToList()
15223 {
15224   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15225       tape.quick_resume)
15226     return;
15227
15228   ListNode *buffers = SaveEngineSnapshotBuffers();
15229
15230   /* finally save all snapshot buffers to snapshot list */
15231   SaveSnapshotToList(buffers);
15232 }
15233
15234 void SaveEngineSnapshotToListInitial()
15235 {
15236   FreeEngineSnapshotList();
15237
15238   SaveEngineSnapshotToList();
15239 }
15240
15241 void LoadEngineSnapshotValues()
15242 {
15243   /* restore special values from snapshot structure */
15244
15245   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15246     LoadEngineSnapshotValues_RND();
15247   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15248     LoadEngineSnapshotValues_EM();
15249   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15250     LoadEngineSnapshotValues_SP();
15251   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15252     LoadEngineSnapshotValues_MM();
15253 }
15254
15255 void LoadEngineSnapshotSingle()
15256 {
15257   LoadSnapshotSingle();
15258
15259   LoadEngineSnapshotValues();
15260 }
15261
15262 void LoadEngineSnapshot_Undo(int steps)
15263 {
15264   LoadSnapshotFromList_Older(steps);
15265
15266   LoadEngineSnapshotValues();
15267 }
15268
15269 void LoadEngineSnapshot_Redo(int steps)
15270 {
15271   LoadSnapshotFromList_Newer(steps);
15272
15273   LoadEngineSnapshotValues();
15274 }
15275
15276 boolean CheckEngineSnapshotSingle()
15277 {
15278   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15279           snapshot_level_nr == level_nr);
15280 }
15281
15282 boolean CheckEngineSnapshotList()
15283 {
15284   return CheckSnapshotList();
15285 }
15286
15287
15288 /* ---------- new game button stuff ---------------------------------------- */
15289
15290 static struct
15291 {
15292   int graphic;
15293   struct XY *pos;
15294   int gadget_id;
15295   boolean *setup_value;
15296   boolean allowed_on_tape;
15297   char *infotext;
15298 } gamebutton_info[NUM_GAME_BUTTONS] =
15299 {
15300   {
15301     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15302     GAME_CTRL_ID_STOP,                          NULL,
15303     TRUE,                                       "stop game"
15304   },
15305   {
15306     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15307     GAME_CTRL_ID_PAUSE,                         NULL,
15308     TRUE,                                       "pause game"
15309   },
15310   {
15311     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15312     GAME_CTRL_ID_PLAY,                          NULL,
15313     TRUE,                                       "play game"
15314   },
15315   {
15316     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15317     GAME_CTRL_ID_UNDO,                          NULL,
15318     TRUE,                                       "undo step"
15319   },
15320   {
15321     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15322     GAME_CTRL_ID_REDO,                          NULL,
15323     TRUE,                                       "redo step"
15324   },
15325   {
15326     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15327     GAME_CTRL_ID_SAVE,                          NULL,
15328     TRUE,                                       "save game"
15329   },
15330   {
15331     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15332     GAME_CTRL_ID_PAUSE2,                        NULL,
15333     TRUE,                                       "pause game"
15334   },
15335   {
15336     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15337     GAME_CTRL_ID_LOAD,                          NULL,
15338     TRUE,                                       "load game"
15339   },
15340   {
15341     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15342     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15343     FALSE,                                      "stop game"
15344   },
15345   {
15346     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15347     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15348     FALSE,                                      "pause game"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15352     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15353     FALSE,                                      "play game"
15354   },
15355   {
15356     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15357     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15358     TRUE,                                       "background music on/off"
15359   },
15360   {
15361     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15362     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15363     TRUE,                                       "sound loops on/off"
15364   },
15365   {
15366     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15367     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15368     TRUE,                                       "normal sounds on/off"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15372     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15373     FALSE,                                      "background music on/off"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15377     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15378     FALSE,                                      "sound loops on/off"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15382     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15383     FALSE,                                      "normal sounds on/off"
15384   }
15385 };
15386
15387 void CreateGameButtons()
15388 {
15389   int i;
15390
15391   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15392   {
15393     int graphic = gamebutton_info[i].graphic;
15394     struct GraphicInfo *gfx = &graphic_info[graphic];
15395     struct XY *pos = gamebutton_info[i].pos;
15396     struct GadgetInfo *gi;
15397     int button_type;
15398     boolean checked;
15399     unsigned int event_mask;
15400     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15401     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15402     int base_x = (on_tape ? VX : DX);
15403     int base_y = (on_tape ? VY : DY);
15404     int gd_x   = gfx->src_x;
15405     int gd_y   = gfx->src_y;
15406     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15407     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15408     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15409     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15410     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15411     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15412     int id = i;
15413
15414     if (gfx->bitmap == NULL)
15415     {
15416       game_gadget[id] = NULL;
15417
15418       continue;
15419     }
15420
15421     if (id == GAME_CTRL_ID_STOP ||
15422         id == GAME_CTRL_ID_PANEL_STOP ||
15423         id == GAME_CTRL_ID_PLAY ||
15424         id == GAME_CTRL_ID_PANEL_PLAY ||
15425         id == GAME_CTRL_ID_SAVE ||
15426         id == GAME_CTRL_ID_LOAD)
15427     {
15428       button_type = GD_TYPE_NORMAL_BUTTON;
15429       checked = FALSE;
15430       event_mask = GD_EVENT_RELEASED;
15431     }
15432     else if (id == GAME_CTRL_ID_UNDO ||
15433              id == GAME_CTRL_ID_REDO)
15434     {
15435       button_type = GD_TYPE_NORMAL_BUTTON;
15436       checked = FALSE;
15437       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15438     }
15439     else
15440     {
15441       button_type = GD_TYPE_CHECK_BUTTON;
15442       checked = (gamebutton_info[i].setup_value != NULL ?
15443                  *gamebutton_info[i].setup_value : FALSE);
15444       event_mask = GD_EVENT_PRESSED;
15445     }
15446
15447     gi = CreateGadget(GDI_CUSTOM_ID, id,
15448                       GDI_IMAGE_ID, graphic,
15449                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15450                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15451                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15452                       GDI_WIDTH, gfx->width,
15453                       GDI_HEIGHT, gfx->height,
15454                       GDI_TYPE, button_type,
15455                       GDI_STATE, GD_BUTTON_UNPRESSED,
15456                       GDI_CHECKED, checked,
15457                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15458                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15459                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15460                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15461                       GDI_DIRECT_DRAW, FALSE,
15462                       GDI_EVENT_MASK, event_mask,
15463                       GDI_CALLBACK_ACTION, HandleGameButtons,
15464                       GDI_END);
15465
15466     if (gi == NULL)
15467       Error(ERR_EXIT, "cannot create gadget");
15468
15469     game_gadget[id] = gi;
15470   }
15471 }
15472
15473 void FreeGameButtons()
15474 {
15475   int i;
15476
15477   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15478     FreeGadget(game_gadget[i]);
15479 }
15480
15481 static void UnmapGameButtonsAtSamePosition(int id)
15482 {
15483   int i;
15484
15485   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15486     if (i != id &&
15487         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15488         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15489       UnmapGadget(game_gadget[i]);
15490 }
15491
15492 static void UnmapGameButtonsAtSamePosition_All()
15493 {
15494   if (setup.show_snapshot_buttons)
15495   {
15496     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15497     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15498     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15499   }
15500   else
15501   {
15502     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15503     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15504     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15505
15506     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15507     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15508     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15509   }
15510 }
15511
15512 static void MapGameButtonsAtSamePosition(int id)
15513 {
15514   int i;
15515
15516   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15517     if (i != id &&
15518         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15519         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15520       MapGadget(game_gadget[i]);
15521
15522   UnmapGameButtonsAtSamePosition_All();
15523 }
15524
15525 void MapUndoRedoButtons()
15526 {
15527   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15528   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15529
15530   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15531   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15532
15533   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15534 }
15535
15536 void UnmapUndoRedoButtons()
15537 {
15538   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15539   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15540
15541   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15542   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15543
15544   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15545 }
15546
15547 void MapGameButtonsExt(boolean on_tape)
15548 {
15549   int i;
15550
15551   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15552     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15553         i != GAME_CTRL_ID_UNDO &&
15554         i != GAME_CTRL_ID_REDO)
15555       MapGadget(game_gadget[i]);
15556
15557   UnmapGameButtonsAtSamePosition_All();
15558
15559   RedrawGameButtons();
15560 }
15561
15562 void UnmapGameButtonsExt(boolean on_tape)
15563 {
15564   int i;
15565
15566   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15567     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15568       UnmapGadget(game_gadget[i]);
15569 }
15570
15571 void RedrawGameButtonsExt(boolean on_tape)
15572 {
15573   int i;
15574
15575   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15576     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15577       RedrawGadget(game_gadget[i]);
15578
15579   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15580   redraw_mask &= ~REDRAW_ALL;
15581 }
15582
15583 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15584 {
15585   if (gi == NULL)
15586     return;
15587
15588   gi->checked = state;
15589 }
15590
15591 void RedrawSoundButtonGadget(int id)
15592 {
15593   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15594              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15595              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15596              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15597              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15598              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15599              id);
15600
15601   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15602   RedrawGadget(game_gadget[id2]);
15603 }
15604
15605 void MapGameButtons()
15606 {
15607   MapGameButtonsExt(FALSE);
15608 }
15609
15610 void UnmapGameButtons()
15611 {
15612   UnmapGameButtonsExt(FALSE);
15613 }
15614
15615 void RedrawGameButtons()
15616 {
15617   RedrawGameButtonsExt(FALSE);
15618 }
15619
15620 void MapGameButtonsOnTape()
15621 {
15622   MapGameButtonsExt(TRUE);
15623 }
15624
15625 void UnmapGameButtonsOnTape()
15626 {
15627   UnmapGameButtonsExt(TRUE);
15628 }
15629
15630 void RedrawGameButtonsOnTape()
15631 {
15632   RedrawGameButtonsExt(TRUE);
15633 }
15634
15635 void GameUndoRedoExt()
15636 {
15637   ClearPlayerAction();
15638
15639   tape.pausing = TRUE;
15640
15641   RedrawPlayfield();
15642   UpdateAndDisplayGameControlValues();
15643
15644   DrawCompleteVideoDisplay();
15645   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15646   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15647   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15648
15649   BackToFront();
15650 }
15651
15652 void GameUndo(int steps)
15653 {
15654   if (!CheckEngineSnapshotList())
15655     return;
15656
15657   LoadEngineSnapshot_Undo(steps);
15658
15659   GameUndoRedoExt();
15660 }
15661
15662 void GameRedo(int steps)
15663 {
15664   if (!CheckEngineSnapshotList())
15665     return;
15666
15667   LoadEngineSnapshot_Redo(steps);
15668
15669   GameUndoRedoExt();
15670 }
15671
15672 static void HandleGameButtonsExt(int id, int button)
15673 {
15674   static boolean game_undo_executed = FALSE;
15675   int steps = BUTTON_STEPSIZE(button);
15676   boolean handle_game_buttons =
15677     (game_status == GAME_MODE_PLAYING ||
15678      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15679
15680   if (!handle_game_buttons)
15681     return;
15682
15683   switch (id)
15684   {
15685     case GAME_CTRL_ID_STOP:
15686     case GAME_CTRL_ID_PANEL_STOP:
15687       if (game_status == GAME_MODE_MAIN)
15688         break;
15689
15690       if (tape.playing)
15691         TapeStop();
15692       else
15693         RequestQuitGame(TRUE);
15694
15695       break;
15696
15697     case GAME_CTRL_ID_PAUSE:
15698     case GAME_CTRL_ID_PAUSE2:
15699     case GAME_CTRL_ID_PANEL_PAUSE:
15700       if (network.enabled && game_status == GAME_MODE_PLAYING)
15701       {
15702         if (tape.pausing)
15703           SendToServer_ContinuePlaying();
15704         else
15705           SendToServer_PausePlaying();
15706       }
15707       else
15708         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15709
15710       game_undo_executed = FALSE;
15711
15712       break;
15713
15714     case GAME_CTRL_ID_PLAY:
15715     case GAME_CTRL_ID_PANEL_PLAY:
15716       if (game_status == GAME_MODE_MAIN)
15717       {
15718         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15719       }
15720       else if (tape.pausing)
15721       {
15722         if (network.enabled)
15723           SendToServer_ContinuePlaying();
15724         else
15725           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15726       }
15727       break;
15728
15729     case GAME_CTRL_ID_UNDO:
15730       // Important: When using "save snapshot when collecting an item" mode,
15731       // load last (current) snapshot for first "undo" after pressing "pause"
15732       // (else the last-but-one snapshot would be loaded, because the snapshot
15733       // pointer already points to the last snapshot when pressing "pause",
15734       // which is fine for "every step/move" mode, but not for "every collect")
15735       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15736           !game_undo_executed)
15737         steps--;
15738
15739       game_undo_executed = TRUE;
15740
15741       GameUndo(steps);
15742       break;
15743
15744     case GAME_CTRL_ID_REDO:
15745       GameRedo(steps);
15746       break;
15747
15748     case GAME_CTRL_ID_SAVE:
15749       TapeQuickSave();
15750       break;
15751
15752     case GAME_CTRL_ID_LOAD:
15753       TapeQuickLoad();
15754       break;
15755
15756     case SOUND_CTRL_ID_MUSIC:
15757     case SOUND_CTRL_ID_PANEL_MUSIC:
15758       if (setup.sound_music)
15759       { 
15760         setup.sound_music = FALSE;
15761
15762         FadeMusic();
15763       }
15764       else if (audio.music_available)
15765       { 
15766         setup.sound = setup.sound_music = TRUE;
15767
15768         SetAudioMode(setup.sound);
15769
15770         if (game_status == GAME_MODE_PLAYING)
15771           PlayLevelMusic();
15772       }
15773
15774       RedrawSoundButtonGadget(id);
15775
15776       break;
15777
15778     case SOUND_CTRL_ID_LOOPS:
15779     case SOUND_CTRL_ID_PANEL_LOOPS:
15780       if (setup.sound_loops)
15781         setup.sound_loops = FALSE;
15782       else if (audio.loops_available)
15783       {
15784         setup.sound = setup.sound_loops = TRUE;
15785
15786         SetAudioMode(setup.sound);
15787       }
15788
15789       RedrawSoundButtonGadget(id);
15790
15791       break;
15792
15793     case SOUND_CTRL_ID_SIMPLE:
15794     case SOUND_CTRL_ID_PANEL_SIMPLE:
15795       if (setup.sound_simple)
15796         setup.sound_simple = FALSE;
15797       else if (audio.sound_available)
15798       {
15799         setup.sound = setup.sound_simple = TRUE;
15800
15801         SetAudioMode(setup.sound);
15802       }
15803
15804       RedrawSoundButtonGadget(id);
15805
15806       break;
15807
15808     default:
15809       break;
15810   }
15811 }
15812
15813 static void HandleGameButtons(struct GadgetInfo *gi)
15814 {
15815   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15816 }
15817
15818 void HandleSoundButtonKeys(Key key)
15819 {
15820   if (key == setup.shortcut.sound_simple)
15821     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15822   else if (key == setup.shortcut.sound_loops)
15823     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15824   else if (key == setup.shortcut.sound_music)
15825     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15826 }