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