2f57bf51cc7f32a6aee74dc3e04f5c3c5dd10fb3
[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(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!options.network || player->connected)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (options.network && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348     player->mouse_action.button_hint = 0;
3349
3350     player->effective_mouse_action.lx = 0;
3351     player->effective_mouse_action.ly = 0;
3352     player->effective_mouse_action.button = 0;
3353     player->effective_mouse_action.button_hint = 0;
3354
3355     player->score = 0;
3356     player->score_final = 0;
3357
3358     player->health = MAX_HEALTH;
3359     player->health_final = MAX_HEALTH;
3360
3361     player->gems_still_needed = level.gems_needed;
3362     player->sokobanfields_still_needed = 0;
3363     player->lights_still_needed = 0;
3364     player->friends_still_needed = 0;
3365
3366     for (j = 0; j < MAX_NUM_KEYS; j++)
3367       player->key[j] = FALSE;
3368
3369     player->num_white_keys = 0;
3370
3371     player->dynabomb_count = 0;
3372     player->dynabomb_size = 1;
3373     player->dynabombs_left = 0;
3374     player->dynabomb_xl = FALSE;
3375
3376     player->MovDir = initial_move_dir;
3377     player->MovPos = 0;
3378     player->GfxPos = 0;
3379     player->GfxDir = initial_move_dir;
3380     player->GfxAction = ACTION_DEFAULT;
3381     player->Frame = 0;
3382     player->StepFrame = 0;
3383
3384     player->initial_element = player->element_nr;
3385     player->artwork_element =
3386       (level.use_artwork_element[i] ? level.artwork_element[i] :
3387        player->element_nr);
3388     player->use_murphy = FALSE;
3389
3390     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3391     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3392
3393     player->gravity = level.initial_player_gravity[i];
3394
3395     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3396
3397     player->actual_frame_counter = 0;
3398
3399     player->step_counter = 0;
3400
3401     player->last_move_dir = initial_move_dir;
3402
3403     player->is_active = FALSE;
3404
3405     player->is_waiting = FALSE;
3406     player->is_moving = FALSE;
3407     player->is_auto_moving = FALSE;
3408     player->is_digging = FALSE;
3409     player->is_snapping = FALSE;
3410     player->is_collecting = FALSE;
3411     player->is_pushing = FALSE;
3412     player->is_switching = FALSE;
3413     player->is_dropping = FALSE;
3414     player->is_dropping_pressed = FALSE;
3415
3416     player->is_bored = FALSE;
3417     player->is_sleeping = FALSE;
3418
3419     player->was_waiting = TRUE;
3420     player->was_moving = FALSE;
3421     player->was_snapping = FALSE;
3422     player->was_dropping = FALSE;
3423
3424     player->force_dropping = FALSE;
3425
3426     player->frame_counter_bored = -1;
3427     player->frame_counter_sleeping = -1;
3428
3429     player->anim_delay_counter = 0;
3430     player->post_delay_counter = 0;
3431
3432     player->dir_waiting = initial_move_dir;
3433     player->action_waiting = ACTION_DEFAULT;
3434     player->last_action_waiting = ACTION_DEFAULT;
3435     player->special_action_bored = ACTION_DEFAULT;
3436     player->special_action_sleeping = ACTION_DEFAULT;
3437
3438     player->switch_x = -1;
3439     player->switch_y = -1;
3440
3441     player->drop_x = -1;
3442     player->drop_y = -1;
3443
3444     player->show_envelope = 0;
3445
3446     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3447
3448     player->push_delay       = -1;      /* initialized when pushing starts */
3449     player->push_delay_value = game.initial_push_delay_value;
3450
3451     player->drop_delay = 0;
3452     player->drop_pressed_delay = 0;
3453
3454     player->last_jx = -1;
3455     player->last_jy = -1;
3456     player->jx = -1;
3457     player->jy = -1;
3458
3459     player->shield_normal_time_left = 0;
3460     player->shield_deadly_time_left = 0;
3461
3462     player->inventory_infinite_element = EL_UNDEFINED;
3463     player->inventory_size = 0;
3464
3465     if (level.use_initial_inventory[i])
3466     {
3467       for (j = 0; j < level.initial_inventory_size[i]; j++)
3468       {
3469         int element = level.initial_inventory_content[i][j];
3470         int collect_count = element_info[element].collect_count_initial;
3471         int k;
3472
3473         if (!IS_CUSTOM_ELEMENT(element))
3474           collect_count = 1;
3475
3476         if (collect_count == 0)
3477           player->inventory_infinite_element = element;
3478         else
3479           for (k = 0; k < collect_count; k++)
3480             if (player->inventory_size < MAX_INVENTORY_SIZE)
3481               player->inventory_element[player->inventory_size++] = element;
3482       }
3483     }
3484
3485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486     SnapField(player, 0, 0);
3487
3488     player->LevelSolved = FALSE;
3489     player->GameOver = FALSE;
3490
3491     player->LevelSolved_GameWon = FALSE;
3492     player->LevelSolved_GameEnd = FALSE;
3493     player->LevelSolved_PanelOff = FALSE;
3494     player->LevelSolved_SaveTape = FALSE;
3495     player->LevelSolved_SaveScore = FALSE;
3496
3497     player->LevelSolved_CountingTime = 0;
3498     player->LevelSolved_CountingScore = 0;
3499     player->LevelSolved_CountingHealth = 0;
3500
3501     map_player_action[i] = i;
3502   }
3503
3504   network_player_action_received = FALSE;
3505
3506 #if defined(NETWORK_AVALIABLE)
3507   /* initial null action */
3508   if (network_playing)
3509     SendToServer_MovePlayer(MV_NONE);
3510 #endif
3511
3512   ZX = ZY = -1;
3513   ExitX = ExitY = -1;
3514
3515   FrameCounter = 0;
3516   TimeFrames = 0;
3517   TimePlayed = 0;
3518   TimeLeft = level.time;
3519   TapeTime = 0;
3520
3521   ScreenMovDir = MV_NONE;
3522   ScreenMovPos = 0;
3523   ScreenGfxPos = 0;
3524
3525   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3526
3527   AllPlayersGone = FALSE;
3528
3529   game.no_time_limit = (level.time == 0);
3530
3531   game.yamyam_content_nr = 0;
3532   game.robot_wheel_active = FALSE;
3533   game.magic_wall_active = FALSE;
3534   game.magic_wall_time_left = 0;
3535   game.light_time_left = 0;
3536   game.timegate_time_left = 0;
3537   game.switchgate_pos = 0;
3538   game.wind_direction = level.wind_direction_initial;
3539
3540   game.lenses_time_left = 0;
3541   game.magnify_time_left = 0;
3542
3543   game.ball_state = level.ball_state_initial;
3544   game.ball_content_nr = 0;
3545
3546   game.envelope_active = FALSE;
3547
3548   /* set focus to local player for network games, else to all players */
3549   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3550   game.centered_player_nr_next = game.centered_player_nr;
3551   game.set_centered_player = FALSE;
3552
3553   if (network_playing && tape.recording)
3554   {
3555     /* store client dependent player focus when recording network games */
3556     tape.centered_player_nr_next = game.centered_player_nr_next;
3557     tape.set_centered_player = TRUE;
3558   }
3559
3560   for (i = 0; i < NUM_BELTS; i++)
3561   {
3562     game.belt_dir[i] = MV_NONE;
3563     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3564   }
3565
3566   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3567     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3568
3569 #if DEBUG_INIT_PLAYER
3570   if (options.debug)
3571   {
3572     printf("Player status at level initialization:\n");
3573   }
3574 #endif
3575
3576   SCAN_PLAYFIELD(x, y)
3577   {
3578     Feld[x][y] = level.field[x][y];
3579     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3580     ChangeDelay[x][y] = 0;
3581     ChangePage[x][y] = -1;
3582     CustomValue[x][y] = 0;              /* initialized in InitField() */
3583     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3584     AmoebaNr[x][y] = 0;
3585     WasJustMoving[x][y] = 0;
3586     WasJustFalling[x][y] = 0;
3587     CheckCollision[x][y] = 0;
3588     CheckImpact[x][y] = 0;
3589     Stop[x][y] = FALSE;
3590     Pushed[x][y] = FALSE;
3591
3592     ChangeCount[x][y] = 0;
3593     ChangeEvent[x][y] = -1;
3594
3595     ExplodePhase[x][y] = 0;
3596     ExplodeDelay[x][y] = 0;
3597     ExplodeField[x][y] = EX_TYPE_NONE;
3598
3599     RunnerVisit[x][y] = 0;
3600     PlayerVisit[x][y] = 0;
3601
3602     GfxFrame[x][y] = 0;
3603     GfxRandom[x][y] = INIT_GFX_RANDOM();
3604     GfxElement[x][y] = EL_UNDEFINED;
3605     GfxAction[x][y] = ACTION_DEFAULT;
3606     GfxDir[x][y] = MV_NONE;
3607     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3608   }
3609
3610   SCAN_PLAYFIELD(x, y)
3611   {
3612     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3613       emulate_bd = FALSE;
3614     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3615       emulate_sb = FALSE;
3616     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3617       emulate_sp = FALSE;
3618
3619     InitField(x, y, TRUE);
3620
3621     ResetGfxAnimation(x, y);
3622   }
3623
3624   InitBeltMovement();
3625
3626   for (i = 0; i < MAX_PLAYERS; i++)
3627   {
3628     struct PlayerInfo *player = &stored_player[i];
3629
3630     /* set number of special actions for bored and sleeping animation */
3631     player->num_special_action_bored =
3632       get_num_special_action(player->artwork_element,
3633                              ACTION_BORING_1, ACTION_BORING_LAST);
3634     player->num_special_action_sleeping =
3635       get_num_special_action(player->artwork_element,
3636                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3637   }
3638
3639   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3640                     emulate_sb ? EMU_SOKOBAN :
3641                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3642
3643   /* initialize type of slippery elements */
3644   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3645   {
3646     if (!IS_CUSTOM_ELEMENT(i))
3647     {
3648       /* default: elements slip down either to the left or right randomly */
3649       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3650
3651       /* SP style elements prefer to slip down on the left side */
3652       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3653         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3654
3655       /* BD style elements prefer to slip down on the left side */
3656       if (game.emulation == EMU_BOULDERDASH)
3657         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658     }
3659   }
3660
3661   /* initialize explosion and ignition delay */
3662   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3663   {
3664     if (!IS_CUSTOM_ELEMENT(i))
3665     {
3666       int num_phase = 8;
3667       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3668                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3669                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670       int last_phase = (num_phase + 1) * delay;
3671       int half_phase = (num_phase / 2) * delay;
3672
3673       element_info[i].explosion_delay = last_phase - 1;
3674       element_info[i].ignition_delay = half_phase;
3675
3676       if (i == EL_BLACK_ORB)
3677         element_info[i].ignition_delay = 1;
3678     }
3679   }
3680
3681   /* correct non-moving belts to start moving left */
3682   for (i = 0; i < NUM_BELTS; i++)
3683     if (game.belt_dir[i] == MV_NONE)
3684       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3685
3686 #if USE_NEW_PLAYER_ASSIGNMENTS
3687   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3688   /* choose default local player */
3689   local_player = &stored_player[0];
3690
3691   for (i = 0; i < MAX_PLAYERS; i++)
3692     stored_player[i].connected = FALSE;
3693
3694   local_player->connected = TRUE;
3695   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3696
3697   if (tape.playing)
3698   {
3699     for (i = 0; i < MAX_PLAYERS; i++)
3700       stored_player[i].connected = tape.player_participates[i];
3701   }
3702   else if (game.team_mode && !options.network)
3703   {
3704     /* try to guess locally connected team mode players (needed for correct
3705        assignment of player figures from level to locally playing players) */
3706
3707     for (i = 0; i < MAX_PLAYERS; i++)
3708       if (setup.input[i].use_joystick ||
3709           setup.input[i].key.left != KSYM_UNDEFINED)
3710         stored_player[i].connected = TRUE;
3711   }
3712
3713 #if DEBUG_INIT_PLAYER
3714   if (options.debug)
3715   {
3716     printf("Player status after level initialization:\n");
3717
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719     {
3720       struct PlayerInfo *player = &stored_player[i];
3721
3722       printf("- player %d: present == %d, connected == %d, active == %d",
3723              i + 1,
3724              player->present,
3725              player->connected,
3726              player->active);
3727
3728       if (local_player == player)
3729         printf(" (local player)");
3730
3731       printf("\n");
3732     }
3733   }
3734 #endif
3735
3736 #if DEBUG_INIT_PLAYER
3737   if (options.debug)
3738     printf("Reassigning players ...\n");
3739 #endif
3740
3741   /* check if any connected player was not found in playfield */
3742   for (i = 0; i < MAX_PLAYERS; i++)
3743   {
3744     struct PlayerInfo *player = &stored_player[i];
3745
3746     if (player->connected && !player->present)
3747     {
3748       struct PlayerInfo *field_player = NULL;
3749
3750 #if DEBUG_INIT_PLAYER
3751       if (options.debug)
3752         printf("- looking for field player for player %d ...\n", i + 1);
3753 #endif
3754
3755       /* assign first free player found that is present in the playfield */
3756
3757       /* first try: look for unmapped playfield player that is not connected */
3758       for (j = 0; j < MAX_PLAYERS; j++)
3759         if (field_player == NULL &&
3760             stored_player[j].present &&
3761             !stored_player[j].mapped &&
3762             !stored_player[j].connected)
3763           field_player = &stored_player[j];
3764
3765       /* second try: look for *any* unmapped playfield player */
3766       for (j = 0; j < MAX_PLAYERS; j++)
3767         if (field_player == NULL &&
3768             stored_player[j].present &&
3769             !stored_player[j].mapped)
3770           field_player = &stored_player[j];
3771
3772       if (field_player != NULL)
3773       {
3774         int jx = field_player->jx, jy = field_player->jy;
3775
3776 #if DEBUG_INIT_PLAYER
3777         if (options.debug)
3778           printf("- found player %d\n", field_player->index_nr + 1);
3779 #endif
3780
3781         player->present = FALSE;
3782         player->active = FALSE;
3783
3784         field_player->present = TRUE;
3785         field_player->active = TRUE;
3786
3787         /*
3788         player->initial_element = field_player->initial_element;
3789         player->artwork_element = field_player->artwork_element;
3790
3791         player->block_last_field       = field_player->block_last_field;
3792         player->block_delay_adjustment = field_player->block_delay_adjustment;
3793         */
3794
3795         StorePlayer[jx][jy] = field_player->element_nr;
3796
3797         field_player->jx = field_player->last_jx = jx;
3798         field_player->jy = field_player->last_jy = jy;
3799
3800         if (local_player == player)
3801           local_player = field_player;
3802
3803         map_player_action[field_player->index_nr] = i;
3804
3805         field_player->mapped = TRUE;
3806
3807 #if DEBUG_INIT_PLAYER
3808         if (options.debug)
3809           printf("- map_player_action[%d] == %d\n",
3810                  field_player->index_nr + 1, i + 1);
3811 #endif
3812       }
3813     }
3814
3815     if (player->connected && player->present)
3816       player->mapped = TRUE;
3817   }
3818
3819 #if DEBUG_INIT_PLAYER
3820   if (options.debug)
3821   {
3822     printf("Player status after player assignment (first stage):\n");
3823
3824     for (i = 0; i < MAX_PLAYERS; i++)
3825     {
3826       struct PlayerInfo *player = &stored_player[i];
3827
3828       printf("- player %d: present == %d, connected == %d, active == %d",
3829              i + 1,
3830              player->present,
3831              player->connected,
3832              player->active);
3833
3834       if (local_player == player)
3835         printf(" (local player)");
3836
3837       printf("\n");
3838     }
3839   }
3840 #endif
3841
3842 #else
3843
3844   /* check if any connected player was not found in playfield */
3845   for (i = 0; i < MAX_PLAYERS; i++)
3846   {
3847     struct PlayerInfo *player = &stored_player[i];
3848
3849     if (player->connected && !player->present)
3850     {
3851       for (j = 0; j < MAX_PLAYERS; j++)
3852       {
3853         struct PlayerInfo *field_player = &stored_player[j];
3854         int jx = field_player->jx, jy = field_player->jy;
3855
3856         /* assign first free player found that is present in the playfield */
3857         if (field_player->present && !field_player->connected)
3858         {
3859           player->present = TRUE;
3860           player->active = TRUE;
3861
3862           field_player->present = FALSE;
3863           field_player->active = FALSE;
3864
3865           player->initial_element = field_player->initial_element;
3866           player->artwork_element = field_player->artwork_element;
3867
3868           player->block_last_field       = field_player->block_last_field;
3869           player->block_delay_adjustment = field_player->block_delay_adjustment;
3870
3871           StorePlayer[jx][jy] = player->element_nr;
3872
3873           player->jx = player->last_jx = jx;
3874           player->jy = player->last_jy = jy;
3875
3876           break;
3877         }
3878       }
3879     }
3880   }
3881 #endif
3882
3883 #if 0
3884   printf("::: local_player->present == %d\n", local_player->present);
3885 #endif
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 (!options.network && !game.team_mode)         /* && !tape.playing */
3933   {
3934     /* when in single player mode, eliminate all but the first active player */
3935
3936     for (i = 0; i < MAX_PLAYERS; i++)
3937     {
3938       if (stored_player[i].active)
3939       {
3940         for (j = i + 1; j < MAX_PLAYERS; j++)
3941         {
3942           if (stored_player[j].active)
3943           {
3944             struct PlayerInfo *player = &stored_player[j];
3945             int jx = player->jx, jy = player->jy;
3946
3947             player->active = FALSE;
3948             player->present = FALSE;
3949
3950             StorePlayer[jx][jy] = 0;
3951             Feld[jx][jy] = EL_EMPTY;
3952           }
3953         }
3954       }
3955     }
3956   }
3957
3958   /* when recording the game, store which players take part in the game */
3959   if (tape.recording)
3960   {
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963       if (stored_player[i].connected)
3964         tape.player_participates[i] = TRUE;
3965 #else
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (stored_player[i].active)
3968         tape.player_participates[i] = TRUE;
3969 #endif
3970   }
3971
3972 #if DEBUG_INIT_PLAYER
3973   if (options.debug)
3974   {
3975     printf("Player status after player assignment (final stage):\n");
3976
3977     for (i = 0; i < MAX_PLAYERS; i++)
3978     {
3979       struct PlayerInfo *player = &stored_player[i];
3980
3981       printf("- player %d: present == %d, connected == %d, active == %d",
3982              i + 1,
3983              player->present,
3984              player->connected,
3985              player->active);
3986
3987       if (local_player == player)
3988         printf(" (local player)");
3989
3990       printf("\n");
3991     }
3992   }
3993 #endif
3994
3995   if (BorderElement == EL_EMPTY)
3996   {
3997     SBX_Left = 0;
3998     SBX_Right = lev_fieldx - SCR_FIELDX;
3999     SBY_Upper = 0;
4000     SBY_Lower = lev_fieldy - SCR_FIELDY;
4001   }
4002   else
4003   {
4004     SBX_Left = -1;
4005     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4006     SBY_Upper = -1;
4007     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4008   }
4009
4010   if (full_lev_fieldx <= SCR_FIELDX)
4011     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4012   if (full_lev_fieldy <= SCR_FIELDY)
4013     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4014
4015   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4016     SBX_Left--;
4017   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4018     SBY_Upper--;
4019
4020   /* if local player not found, look for custom element that might create
4021      the player (make some assumptions about the right custom element) */
4022   if (!local_player->present)
4023   {
4024     int start_x = 0, start_y = 0;
4025     int found_rating = 0;
4026     int found_element = EL_UNDEFINED;
4027     int player_nr = local_player->index_nr;
4028
4029     SCAN_PLAYFIELD(x, y)
4030     {
4031       int element = Feld[x][y];
4032       int content;
4033       int xx, yy;
4034       boolean is_player;
4035
4036       if (level.use_start_element[player_nr] &&
4037           level.start_element[player_nr] == element &&
4038           found_rating < 4)
4039       {
4040         start_x = x;
4041         start_y = y;
4042
4043         found_rating = 4;
4044         found_element = element;
4045       }
4046
4047       if (!IS_CUSTOM_ELEMENT(element))
4048         continue;
4049
4050       if (CAN_CHANGE(element))
4051       {
4052         for (i = 0; i < element_info[element].num_change_pages; i++)
4053         {
4054           /* check for player created from custom element as single target */
4055           content = element_info[element].change_page[i].target_element;
4056           is_player = ELEM_IS_PLAYER(content);
4057
4058           if (is_player && (found_rating < 3 ||
4059                             (found_rating == 3 && element < found_element)))
4060           {
4061             start_x = x;
4062             start_y = y;
4063
4064             found_rating = 3;
4065             found_element = element;
4066           }
4067         }
4068       }
4069
4070       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4071       {
4072         /* check for player created from custom element as explosion content */
4073         content = element_info[element].content.e[xx][yy];
4074         is_player = ELEM_IS_PLAYER(content);
4075
4076         if (is_player && (found_rating < 2 ||
4077                           (found_rating == 2 && element < found_element)))
4078         {
4079           start_x = x + xx - 1;
4080           start_y = y + yy - 1;
4081
4082           found_rating = 2;
4083           found_element = element;
4084         }
4085
4086         if (!CAN_CHANGE(element))
4087           continue;
4088
4089         for (i = 0; i < element_info[element].num_change_pages; i++)
4090         {
4091           /* check for player created from custom element as extended target */
4092           content =
4093             element_info[element].change_page[i].target_content.e[xx][yy];
4094
4095           is_player = ELEM_IS_PLAYER(content);
4096
4097           if (is_player && (found_rating < 1 ||
4098                             (found_rating == 1 && element < found_element)))
4099           {
4100             start_x = x + xx - 1;
4101             start_y = y + yy - 1;
4102
4103             found_rating = 1;
4104             found_element = element;
4105           }
4106         }
4107       }
4108     }
4109
4110     scroll_x = SCROLL_POSITION_X(start_x);
4111     scroll_y = SCROLL_POSITION_Y(start_y);
4112   }
4113   else
4114   {
4115     scroll_x = SCROLL_POSITION_X(local_player->jx);
4116     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4117   }
4118
4119   /* !!! FIX THIS (START) !!! */
4120   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4121   {
4122     InitGameEngine_EM();
4123   }
4124   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4125   {
4126     InitGameEngine_SP();
4127   }
4128   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4129   {
4130     InitGameEngine_MM();
4131   }
4132   else
4133   {
4134     DrawLevel(REDRAW_FIELD);
4135     DrawAllPlayers();
4136
4137     /* after drawing the level, correct some elements */
4138     if (game.timegate_time_left == 0)
4139       CloseAllOpenTimegates();
4140   }
4141
4142   /* blit playfield from scroll buffer to normal back buffer for fading in */
4143   BlitScreenToBitmap(backbuffer);
4144   /* !!! FIX THIS (END) !!! */
4145
4146   DrawMaskedBorder(fade_mask);
4147
4148   FadeIn(fade_mask);
4149
4150 #if 1
4151   // full screen redraw is required at this point in the following cases:
4152   // - special editor door undrawn when game was started from level editor
4153   // - drawing area (playfield) was changed and has to be removed completely
4154   redraw_mask = REDRAW_ALL;
4155   BackToFront();
4156 #endif
4157
4158   if (!game.restart_level)
4159   {
4160     /* copy default game door content to main double buffer */
4161
4162     /* !!! CHECK AGAIN !!! */
4163     SetPanelBackground();
4164     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4165     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4166   }
4167
4168   SetPanelBackground();
4169   SetDrawBackgroundMask(REDRAW_DOOR_1);
4170
4171   UpdateAndDisplayGameControlValues();
4172
4173   if (!game.restart_level)
4174   {
4175     UnmapGameButtons();
4176     UnmapTapeButtons();
4177
4178     FreeGameButtons();
4179     CreateGameButtons();
4180
4181     MapGameButtons();
4182     MapTapeButtons();
4183
4184     /* copy actual game door content to door double buffer for OpenDoor() */
4185     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4186
4187     OpenDoor(DOOR_OPEN_ALL);
4188
4189     PlaySound(SND_GAME_STARTING);
4190
4191     if (setup.sound_music)
4192       PlayLevelMusic();
4193
4194     KeyboardAutoRepeatOffUnlessAutoplay();
4195
4196 #if DEBUG_INIT_PLAYER
4197     if (options.debug)
4198     {
4199       printf("Player status (final):\n");
4200
4201       for (i = 0; i < MAX_PLAYERS; i++)
4202       {
4203         struct PlayerInfo *player = &stored_player[i];
4204
4205         printf("- player %d: present == %d, connected == %d, active == %d",
4206                i + 1,
4207                player->present,
4208                player->connected,
4209                player->active);
4210
4211         if (local_player == player)
4212           printf(" (local player)");
4213
4214         printf("\n");
4215       }
4216     }
4217 #endif
4218   }
4219
4220   UnmapAllGadgets();
4221
4222   MapGameButtons();
4223   MapTapeButtons();
4224
4225   if (!game.restart_level && !tape.playing)
4226   {
4227     LevelStats_incPlayed(level_nr);
4228
4229     SaveLevelSetup_SeriesInfo();
4230   }
4231
4232   game.restart_level = FALSE;
4233   game.restart_game_message = NULL;
4234
4235   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4236     InitGameActions_MM();
4237
4238   SaveEngineSnapshotToListInitial();
4239 }
4240
4241 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4242                         int actual_player_x, int actual_player_y)
4243 {
4244   /* this is used for non-R'n'D game engines to update certain engine values */
4245
4246   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4247   {
4248     actual_player_x = correctLevelPosX_EM(actual_player_x);
4249     actual_player_y = correctLevelPosY_EM(actual_player_y);
4250   }
4251
4252   /* needed to determine if sounds are played within the visible screen area */
4253   scroll_x = actual_scroll_x;
4254   scroll_y = actual_scroll_y;
4255
4256   /* needed to get player position for "follow finger" playing input method */
4257   local_player->jx = actual_player_x;
4258   local_player->jy = actual_player_y;
4259 }
4260
4261 void InitMovDir(int x, int y)
4262 {
4263   int i, element = Feld[x][y];
4264   static int xy[4][2] =
4265   {
4266     {  0, +1 },
4267     { +1,  0 },
4268     {  0, -1 },
4269     { -1,  0 }
4270   };
4271   static int direction[3][4] =
4272   {
4273     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4274     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4275     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4276   };
4277
4278   switch (element)
4279   {
4280     case EL_BUG_RIGHT:
4281     case EL_BUG_UP:
4282     case EL_BUG_LEFT:
4283     case EL_BUG_DOWN:
4284       Feld[x][y] = EL_BUG;
4285       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4286       break;
4287
4288     case EL_SPACESHIP_RIGHT:
4289     case EL_SPACESHIP_UP:
4290     case EL_SPACESHIP_LEFT:
4291     case EL_SPACESHIP_DOWN:
4292       Feld[x][y] = EL_SPACESHIP;
4293       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4294       break;
4295
4296     case EL_BD_BUTTERFLY_RIGHT:
4297     case EL_BD_BUTTERFLY_UP:
4298     case EL_BD_BUTTERFLY_LEFT:
4299     case EL_BD_BUTTERFLY_DOWN:
4300       Feld[x][y] = EL_BD_BUTTERFLY;
4301       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4302       break;
4303
4304     case EL_BD_FIREFLY_RIGHT:
4305     case EL_BD_FIREFLY_UP:
4306     case EL_BD_FIREFLY_LEFT:
4307     case EL_BD_FIREFLY_DOWN:
4308       Feld[x][y] = EL_BD_FIREFLY;
4309       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4310       break;
4311
4312     case EL_PACMAN_RIGHT:
4313     case EL_PACMAN_UP:
4314     case EL_PACMAN_LEFT:
4315     case EL_PACMAN_DOWN:
4316       Feld[x][y] = EL_PACMAN;
4317       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4318       break;
4319
4320     case EL_YAMYAM_LEFT:
4321     case EL_YAMYAM_RIGHT:
4322     case EL_YAMYAM_UP:
4323     case EL_YAMYAM_DOWN:
4324       Feld[x][y] = EL_YAMYAM;
4325       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4326       break;
4327
4328     case EL_SP_SNIKSNAK:
4329       MovDir[x][y] = MV_UP;
4330       break;
4331
4332     case EL_SP_ELECTRON:
4333       MovDir[x][y] = MV_LEFT;
4334       break;
4335
4336     case EL_MOLE_LEFT:
4337     case EL_MOLE_RIGHT:
4338     case EL_MOLE_UP:
4339     case EL_MOLE_DOWN:
4340       Feld[x][y] = EL_MOLE;
4341       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4342       break;
4343
4344     default:
4345       if (IS_CUSTOM_ELEMENT(element))
4346       {
4347         struct ElementInfo *ei = &element_info[element];
4348         int move_direction_initial = ei->move_direction_initial;
4349         int move_pattern = ei->move_pattern;
4350
4351         if (move_direction_initial == MV_START_PREVIOUS)
4352         {
4353           if (MovDir[x][y] != MV_NONE)
4354             return;
4355
4356           move_direction_initial = MV_START_AUTOMATIC;
4357         }
4358
4359         if (move_direction_initial == MV_START_RANDOM)
4360           MovDir[x][y] = 1 << RND(4);
4361         else if (move_direction_initial & MV_ANY_DIRECTION)
4362           MovDir[x][y] = move_direction_initial;
4363         else if (move_pattern == MV_ALL_DIRECTIONS ||
4364                  move_pattern == MV_TURNING_LEFT ||
4365                  move_pattern == MV_TURNING_RIGHT ||
4366                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4367                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4368                  move_pattern == MV_TURNING_RANDOM)
4369           MovDir[x][y] = 1 << RND(4);
4370         else if (move_pattern == MV_HORIZONTAL)
4371           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372         else if (move_pattern == MV_VERTICAL)
4373           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374         else if (move_pattern & MV_ANY_DIRECTION)
4375           MovDir[x][y] = element_info[element].move_pattern;
4376         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377                  move_pattern == MV_ALONG_RIGHT_SIDE)
4378         {
4379           /* use random direction as default start direction */
4380           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381             MovDir[x][y] = 1 << RND(4);
4382
4383           for (i = 0; i < NUM_DIRECTIONS; i++)
4384           {
4385             int x1 = x + xy[i][0];
4386             int y1 = y + xy[i][1];
4387
4388             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4389             {
4390               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391                 MovDir[x][y] = direction[0][i];
4392               else
4393                 MovDir[x][y] = direction[1][i];
4394
4395               break;
4396             }
4397           }
4398         }                
4399       }
4400       else
4401       {
4402         MovDir[x][y] = 1 << RND(4);
4403
4404         if (element != EL_BUG &&
4405             element != EL_SPACESHIP &&
4406             element != EL_BD_BUTTERFLY &&
4407             element != EL_BD_FIREFLY)
4408           break;
4409
4410         for (i = 0; i < NUM_DIRECTIONS; i++)
4411         {
4412           int x1 = x + xy[i][0];
4413           int y1 = y + xy[i][1];
4414
4415           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4416           {
4417             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4418             {
4419               MovDir[x][y] = direction[0][i];
4420               break;
4421             }
4422             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4424             {
4425               MovDir[x][y] = direction[1][i];
4426               break;
4427             }
4428           }
4429         }
4430       }
4431       break;
4432   }
4433
4434   GfxDir[x][y] = MovDir[x][y];
4435 }
4436
4437 void InitAmoebaNr(int x, int y)
4438 {
4439   int i;
4440   int group_nr = AmoebeNachbarNr(x, y);
4441
4442   if (group_nr == 0)
4443   {
4444     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4445     {
4446       if (AmoebaCnt[i] == 0)
4447       {
4448         group_nr = i;
4449         break;
4450       }
4451     }
4452   }
4453
4454   AmoebaNr[x][y] = group_nr;
4455   AmoebaCnt[group_nr]++;
4456   AmoebaCnt2[group_nr]++;
4457 }
4458
4459 static void PlayerWins(struct PlayerInfo *player)
4460 {
4461   player->LevelSolved = TRUE;
4462   player->GameOver = TRUE;
4463
4464   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4465                          level.native_em_level->lev->score :
4466                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4467                          game_mm.score :
4468                          player->score);
4469   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4470                           MM_HEALTH(game_mm.laser_overload_value) :
4471                           player->health);
4472
4473   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4474                                       TimeLeft);
4475   player->LevelSolved_CountingScore = player->score_final;
4476   player->LevelSolved_CountingHealth = player->health_final;
4477 }
4478
4479 void GameWon()
4480 {
4481   static int time_count_steps;
4482   static int time, time_final;
4483   static int score, score_final;
4484   static int health, health_final;
4485   static int game_over_delay_1 = 0;
4486   static int game_over_delay_2 = 0;
4487   static int game_over_delay_3 = 0;
4488   int game_over_delay_value_1 = 50;
4489   int game_over_delay_value_2 = 25;
4490   int game_over_delay_value_3 = 50;
4491
4492   if (!local_player->LevelSolved_GameWon)
4493   {
4494     int i;
4495
4496     /* do not start end game actions before the player stops moving (to exit) */
4497     if (local_player->MovPos)
4498       return;
4499
4500     local_player->LevelSolved_GameWon = TRUE;
4501     local_player->LevelSolved_SaveTape = tape.recording;
4502     local_player->LevelSolved_SaveScore = !tape.playing;
4503
4504     if (!tape.playing)
4505     {
4506       LevelStats_incSolved(level_nr);
4507
4508       SaveLevelSetup_SeriesInfo();
4509     }
4510
4511     if (tape.auto_play)         /* tape might already be stopped here */
4512       tape.auto_play_level_solved = TRUE;
4513
4514     TapeStop();
4515
4516     game_over_delay_1 = 0;
4517     game_over_delay_2 = 0;
4518     game_over_delay_3 = game_over_delay_value_3;
4519
4520     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4521     score = score_final = local_player->score_final;
4522     health = health_final = local_player->health_final;
4523
4524     if (level.score[SC_TIME_BONUS] > 0)
4525     {
4526       if (TimeLeft > 0)
4527       {
4528         time_final = 0;
4529         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4530       }
4531       else if (game.no_time_limit && TimePlayed < 999)
4532       {
4533         time_final = 999;
4534         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4535       }
4536
4537       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4538
4539       game_over_delay_1 = game_over_delay_value_1;
4540
4541       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542       {
4543         health_final = 0;
4544         score_final += health * level.score[SC_TIME_BONUS];
4545
4546         game_over_delay_2 = game_over_delay_value_2;
4547       }
4548
4549       local_player->score_final = score_final;
4550       local_player->health_final = health_final;
4551     }
4552
4553     if (level_editor_test_game)
4554     {
4555       time = time_final;
4556       score = score_final;
4557
4558       local_player->LevelSolved_CountingTime = time;
4559       local_player->LevelSolved_CountingScore = score;
4560
4561       game_panel_controls[GAME_PANEL_TIME].value = time;
4562       game_panel_controls[GAME_PANEL_SCORE].value = score;
4563
4564       DisplayGameControlValues();
4565     }
4566
4567     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4568     {
4569       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4570       {
4571         /* close exit door after last player */
4572         if ((AllPlayersGone &&
4573              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4574               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4575               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4576             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4577             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4578         {
4579           int element = Feld[ExitX][ExitY];
4580
4581           Feld[ExitX][ExitY] =
4582             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4583              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4584              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4585              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4586              EL_EM_STEEL_EXIT_CLOSING);
4587
4588           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4589         }
4590
4591         /* player disappears */
4592         DrawLevelField(ExitX, ExitY);
4593       }
4594
4595       for (i = 0; i < MAX_PLAYERS; i++)
4596       {
4597         struct PlayerInfo *player = &stored_player[i];
4598
4599         if (player->present)
4600         {
4601           RemovePlayer(player);
4602
4603           /* player disappears */
4604           DrawLevelField(player->jx, player->jy);
4605         }
4606       }
4607     }
4608
4609     PlaySound(SND_GAME_WINNING);
4610   }
4611
4612   if (game_over_delay_1 > 0)
4613   {
4614     game_over_delay_1--;
4615
4616     return;
4617   }
4618
4619   if (time != time_final)
4620   {
4621     int time_to_go = ABS(time_final - time);
4622     int time_count_dir = (time < time_final ? +1 : -1);
4623
4624     if (time_to_go < time_count_steps)
4625       time_count_steps = 1;
4626
4627     time  += time_count_steps * time_count_dir;
4628     score += time_count_steps * level.score[SC_TIME_BONUS];
4629
4630     local_player->LevelSolved_CountingTime = time;
4631     local_player->LevelSolved_CountingScore = score;
4632
4633     game_panel_controls[GAME_PANEL_TIME].value = time;
4634     game_panel_controls[GAME_PANEL_SCORE].value = score;
4635
4636     DisplayGameControlValues();
4637
4638     if (time == time_final)
4639       StopSound(SND_GAME_LEVELTIME_BONUS);
4640     else if (setup.sound_loops)
4641       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4642     else
4643       PlaySound(SND_GAME_LEVELTIME_BONUS);
4644
4645     return;
4646   }
4647
4648   if (game_over_delay_2 > 0)
4649   {
4650     game_over_delay_2--;
4651
4652     return;
4653   }
4654
4655   if (health != health_final)
4656   {
4657     int health_count_dir = (health < health_final ? +1 : -1);
4658
4659     health += health_count_dir;
4660     score  += level.score[SC_TIME_BONUS];
4661
4662     local_player->LevelSolved_CountingHealth = health;
4663     local_player->LevelSolved_CountingScore = score;
4664
4665     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4666     game_panel_controls[GAME_PANEL_SCORE].value = score;
4667
4668     DisplayGameControlValues();
4669
4670     if (health == health_final)
4671       StopSound(SND_GAME_LEVELTIME_BONUS);
4672     else if (setup.sound_loops)
4673       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4674     else
4675       PlaySound(SND_GAME_LEVELTIME_BONUS);
4676
4677     return;
4678   }
4679
4680   local_player->LevelSolved_PanelOff = TRUE;
4681
4682   if (game_over_delay_3 > 0)
4683   {
4684     game_over_delay_3--;
4685
4686     return;
4687   }
4688
4689   GameEnd();
4690 }
4691
4692 void GameEnd()
4693 {
4694   int hi_pos;
4695   boolean raise_level = FALSE;
4696
4697   local_player->LevelSolved_GameEnd = TRUE;
4698
4699   if (local_player->LevelSolved_SaveTape)
4700   {
4701     /* make sure that request dialog to save tape does not open door again */
4702     if (!global.use_envelope_request)
4703       CloseDoor(DOOR_CLOSE_1);
4704
4705     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4706   }
4707
4708   /* if no tape is to be saved, close both doors simultaneously */
4709   CloseDoor(DOOR_CLOSE_ALL);
4710
4711   if (level_editor_test_game)
4712   {
4713     SetGameStatus(GAME_MODE_MAIN);
4714
4715     DrawMainMenu();
4716
4717     return;
4718   }
4719
4720   if (!local_player->LevelSolved_SaveScore)
4721   {
4722     SetGameStatus(GAME_MODE_MAIN);
4723
4724     DrawMainMenu();
4725
4726     return;
4727   }
4728
4729   if (level_nr == leveldir_current->handicap_level)
4730   {
4731     leveldir_current->handicap_level++;
4732
4733     SaveLevelSetup_SeriesInfo();
4734   }
4735
4736   if (setup.increment_levels &&
4737       level_nr < leveldir_current->last_level)
4738     raise_level = TRUE;                 /* advance to next level */
4739
4740   if ((hi_pos = NewHiScore()) >= 0) 
4741   {
4742     SetGameStatus(GAME_MODE_SCORES);
4743
4744     DrawHallOfFame(hi_pos);
4745
4746     if (raise_level)
4747     {
4748       level_nr++;
4749       TapeErase();
4750     }
4751   }
4752   else
4753   {
4754     SetGameStatus(GAME_MODE_MAIN);
4755
4756     if (raise_level)
4757     {
4758       level_nr++;
4759       TapeErase();
4760     }
4761
4762     DrawMainMenu();
4763   }
4764 }
4765
4766 int NewHiScore()
4767 {
4768   int k, l;
4769   int position = -1;
4770   boolean one_score_entry_per_name = !program.many_scores_per_name;
4771
4772   LoadScore(level_nr);
4773
4774   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4775       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4776     return -1;
4777
4778   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4779   {
4780     if (local_player->score_final > highscore[k].Score)
4781     {
4782       /* player has made it to the hall of fame */
4783
4784       if (k < MAX_SCORE_ENTRIES - 1)
4785       {
4786         int m = MAX_SCORE_ENTRIES - 1;
4787
4788         if (one_score_entry_per_name)
4789         {
4790           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4791             if (strEqual(setup.player_name, highscore[l].Name))
4792               m = l;
4793
4794           if (m == k)   /* player's new highscore overwrites his old one */
4795             goto put_into_list;
4796         }
4797
4798         for (l = m; l > k; l--)
4799         {
4800           strcpy(highscore[l].Name, highscore[l - 1].Name);
4801           highscore[l].Score = highscore[l - 1].Score;
4802         }
4803       }
4804
4805       put_into_list:
4806
4807       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4808       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4809       highscore[k].Score = local_player->score_final; 
4810       position = k;
4811
4812       break;
4813     }
4814     else if (one_score_entry_per_name &&
4815              !strncmp(setup.player_name, highscore[k].Name,
4816                       MAX_PLAYER_NAME_LEN))
4817       break;    /* player already there with a higher score */
4818   }
4819
4820   if (position >= 0) 
4821     SaveScore(level_nr);
4822
4823   return position;
4824 }
4825
4826 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4827 {
4828   int element = Feld[x][y];
4829   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4830   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4831   int horiz_move = (dx != 0);
4832   int sign = (horiz_move ? dx : dy);
4833   int step = sign * element_info[element].move_stepsize;
4834
4835   /* special values for move stepsize for spring and things on conveyor belt */
4836   if (horiz_move)
4837   {
4838     if (CAN_FALL(element) &&
4839         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4840       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4841     else if (element == EL_SPRING)
4842       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4843   }
4844
4845   return step;
4846 }
4847
4848 inline static int getElementMoveStepsize(int x, int y)
4849 {
4850   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4851 }
4852
4853 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4854 {
4855   if (player->GfxAction != action || player->GfxDir != dir)
4856   {
4857     player->GfxAction = action;
4858     player->GfxDir = dir;
4859     player->Frame = 0;
4860     player->StepFrame = 0;
4861   }
4862 }
4863
4864 static void ResetGfxFrame(int x, int y)
4865 {
4866   // profiling showed that "autotest" spends 10~20% of its time in this function
4867   if (DrawingDeactivatedField())
4868     return;
4869
4870   int element = Feld[x][y];
4871   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4872
4873   if (graphic_info[graphic].anim_global_sync)
4874     GfxFrame[x][y] = FrameCounter;
4875   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4876     GfxFrame[x][y] = CustomValue[x][y];
4877   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4878     GfxFrame[x][y] = element_info[element].collect_score;
4879   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4880     GfxFrame[x][y] = ChangeDelay[x][y];
4881 }
4882
4883 static void ResetGfxAnimation(int x, int y)
4884 {
4885   GfxAction[x][y] = ACTION_DEFAULT;
4886   GfxDir[x][y] = MovDir[x][y];
4887   GfxFrame[x][y] = 0;
4888
4889   ResetGfxFrame(x, y);
4890 }
4891
4892 static void ResetRandomAnimationValue(int x, int y)
4893 {
4894   GfxRandom[x][y] = INIT_GFX_RANDOM();
4895 }
4896
4897 void InitMovingField(int x, int y, int direction)
4898 {
4899   int element = Feld[x][y];
4900   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4901   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4902   int newx = x + dx;
4903   int newy = y + dy;
4904   boolean is_moving_before, is_moving_after;
4905
4906   /* check if element was/is moving or being moved before/after mode change */
4907   is_moving_before = (WasJustMoving[x][y] != 0);
4908   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4909
4910   /* reset animation only for moving elements which change direction of moving
4911      or which just started or stopped moving
4912      (else CEs with property "can move" / "not moving" are reset each frame) */
4913   if (is_moving_before != is_moving_after ||
4914       direction != MovDir[x][y])
4915     ResetGfxAnimation(x, y);
4916
4917   MovDir[x][y] = direction;
4918   GfxDir[x][y] = direction;
4919
4920   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4921                      direction == MV_DOWN && CAN_FALL(element) ?
4922                      ACTION_FALLING : ACTION_MOVING);
4923
4924   /* this is needed for CEs with property "can move" / "not moving" */
4925
4926   if (is_moving_after)
4927   {
4928     if (Feld[newx][newy] == EL_EMPTY)
4929       Feld[newx][newy] = EL_BLOCKED;
4930
4931     MovDir[newx][newy] = MovDir[x][y];
4932
4933     CustomValue[newx][newy] = CustomValue[x][y];
4934
4935     GfxFrame[newx][newy] = GfxFrame[x][y];
4936     GfxRandom[newx][newy] = GfxRandom[x][y];
4937     GfxAction[newx][newy] = GfxAction[x][y];
4938     GfxDir[newx][newy] = GfxDir[x][y];
4939   }
4940 }
4941
4942 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4943 {
4944   int direction = MovDir[x][y];
4945   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4946   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4947
4948   *goes_to_x = newx;
4949   *goes_to_y = newy;
4950 }
4951
4952 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4953 {
4954   int oldx = x, oldy = y;
4955   int direction = MovDir[x][y];
4956
4957   if (direction == MV_LEFT)
4958     oldx++;
4959   else if (direction == MV_RIGHT)
4960     oldx--;
4961   else if (direction == MV_UP)
4962     oldy++;
4963   else if (direction == MV_DOWN)
4964     oldy--;
4965
4966   *comes_from_x = oldx;
4967   *comes_from_y = oldy;
4968 }
4969
4970 int MovingOrBlocked2Element(int x, int y)
4971 {
4972   int element = Feld[x][y];
4973
4974   if (element == EL_BLOCKED)
4975   {
4976     int oldx, oldy;
4977
4978     Blocked2Moving(x, y, &oldx, &oldy);
4979     return Feld[oldx][oldy];
4980   }
4981   else
4982     return element;
4983 }
4984
4985 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4986 {
4987   /* like MovingOrBlocked2Element(), but if element is moving
4988      and (x,y) is the field the moving element is just leaving,
4989      return EL_BLOCKED instead of the element value */
4990   int element = Feld[x][y];
4991
4992   if (IS_MOVING(x, y))
4993   {
4994     if (element == EL_BLOCKED)
4995     {
4996       int oldx, oldy;
4997
4998       Blocked2Moving(x, y, &oldx, &oldy);
4999       return Feld[oldx][oldy];
5000     }
5001     else
5002       return EL_BLOCKED;
5003   }
5004   else
5005     return element;
5006 }
5007
5008 static void RemoveField(int x, int y)
5009 {
5010   Feld[x][y] = EL_EMPTY;
5011
5012   MovPos[x][y] = 0;
5013   MovDir[x][y] = 0;
5014   MovDelay[x][y] = 0;
5015
5016   CustomValue[x][y] = 0;
5017
5018   AmoebaNr[x][y] = 0;
5019   ChangeDelay[x][y] = 0;
5020   ChangePage[x][y] = -1;
5021   Pushed[x][y] = FALSE;
5022
5023   GfxElement[x][y] = EL_UNDEFINED;
5024   GfxAction[x][y] = ACTION_DEFAULT;
5025   GfxDir[x][y] = MV_NONE;
5026 }
5027
5028 void RemoveMovingField(int x, int y)
5029 {
5030   int oldx = x, oldy = y, newx = x, newy = y;
5031   int element = Feld[x][y];
5032   int next_element = EL_UNDEFINED;
5033
5034   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5035     return;
5036
5037   if (IS_MOVING(x, y))
5038   {
5039     Moving2Blocked(x, y, &newx, &newy);
5040
5041     if (Feld[newx][newy] != EL_BLOCKED)
5042     {
5043       /* element is moving, but target field is not free (blocked), but
5044          already occupied by something different (example: acid pool);
5045          in this case, only remove the moving field, but not the target */
5046
5047       RemoveField(oldx, oldy);
5048
5049       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5050
5051       TEST_DrawLevelField(oldx, oldy);
5052
5053       return;
5054     }
5055   }
5056   else if (element == EL_BLOCKED)
5057   {
5058     Blocked2Moving(x, y, &oldx, &oldy);
5059     if (!IS_MOVING(oldx, oldy))
5060       return;
5061   }
5062
5063   if (element == EL_BLOCKED &&
5064       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5065        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5066        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5067        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5068        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5069        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5070     next_element = get_next_element(Feld[oldx][oldy]);
5071
5072   RemoveField(oldx, oldy);
5073   RemoveField(newx, newy);
5074
5075   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5076
5077   if (next_element != EL_UNDEFINED)
5078     Feld[oldx][oldy] = next_element;
5079
5080   TEST_DrawLevelField(oldx, oldy);
5081   TEST_DrawLevelField(newx, newy);
5082 }
5083
5084 void DrawDynamite(int x, int y)
5085 {
5086   int sx = SCREENX(x), sy = SCREENY(y);
5087   int graphic = el2img(Feld[x][y]);
5088   int frame;
5089
5090   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5091     return;
5092
5093   if (IS_WALKABLE_INSIDE(Back[x][y]))
5094     return;
5095
5096   if (Back[x][y])
5097     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5098   else if (Store[x][y])
5099     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5100
5101   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5102
5103   if (Back[x][y] || Store[x][y])
5104     DrawGraphicThruMask(sx, sy, graphic, frame);
5105   else
5106     DrawGraphic(sx, sy, graphic, frame);
5107 }
5108
5109 void CheckDynamite(int x, int y)
5110 {
5111   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5112   {
5113     MovDelay[x][y]--;
5114
5115     if (MovDelay[x][y] != 0)
5116     {
5117       DrawDynamite(x, y);
5118       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5119
5120       return;
5121     }
5122   }
5123
5124   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5125
5126   Bang(x, y);
5127 }
5128
5129 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5130 {
5131   boolean num_checked_players = 0;
5132   int i;
5133
5134   for (i = 0; i < MAX_PLAYERS; i++)
5135   {
5136     if (stored_player[i].active)
5137     {
5138       int sx = stored_player[i].jx;
5139       int sy = stored_player[i].jy;
5140
5141       if (num_checked_players == 0)
5142       {
5143         *sx1 = *sx2 = sx;
5144         *sy1 = *sy2 = sy;
5145       }
5146       else
5147       {
5148         *sx1 = MIN(*sx1, sx);
5149         *sy1 = MIN(*sy1, sy);
5150         *sx2 = MAX(*sx2, sx);
5151         *sy2 = MAX(*sy2, sy);
5152       }
5153
5154       num_checked_players++;
5155     }
5156   }
5157 }
5158
5159 static boolean checkIfAllPlayersFitToScreen_RND()
5160 {
5161   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5162
5163   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5164
5165   return (sx2 - sx1 < SCR_FIELDX &&
5166           sy2 - sy1 < SCR_FIELDY);
5167 }
5168
5169 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5170 {
5171   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5172
5173   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5174
5175   *sx = (sx1 + sx2) / 2;
5176   *sy = (sy1 + sy2) / 2;
5177 }
5178
5179 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5180                         boolean center_screen, boolean quick_relocation)
5181 {
5182   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5183   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5184   boolean no_delay = (tape.warp_forward);
5185   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5186   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5187   int new_scroll_x, new_scroll_y;
5188
5189   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5190   {
5191     /* case 1: quick relocation inside visible screen (without scrolling) */
5192
5193     RedrawPlayfield();
5194
5195     return;
5196   }
5197
5198   if (!level.shifted_relocation || center_screen)
5199   {
5200     /* relocation _with_ centering of screen */
5201
5202     new_scroll_x = SCROLL_POSITION_X(x);
5203     new_scroll_y = SCROLL_POSITION_Y(y);
5204   }
5205   else
5206   {
5207     /* relocation _without_ centering of screen */
5208
5209     int center_scroll_x = SCROLL_POSITION_X(old_x);
5210     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5211     int offset_x = x + (scroll_x - center_scroll_x);
5212     int offset_y = y + (scroll_y - center_scroll_y);
5213
5214     /* for new screen position, apply previous offset to center position */
5215     new_scroll_x = SCROLL_POSITION_X(offset_x);
5216     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5217   }
5218
5219   if (quick_relocation)
5220   {
5221     /* case 2: quick relocation (redraw without visible scrolling) */
5222
5223     scroll_x = new_scroll_x;
5224     scroll_y = new_scroll_y;
5225
5226     RedrawPlayfield();
5227
5228     return;
5229   }
5230
5231   /* case 3: visible relocation (with scrolling to new position) */
5232
5233   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5234
5235   SetVideoFrameDelay(wait_delay_value);
5236
5237   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5238   {
5239     int dx = 0, dy = 0;
5240     int fx = FX, fy = FY;
5241
5242     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5243     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5244
5245     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5246       break;
5247
5248     scroll_x -= dx;
5249     scroll_y -= dy;
5250
5251     fx += dx * TILEX / 2;
5252     fy += dy * TILEY / 2;
5253
5254     ScrollLevel(dx, dy);
5255     DrawAllPlayers();
5256
5257     /* scroll in two steps of half tile size to make things smoother */
5258     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5259
5260     /* scroll second step to align at full tile size */
5261     BlitScreenToBitmap(window);
5262   }
5263
5264   DrawAllPlayers();
5265   BackToFront();
5266
5267   SetVideoFrameDelay(frame_delay_value_old);
5268 }
5269
5270 void RelocatePlayer(int jx, int jy, int el_player_raw)
5271 {
5272   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5273   int player_nr = GET_PLAYER_NR(el_player);
5274   struct PlayerInfo *player = &stored_player[player_nr];
5275   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5276   boolean no_delay = (tape.warp_forward);
5277   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5278   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5279   int old_jx = player->jx;
5280   int old_jy = player->jy;
5281   int old_element = Feld[old_jx][old_jy];
5282   int element = Feld[jx][jy];
5283   boolean player_relocated = (old_jx != jx || old_jy != jy);
5284
5285   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5286   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5287   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5288   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5289   int leave_side_horiz = move_dir_horiz;
5290   int leave_side_vert  = move_dir_vert;
5291   int enter_side = enter_side_horiz | enter_side_vert;
5292   int leave_side = leave_side_horiz | leave_side_vert;
5293
5294   if (player->GameOver)         /* do not reanimate dead player */
5295     return;
5296
5297   if (!player_relocated)        /* no need to relocate the player */
5298     return;
5299
5300   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5301   {
5302     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5303     DrawLevelField(jx, jy);
5304   }
5305
5306   if (player->present)
5307   {
5308     while (player->MovPos)
5309     {
5310       ScrollPlayer(player, SCROLL_GO_ON);
5311       ScrollScreen(NULL, SCROLL_GO_ON);
5312
5313       AdvanceFrameAndPlayerCounters(player->index_nr);
5314
5315       DrawPlayer(player);
5316
5317       BackToFront_WithFrameDelay(wait_delay_value);
5318     }
5319
5320     DrawPlayer(player);         /* needed here only to cleanup last field */
5321     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5322
5323     player->is_moving = FALSE;
5324   }
5325
5326   if (IS_CUSTOM_ELEMENT(old_element))
5327     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5328                                CE_LEFT_BY_PLAYER,
5329                                player->index_bit, leave_side);
5330
5331   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5332                                       CE_PLAYER_LEAVES_X,
5333                                       player->index_bit, leave_side);
5334
5335   Feld[jx][jy] = el_player;
5336   InitPlayerField(jx, jy, el_player, TRUE);
5337
5338   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5339      possible that the relocation target field did not contain a player element,
5340      but a walkable element, to which the new player was relocated -- in this
5341      case, restore that (already initialized!) element on the player field */
5342   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5343   {
5344     Feld[jx][jy] = element;     /* restore previously existing element */
5345   }
5346
5347   /* only visually relocate centered player */
5348   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5349                      FALSE, level.instant_relocation);
5350
5351   TestIfPlayerTouchesBadThing(jx, jy);
5352   TestIfPlayerTouchesCustomElement(jx, jy);
5353
5354   if (IS_CUSTOM_ELEMENT(element))
5355     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5356                                player->index_bit, enter_side);
5357
5358   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5359                                       player->index_bit, enter_side);
5360
5361   if (player->is_switching)
5362   {
5363     /* ensure that relocation while still switching an element does not cause
5364        a new element to be treated as also switched directly after relocation
5365        (this is important for teleporter switches that teleport the player to
5366        a place where another teleporter switch is in the same direction, which
5367        would then incorrectly be treated as immediately switched before the
5368        direction key that caused the switch was released) */
5369
5370     player->switch_x += jx - old_jx;
5371     player->switch_y += jy - old_jy;
5372   }
5373 }
5374
5375 void Explode(int ex, int ey, int phase, int mode)
5376 {
5377   int x, y;
5378   int last_phase;
5379   int border_element;
5380
5381   /* !!! eliminate this variable !!! */
5382   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5383
5384   if (game.explosions_delayed)
5385   {
5386     ExplodeField[ex][ey] = mode;
5387     return;
5388   }
5389
5390   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5391   {
5392     int center_element = Feld[ex][ey];
5393     int artwork_element, explosion_element;     /* set these values later */
5394
5395     /* remove things displayed in background while burning dynamite */
5396     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5397       Back[ex][ey] = 0;
5398
5399     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5400     {
5401       /* put moving element to center field (and let it explode there) */
5402       center_element = MovingOrBlocked2Element(ex, ey);
5403       RemoveMovingField(ex, ey);
5404       Feld[ex][ey] = center_element;
5405     }
5406
5407     /* now "center_element" is finally determined -- set related values now */
5408     artwork_element = center_element;           /* for custom player artwork */
5409     explosion_element = center_element;         /* for custom player artwork */
5410
5411     if (IS_PLAYER(ex, ey))
5412     {
5413       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5414
5415       artwork_element = stored_player[player_nr].artwork_element;
5416
5417       if (level.use_explosion_element[player_nr])
5418       {
5419         explosion_element = level.explosion_element[player_nr];
5420         artwork_element = explosion_element;
5421       }
5422     }
5423
5424     if (mode == EX_TYPE_NORMAL ||
5425         mode == EX_TYPE_CENTER ||
5426         mode == EX_TYPE_CROSS)
5427       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5428
5429     last_phase = element_info[explosion_element].explosion_delay + 1;
5430
5431     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5432     {
5433       int xx = x - ex + 1;
5434       int yy = y - ey + 1;
5435       int element;
5436
5437       if (!IN_LEV_FIELD(x, y) ||
5438           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5439           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5440         continue;
5441
5442       element = Feld[x][y];
5443
5444       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5445       {
5446         element = MovingOrBlocked2Element(x, y);
5447
5448         if (!IS_EXPLOSION_PROOF(element))
5449           RemoveMovingField(x, y);
5450       }
5451
5452       /* indestructible elements can only explode in center (but not flames) */
5453       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5454                                            mode == EX_TYPE_BORDER)) ||
5455           element == EL_FLAMES)
5456         continue;
5457
5458       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5459          behaviour, for example when touching a yamyam that explodes to rocks
5460          with active deadly shield, a rock is created under the player !!! */
5461       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5462 #if 0
5463       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5464           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5465            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5466 #else
5467       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5468 #endif
5469       {
5470         if (IS_ACTIVE_BOMB(element))
5471         {
5472           /* re-activate things under the bomb like gate or penguin */
5473           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5474           Back[x][y] = 0;
5475         }
5476
5477         continue;
5478       }
5479
5480       /* save walkable background elements while explosion on same tile */
5481       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5482           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5483         Back[x][y] = element;
5484
5485       /* ignite explodable elements reached by other explosion */
5486       if (element == EL_EXPLOSION)
5487         element = Store2[x][y];
5488
5489       if (AmoebaNr[x][y] &&
5490           (element == EL_AMOEBA_FULL ||
5491            element == EL_BD_AMOEBA ||
5492            element == EL_AMOEBA_GROWING))
5493       {
5494         AmoebaCnt[AmoebaNr[x][y]]--;
5495         AmoebaCnt2[AmoebaNr[x][y]]--;
5496       }
5497
5498       RemoveField(x, y);
5499
5500       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5501       {
5502         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5503
5504         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5505
5506         if (PLAYERINFO(ex, ey)->use_murphy)
5507           Store[x][y] = EL_EMPTY;
5508       }
5509
5510       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5511          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5512       else if (ELEM_IS_PLAYER(center_element))
5513         Store[x][y] = EL_EMPTY;
5514       else if (center_element == EL_YAMYAM)
5515         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5516       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5517         Store[x][y] = element_info[center_element].content.e[xx][yy];
5518 #if 1
5519       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5520          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5521          otherwise) -- FIX THIS !!! */
5522       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5523         Store[x][y] = element_info[element].content.e[1][1];
5524 #else
5525       else if (!CAN_EXPLODE(element))
5526         Store[x][y] = element_info[element].content.e[1][1];
5527 #endif
5528       else
5529         Store[x][y] = EL_EMPTY;
5530
5531       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5532           center_element == EL_AMOEBA_TO_DIAMOND)
5533         Store2[x][y] = element;
5534
5535       Feld[x][y] = EL_EXPLOSION;
5536       GfxElement[x][y] = artwork_element;
5537
5538       ExplodePhase[x][y] = 1;
5539       ExplodeDelay[x][y] = last_phase;
5540
5541       Stop[x][y] = TRUE;
5542     }
5543
5544     if (center_element == EL_YAMYAM)
5545       game.yamyam_content_nr =
5546         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5547
5548     return;
5549   }
5550
5551   if (Stop[ex][ey])
5552     return;
5553
5554   x = ex;
5555   y = ey;
5556
5557   if (phase == 1)
5558     GfxFrame[x][y] = 0;         /* restart explosion animation */
5559
5560   last_phase = ExplodeDelay[x][y];
5561
5562   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5563
5564   /* this can happen if the player leaves an explosion just in time */
5565   if (GfxElement[x][y] == EL_UNDEFINED)
5566     GfxElement[x][y] = EL_EMPTY;
5567
5568   border_element = Store2[x][y];
5569   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5570     border_element = StorePlayer[x][y];
5571
5572   if (phase == element_info[border_element].ignition_delay ||
5573       phase == last_phase)
5574   {
5575     boolean border_explosion = FALSE;
5576
5577     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5578         !PLAYER_EXPLOSION_PROTECTED(x, y))
5579     {
5580       KillPlayerUnlessExplosionProtected(x, y);
5581       border_explosion = TRUE;
5582     }
5583     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5584     {
5585       Feld[x][y] = Store2[x][y];
5586       Store2[x][y] = 0;
5587       Bang(x, y);
5588       border_explosion = TRUE;
5589     }
5590     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5591     {
5592       AmoebeUmwandeln(x, y);
5593       Store2[x][y] = 0;
5594       border_explosion = TRUE;
5595     }
5596
5597     /* if an element just explodes due to another explosion (chain-reaction),
5598        do not immediately end the new explosion when it was the last frame of
5599        the explosion (as it would be done in the following "if"-statement!) */
5600     if (border_explosion && phase == last_phase)
5601       return;
5602   }
5603
5604   if (phase == last_phase)
5605   {
5606     int element;
5607
5608     element = Feld[x][y] = Store[x][y];
5609     Store[x][y] = Store2[x][y] = 0;
5610     GfxElement[x][y] = EL_UNDEFINED;
5611
5612     /* player can escape from explosions and might therefore be still alive */
5613     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5614         element <= EL_PLAYER_IS_EXPLODING_4)
5615     {
5616       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5617       int explosion_element = EL_PLAYER_1 + player_nr;
5618       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5619       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5620
5621       if (level.use_explosion_element[player_nr])
5622         explosion_element = level.explosion_element[player_nr];
5623
5624       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5625                     element_info[explosion_element].content.e[xx][yy]);
5626     }
5627
5628     /* restore probably existing indestructible background element */
5629     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5630       element = Feld[x][y] = Back[x][y];
5631     Back[x][y] = 0;
5632
5633     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5634     GfxDir[x][y] = MV_NONE;
5635     ChangeDelay[x][y] = 0;
5636     ChangePage[x][y] = -1;
5637
5638     CustomValue[x][y] = 0;
5639
5640     InitField_WithBug2(x, y, FALSE);
5641
5642     TEST_DrawLevelField(x, y);
5643
5644     TestIfElementTouchesCustomElement(x, y);
5645
5646     if (GFX_CRUMBLED(element))
5647       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5648
5649     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5650       StorePlayer[x][y] = 0;
5651
5652     if (ELEM_IS_PLAYER(element))
5653       RelocatePlayer(x, y, element);
5654   }
5655   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5656   {
5657     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5658     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5659
5660     if (phase == delay)
5661       TEST_DrawLevelFieldCrumbled(x, y);
5662
5663     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5664     {
5665       DrawLevelElement(x, y, Back[x][y]);
5666       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5667     }
5668     else if (IS_WALKABLE_UNDER(Back[x][y]))
5669     {
5670       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5671       DrawLevelElementThruMask(x, y, Back[x][y]);
5672     }
5673     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5674       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5675   }
5676 }
5677
5678 void DynaExplode(int ex, int ey)
5679 {
5680   int i, j;
5681   int dynabomb_element = Feld[ex][ey];
5682   int dynabomb_size = 1;
5683   boolean dynabomb_xl = FALSE;
5684   struct PlayerInfo *player;
5685   static int xy[4][2] =
5686   {
5687     { 0, -1 },
5688     { -1, 0 },
5689     { +1, 0 },
5690     { 0, +1 }
5691   };
5692
5693   if (IS_ACTIVE_BOMB(dynabomb_element))
5694   {
5695     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5696     dynabomb_size = player->dynabomb_size;
5697     dynabomb_xl = player->dynabomb_xl;
5698     player->dynabombs_left++;
5699   }
5700
5701   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5702
5703   for (i = 0; i < NUM_DIRECTIONS; i++)
5704   {
5705     for (j = 1; j <= dynabomb_size; j++)
5706     {
5707       int x = ex + j * xy[i][0];
5708       int y = ey + j * xy[i][1];
5709       int element;
5710
5711       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5712         break;
5713
5714       element = Feld[x][y];
5715
5716       /* do not restart explosions of fields with active bombs */
5717       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5718         continue;
5719
5720       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5721
5722       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5723           !IS_DIGGABLE(element) && !dynabomb_xl)
5724         break;
5725     }
5726   }
5727 }
5728
5729 void Bang(int x, int y)
5730 {
5731   int element = MovingOrBlocked2Element(x, y);
5732   int explosion_type = EX_TYPE_NORMAL;
5733
5734   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5735   {
5736     struct PlayerInfo *player = PLAYERINFO(x, y);
5737
5738     element = Feld[x][y] = player->initial_element;
5739
5740     if (level.use_explosion_element[player->index_nr])
5741     {
5742       int explosion_element = level.explosion_element[player->index_nr];
5743
5744       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5745         explosion_type = EX_TYPE_CROSS;
5746       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5747         explosion_type = EX_TYPE_CENTER;
5748     }
5749   }
5750
5751   switch (element)
5752   {
5753     case EL_BUG:
5754     case EL_SPACESHIP:
5755     case EL_BD_BUTTERFLY:
5756     case EL_BD_FIREFLY:
5757     case EL_YAMYAM:
5758     case EL_DARK_YAMYAM:
5759     case EL_ROBOT:
5760     case EL_PACMAN:
5761     case EL_MOLE:
5762       RaiseScoreElement(element);
5763       break;
5764
5765     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5766     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5767     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5768     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5769     case EL_DYNABOMB_INCREASE_NUMBER:
5770     case EL_DYNABOMB_INCREASE_SIZE:
5771     case EL_DYNABOMB_INCREASE_POWER:
5772       explosion_type = EX_TYPE_DYNA;
5773       break;
5774
5775     case EL_DC_LANDMINE:
5776       explosion_type = EX_TYPE_CENTER;
5777       break;
5778
5779     case EL_PENGUIN:
5780     case EL_LAMP:
5781     case EL_LAMP_ACTIVE:
5782     case EL_AMOEBA_TO_DIAMOND:
5783       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5784         explosion_type = EX_TYPE_CENTER;
5785       break;
5786
5787     default:
5788       if (element_info[element].explosion_type == EXPLODES_CROSS)
5789         explosion_type = EX_TYPE_CROSS;
5790       else if (element_info[element].explosion_type == EXPLODES_1X1)
5791         explosion_type = EX_TYPE_CENTER;
5792       break;
5793   }
5794
5795   if (explosion_type == EX_TYPE_DYNA)
5796     DynaExplode(x, y);
5797   else
5798     Explode(x, y, EX_PHASE_START, explosion_type);
5799
5800   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5801 }
5802
5803 void SplashAcid(int x, int y)
5804 {
5805   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5806       (!IN_LEV_FIELD(x - 1, y - 2) ||
5807        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5808     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5809
5810   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5811       (!IN_LEV_FIELD(x + 1, y - 2) ||
5812        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5813     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5814
5815   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5816 }
5817
5818 static void InitBeltMovement()
5819 {
5820   static int belt_base_element[4] =
5821   {
5822     EL_CONVEYOR_BELT_1_LEFT,
5823     EL_CONVEYOR_BELT_2_LEFT,
5824     EL_CONVEYOR_BELT_3_LEFT,
5825     EL_CONVEYOR_BELT_4_LEFT
5826   };
5827   static int belt_base_active_element[4] =
5828   {
5829     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5830     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5831     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5832     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5833   };
5834
5835   int x, y, i, j;
5836
5837   /* set frame order for belt animation graphic according to belt direction */
5838   for (i = 0; i < NUM_BELTS; i++)
5839   {
5840     int belt_nr = i;
5841
5842     for (j = 0; j < NUM_BELT_PARTS; j++)
5843     {
5844       int element = belt_base_active_element[belt_nr] + j;
5845       int graphic_1 = el2img(element);
5846       int graphic_2 = el2panelimg(element);
5847
5848       if (game.belt_dir[i] == MV_LEFT)
5849       {
5850         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5851         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5852       }
5853       else
5854       {
5855         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5856         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5857       }
5858     }
5859   }
5860
5861   SCAN_PLAYFIELD(x, y)
5862   {
5863     int element = Feld[x][y];
5864
5865     for (i = 0; i < NUM_BELTS; i++)
5866     {
5867       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5868       {
5869         int e_belt_nr = getBeltNrFromBeltElement(element);
5870         int belt_nr = i;
5871
5872         if (e_belt_nr == belt_nr)
5873         {
5874           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5875
5876           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5877         }
5878       }
5879     }
5880   }
5881 }
5882
5883 static void ToggleBeltSwitch(int x, int y)
5884 {
5885   static int belt_base_element[4] =
5886   {
5887     EL_CONVEYOR_BELT_1_LEFT,
5888     EL_CONVEYOR_BELT_2_LEFT,
5889     EL_CONVEYOR_BELT_3_LEFT,
5890     EL_CONVEYOR_BELT_4_LEFT
5891   };
5892   static int belt_base_active_element[4] =
5893   {
5894     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5895     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5896     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5897     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5898   };
5899   static int belt_base_switch_element[4] =
5900   {
5901     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5902     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5903     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5904     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5905   };
5906   static int belt_move_dir[4] =
5907   {
5908     MV_LEFT,
5909     MV_NONE,
5910     MV_RIGHT,
5911     MV_NONE,
5912   };
5913
5914   int element = Feld[x][y];
5915   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5916   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5917   int belt_dir = belt_move_dir[belt_dir_nr];
5918   int xx, yy, i;
5919
5920   if (!IS_BELT_SWITCH(element))
5921     return;
5922
5923   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5924   game.belt_dir[belt_nr] = belt_dir;
5925
5926   if (belt_dir_nr == 3)
5927     belt_dir_nr = 1;
5928
5929   /* set frame order for belt animation graphic according to belt direction */
5930   for (i = 0; i < NUM_BELT_PARTS; i++)
5931   {
5932     int element = belt_base_active_element[belt_nr] + i;
5933     int graphic_1 = el2img(element);
5934     int graphic_2 = el2panelimg(element);
5935
5936     if (belt_dir == MV_LEFT)
5937     {
5938       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5939       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5940     }
5941     else
5942     {
5943       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5944       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5945     }
5946   }
5947
5948   SCAN_PLAYFIELD(xx, yy)
5949   {
5950     int element = Feld[xx][yy];
5951
5952     if (IS_BELT_SWITCH(element))
5953     {
5954       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5955
5956       if (e_belt_nr == belt_nr)
5957       {
5958         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5959         TEST_DrawLevelField(xx, yy);
5960       }
5961     }
5962     else if (IS_BELT(element) && belt_dir != MV_NONE)
5963     {
5964       int e_belt_nr = getBeltNrFromBeltElement(element);
5965
5966       if (e_belt_nr == belt_nr)
5967       {
5968         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5969
5970         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5971         TEST_DrawLevelField(xx, yy);
5972       }
5973     }
5974     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5975     {
5976       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5977
5978       if (e_belt_nr == belt_nr)
5979       {
5980         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5981
5982         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5983         TEST_DrawLevelField(xx, yy);
5984       }
5985     }
5986   }
5987 }
5988
5989 static void ToggleSwitchgateSwitch(int x, int y)
5990 {
5991   int xx, yy;
5992
5993   game.switchgate_pos = !game.switchgate_pos;
5994
5995   SCAN_PLAYFIELD(xx, yy)
5996   {
5997     int element = Feld[xx][yy];
5998
5999     if (element == EL_SWITCHGATE_SWITCH_UP)
6000     {
6001       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6002       TEST_DrawLevelField(xx, yy);
6003     }
6004     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6005     {
6006       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6007       TEST_DrawLevelField(xx, yy);
6008     }
6009     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6010     {
6011       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6012       TEST_DrawLevelField(xx, yy);
6013     }
6014     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6015     {
6016       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6017       TEST_DrawLevelField(xx, yy);
6018     }
6019     else if (element == EL_SWITCHGATE_OPEN ||
6020              element == EL_SWITCHGATE_OPENING)
6021     {
6022       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6023
6024       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6025     }
6026     else if (element == EL_SWITCHGATE_CLOSED ||
6027              element == EL_SWITCHGATE_CLOSING)
6028     {
6029       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6030
6031       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6032     }
6033   }
6034 }
6035
6036 static int getInvisibleActiveFromInvisibleElement(int element)
6037 {
6038   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6039           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6040           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6041           element);
6042 }
6043
6044 static int getInvisibleFromInvisibleActiveElement(int element)
6045 {
6046   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6047           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6048           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6049           element);
6050 }
6051
6052 static void RedrawAllLightSwitchesAndInvisibleElements()
6053 {
6054   int x, y;
6055
6056   SCAN_PLAYFIELD(x, y)
6057   {
6058     int element = Feld[x][y];
6059
6060     if (element == EL_LIGHT_SWITCH &&
6061         game.light_time_left > 0)
6062     {
6063       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6064       TEST_DrawLevelField(x, y);
6065     }
6066     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6067              game.light_time_left == 0)
6068     {
6069       Feld[x][y] = EL_LIGHT_SWITCH;
6070       TEST_DrawLevelField(x, y);
6071     }
6072     else if (element == EL_EMC_DRIPPER &&
6073              game.light_time_left > 0)
6074     {
6075       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6076       TEST_DrawLevelField(x, y);
6077     }
6078     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6079              game.light_time_left == 0)
6080     {
6081       Feld[x][y] = EL_EMC_DRIPPER;
6082       TEST_DrawLevelField(x, y);
6083     }
6084     else if (element == EL_INVISIBLE_STEELWALL ||
6085              element == EL_INVISIBLE_WALL ||
6086              element == EL_INVISIBLE_SAND)
6087     {
6088       if (game.light_time_left > 0)
6089         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6090
6091       TEST_DrawLevelField(x, y);
6092
6093       /* uncrumble neighbour fields, if needed */
6094       if (element == EL_INVISIBLE_SAND)
6095         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6096     }
6097     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6098              element == EL_INVISIBLE_WALL_ACTIVE ||
6099              element == EL_INVISIBLE_SAND_ACTIVE)
6100     {
6101       if (game.light_time_left == 0)
6102         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6103
6104       TEST_DrawLevelField(x, y);
6105
6106       /* re-crumble neighbour fields, if needed */
6107       if (element == EL_INVISIBLE_SAND)
6108         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6109     }
6110   }
6111 }
6112
6113 static void RedrawAllInvisibleElementsForLenses()
6114 {
6115   int x, y;
6116
6117   SCAN_PLAYFIELD(x, y)
6118   {
6119     int element = Feld[x][y];
6120
6121     if (element == EL_EMC_DRIPPER &&
6122         game.lenses_time_left > 0)
6123     {
6124       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6125       TEST_DrawLevelField(x, y);
6126     }
6127     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6128              game.lenses_time_left == 0)
6129     {
6130       Feld[x][y] = EL_EMC_DRIPPER;
6131       TEST_DrawLevelField(x, y);
6132     }
6133     else if (element == EL_INVISIBLE_STEELWALL ||
6134              element == EL_INVISIBLE_WALL ||
6135              element == EL_INVISIBLE_SAND)
6136     {
6137       if (game.lenses_time_left > 0)
6138         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6139
6140       TEST_DrawLevelField(x, y);
6141
6142       /* uncrumble neighbour fields, if needed */
6143       if (element == EL_INVISIBLE_SAND)
6144         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6145     }
6146     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6147              element == EL_INVISIBLE_WALL_ACTIVE ||
6148              element == EL_INVISIBLE_SAND_ACTIVE)
6149     {
6150       if (game.lenses_time_left == 0)
6151         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6152
6153       TEST_DrawLevelField(x, y);
6154
6155       /* re-crumble neighbour fields, if needed */
6156       if (element == EL_INVISIBLE_SAND)
6157         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6158     }
6159   }
6160 }
6161
6162 static void RedrawAllInvisibleElementsForMagnifier()
6163 {
6164   int x, y;
6165
6166   SCAN_PLAYFIELD(x, y)
6167   {
6168     int element = Feld[x][y];
6169
6170     if (element == EL_EMC_FAKE_GRASS &&
6171         game.magnify_time_left > 0)
6172     {
6173       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6174       TEST_DrawLevelField(x, y);
6175     }
6176     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6177              game.magnify_time_left == 0)
6178     {
6179       Feld[x][y] = EL_EMC_FAKE_GRASS;
6180       TEST_DrawLevelField(x, y);
6181     }
6182     else if (IS_GATE_GRAY(element) &&
6183              game.magnify_time_left > 0)
6184     {
6185       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6186                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6187                     IS_EM_GATE_GRAY(element) ?
6188                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6189                     IS_EMC_GATE_GRAY(element) ?
6190                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6191                     IS_DC_GATE_GRAY(element) ?
6192                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6193                     element);
6194       TEST_DrawLevelField(x, y);
6195     }
6196     else if (IS_GATE_GRAY_ACTIVE(element) &&
6197              game.magnify_time_left == 0)
6198     {
6199       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6200                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6201                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6202                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6203                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6204                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6205                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6206                     EL_DC_GATE_WHITE_GRAY :
6207                     element);
6208       TEST_DrawLevelField(x, y);
6209     }
6210   }
6211 }
6212
6213 static void ToggleLightSwitch(int x, int y)
6214 {
6215   int element = Feld[x][y];
6216
6217   game.light_time_left =
6218     (element == EL_LIGHT_SWITCH ?
6219      level.time_light * FRAMES_PER_SECOND : 0);
6220
6221   RedrawAllLightSwitchesAndInvisibleElements();
6222 }
6223
6224 static void ActivateTimegateSwitch(int x, int y)
6225 {
6226   int xx, yy;
6227
6228   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6229
6230   SCAN_PLAYFIELD(xx, yy)
6231   {
6232     int element = Feld[xx][yy];
6233
6234     if (element == EL_TIMEGATE_CLOSED ||
6235         element == EL_TIMEGATE_CLOSING)
6236     {
6237       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6238       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6239     }
6240
6241     /*
6242     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6243     {
6244       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6245       TEST_DrawLevelField(xx, yy);
6246     }
6247     */
6248
6249   }
6250
6251   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6252                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6253 }
6254
6255 void Impact(int x, int y)
6256 {
6257   boolean last_line = (y == lev_fieldy - 1);
6258   boolean object_hit = FALSE;
6259   boolean impact = (last_line || object_hit);
6260   int element = Feld[x][y];
6261   int smashed = EL_STEELWALL;
6262
6263   if (!last_line)       /* check if element below was hit */
6264   {
6265     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6266       return;
6267
6268     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6269                                          MovDir[x][y + 1] != MV_DOWN ||
6270                                          MovPos[x][y + 1] <= TILEY / 2));
6271
6272     /* do not smash moving elements that left the smashed field in time */
6273     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6274         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6275       object_hit = FALSE;
6276
6277 #if USE_QUICKSAND_IMPACT_BUGFIX
6278     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6279     {
6280       RemoveMovingField(x, y + 1);
6281       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6282       Feld[x][y + 2] = EL_ROCK;
6283       TEST_DrawLevelField(x, y + 2);
6284
6285       object_hit = TRUE;
6286     }
6287
6288     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6289     {
6290       RemoveMovingField(x, y + 1);
6291       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6292       Feld[x][y + 2] = EL_ROCK;
6293       TEST_DrawLevelField(x, y + 2);
6294
6295       object_hit = TRUE;
6296     }
6297 #endif
6298
6299     if (object_hit)
6300       smashed = MovingOrBlocked2Element(x, y + 1);
6301
6302     impact = (last_line || object_hit);
6303   }
6304
6305   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6306   {
6307     SplashAcid(x, y + 1);
6308     return;
6309   }
6310
6311   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6312   /* only reset graphic animation if graphic really changes after impact */
6313   if (impact &&
6314       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6315   {
6316     ResetGfxAnimation(x, y);
6317     TEST_DrawLevelField(x, y);
6318   }
6319
6320   if (impact && CAN_EXPLODE_IMPACT(element))
6321   {
6322     Bang(x, y);
6323     return;
6324   }
6325   else if (impact && element == EL_PEARL &&
6326            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6327   {
6328     ResetGfxAnimation(x, y);
6329
6330     Feld[x][y] = EL_PEARL_BREAKING;
6331     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6332     return;
6333   }
6334   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6335   {
6336     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6337
6338     return;
6339   }
6340
6341   if (impact && element == EL_AMOEBA_DROP)
6342   {
6343     if (object_hit && IS_PLAYER(x, y + 1))
6344       KillPlayerUnlessEnemyProtected(x, y + 1);
6345     else if (object_hit && smashed == EL_PENGUIN)
6346       Bang(x, y + 1);
6347     else
6348     {
6349       Feld[x][y] = EL_AMOEBA_GROWING;
6350       Store[x][y] = EL_AMOEBA_WET;
6351
6352       ResetRandomAnimationValue(x, y);
6353     }
6354     return;
6355   }
6356
6357   if (object_hit)               /* check which object was hit */
6358   {
6359     if ((CAN_PASS_MAGIC_WALL(element) && 
6360          (smashed == EL_MAGIC_WALL ||
6361           smashed == EL_BD_MAGIC_WALL)) ||
6362         (CAN_PASS_DC_MAGIC_WALL(element) &&
6363          smashed == EL_DC_MAGIC_WALL))
6364     {
6365       int xx, yy;
6366       int activated_magic_wall =
6367         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6368          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6369          EL_DC_MAGIC_WALL_ACTIVE);
6370
6371       /* activate magic wall / mill */
6372       SCAN_PLAYFIELD(xx, yy)
6373       {
6374         if (Feld[xx][yy] == smashed)
6375           Feld[xx][yy] = activated_magic_wall;
6376       }
6377
6378       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6379       game.magic_wall_active = TRUE;
6380
6381       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6382                             SND_MAGIC_WALL_ACTIVATING :
6383                             smashed == EL_BD_MAGIC_WALL ?
6384                             SND_BD_MAGIC_WALL_ACTIVATING :
6385                             SND_DC_MAGIC_WALL_ACTIVATING));
6386     }
6387
6388     if (IS_PLAYER(x, y + 1))
6389     {
6390       if (CAN_SMASH_PLAYER(element))
6391       {
6392         KillPlayerUnlessEnemyProtected(x, y + 1);
6393         return;
6394       }
6395     }
6396     else if (smashed == EL_PENGUIN)
6397     {
6398       if (CAN_SMASH_PLAYER(element))
6399       {
6400         Bang(x, y + 1);
6401         return;
6402       }
6403     }
6404     else if (element == EL_BD_DIAMOND)
6405     {
6406       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6407       {
6408         Bang(x, y + 1);
6409         return;
6410       }
6411     }
6412     else if (((element == EL_SP_INFOTRON ||
6413                element == EL_SP_ZONK) &&
6414               (smashed == EL_SP_SNIKSNAK ||
6415                smashed == EL_SP_ELECTRON ||
6416                smashed == EL_SP_DISK_ORANGE)) ||
6417              (element == EL_SP_INFOTRON &&
6418               smashed == EL_SP_DISK_YELLOW))
6419     {
6420       Bang(x, y + 1);
6421       return;
6422     }
6423     else if (CAN_SMASH_EVERYTHING(element))
6424     {
6425       if (IS_CLASSIC_ENEMY(smashed) ||
6426           CAN_EXPLODE_SMASHED(smashed))
6427       {
6428         Bang(x, y + 1);
6429         return;
6430       }
6431       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6432       {
6433         if (smashed == EL_LAMP ||
6434             smashed == EL_LAMP_ACTIVE)
6435         {
6436           Bang(x, y + 1);
6437           return;
6438         }
6439         else if (smashed == EL_NUT)
6440         {
6441           Feld[x][y + 1] = EL_NUT_BREAKING;
6442           PlayLevelSound(x, y, SND_NUT_BREAKING);
6443           RaiseScoreElement(EL_NUT);
6444           return;
6445         }
6446         else if (smashed == EL_PEARL)
6447         {
6448           ResetGfxAnimation(x, y);
6449
6450           Feld[x][y + 1] = EL_PEARL_BREAKING;
6451           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6452           return;
6453         }
6454         else if (smashed == EL_DIAMOND)
6455         {
6456           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6457           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6458           return;
6459         }
6460         else if (IS_BELT_SWITCH(smashed))
6461         {
6462           ToggleBeltSwitch(x, y + 1);
6463         }
6464         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6465                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6466                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6467                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6468         {
6469           ToggleSwitchgateSwitch(x, y + 1);
6470         }
6471         else if (smashed == EL_LIGHT_SWITCH ||
6472                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6473         {
6474           ToggleLightSwitch(x, y + 1);
6475         }
6476         else
6477         {
6478           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6479
6480           CheckElementChangeBySide(x, y + 1, smashed, element,
6481                                    CE_SWITCHED, CH_SIDE_TOP);
6482           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6483                                             CH_SIDE_TOP);
6484         }
6485       }
6486       else
6487       {
6488         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6489       }
6490     }
6491   }
6492
6493   /* play sound of magic wall / mill */
6494   if (!last_line &&
6495       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6496        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6497        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6498   {
6499     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6500       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6501     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6502       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6503     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6504       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6505
6506     return;
6507   }
6508
6509   /* play sound of object that hits the ground */
6510   if (last_line || object_hit)
6511     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6512 }
6513
6514 inline static void TurnRoundExt(int x, int y)
6515 {
6516   static struct
6517   {
6518     int dx, dy;
6519   } move_xy[] =
6520   {
6521     {  0,  0 },
6522     { -1,  0 },
6523     { +1,  0 },
6524     {  0,  0 },
6525     {  0, -1 },
6526     {  0,  0 }, { 0, 0 }, { 0, 0 },
6527     {  0, +1 }
6528   };
6529   static struct
6530   {
6531     int left, right, back;
6532   } turn[] =
6533   {
6534     { 0,        0,              0        },
6535     { MV_DOWN,  MV_UP,          MV_RIGHT },
6536     { MV_UP,    MV_DOWN,        MV_LEFT  },
6537     { 0,        0,              0        },
6538     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6539     { 0,        0,              0        },
6540     { 0,        0,              0        },
6541     { 0,        0,              0        },
6542     { MV_RIGHT, MV_LEFT,        MV_UP    }
6543   };
6544
6545   int element = Feld[x][y];
6546   int move_pattern = element_info[element].move_pattern;
6547
6548   int old_move_dir = MovDir[x][y];
6549   int left_dir  = turn[old_move_dir].left;
6550   int right_dir = turn[old_move_dir].right;
6551   int back_dir  = turn[old_move_dir].back;
6552
6553   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6554   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6555   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6556   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6557
6558   int left_x  = x + left_dx,  left_y  = y + left_dy;
6559   int right_x = x + right_dx, right_y = y + right_dy;
6560   int move_x  = x + move_dx,  move_y  = y + move_dy;
6561
6562   int xx, yy;
6563
6564   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6565   {
6566     TestIfBadThingTouchesOtherBadThing(x, y);
6567
6568     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6569       MovDir[x][y] = right_dir;
6570     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6571       MovDir[x][y] = left_dir;
6572
6573     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6574       MovDelay[x][y] = 9;
6575     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6576       MovDelay[x][y] = 1;
6577   }
6578   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6579   {
6580     TestIfBadThingTouchesOtherBadThing(x, y);
6581
6582     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6583       MovDir[x][y] = left_dir;
6584     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6585       MovDir[x][y] = right_dir;
6586
6587     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6588       MovDelay[x][y] = 9;
6589     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6590       MovDelay[x][y] = 1;
6591   }
6592   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6593   {
6594     TestIfBadThingTouchesOtherBadThing(x, y);
6595
6596     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6597       MovDir[x][y] = left_dir;
6598     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6599       MovDir[x][y] = right_dir;
6600
6601     if (MovDir[x][y] != old_move_dir)
6602       MovDelay[x][y] = 9;
6603   }
6604   else if (element == EL_YAMYAM)
6605   {
6606     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6607     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6608
6609     if (can_turn_left && can_turn_right)
6610       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6611     else if (can_turn_left)
6612       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6613     else if (can_turn_right)
6614       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6615     else
6616       MovDir[x][y] = back_dir;
6617
6618     MovDelay[x][y] = 16 + 16 * RND(3);
6619   }
6620   else if (element == EL_DARK_YAMYAM)
6621   {
6622     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6623                                                          left_x, left_y);
6624     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6625                                                          right_x, right_y);
6626
6627     if (can_turn_left && can_turn_right)
6628       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6629     else if (can_turn_left)
6630       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6631     else if (can_turn_right)
6632       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6633     else
6634       MovDir[x][y] = back_dir;
6635
6636     MovDelay[x][y] = 16 + 16 * RND(3);
6637   }
6638   else if (element == EL_PACMAN)
6639   {
6640     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6641     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6642
6643     if (can_turn_left && can_turn_right)
6644       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6645     else if (can_turn_left)
6646       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6647     else if (can_turn_right)
6648       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6649     else
6650       MovDir[x][y] = back_dir;
6651
6652     MovDelay[x][y] = 6 + RND(40);
6653   }
6654   else if (element == EL_PIG)
6655   {
6656     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6657     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6658     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6659     boolean should_turn_left, should_turn_right, should_move_on;
6660     int rnd_value = 24;
6661     int rnd = RND(rnd_value);
6662
6663     should_turn_left = (can_turn_left &&
6664                         (!can_move_on ||
6665                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6666                                                    y + back_dy + left_dy)));
6667     should_turn_right = (can_turn_right &&
6668                          (!can_move_on ||
6669                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6670                                                     y + back_dy + right_dy)));
6671     should_move_on = (can_move_on &&
6672                       (!can_turn_left ||
6673                        !can_turn_right ||
6674                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6675                                                  y + move_dy + left_dy) ||
6676                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6677                                                  y + move_dy + right_dy)));
6678
6679     if (should_turn_left || should_turn_right || should_move_on)
6680     {
6681       if (should_turn_left && should_turn_right && should_move_on)
6682         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6683                         rnd < 2 * rnd_value / 3 ? right_dir :
6684                         old_move_dir);
6685       else if (should_turn_left && should_turn_right)
6686         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6687       else if (should_turn_left && should_move_on)
6688         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6689       else if (should_turn_right && should_move_on)
6690         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6691       else if (should_turn_left)
6692         MovDir[x][y] = left_dir;
6693       else if (should_turn_right)
6694         MovDir[x][y] = right_dir;
6695       else if (should_move_on)
6696         MovDir[x][y] = old_move_dir;
6697     }
6698     else if (can_move_on && rnd > rnd_value / 8)
6699       MovDir[x][y] = old_move_dir;
6700     else if (can_turn_left && can_turn_right)
6701       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6702     else if (can_turn_left && rnd > rnd_value / 8)
6703       MovDir[x][y] = left_dir;
6704     else if (can_turn_right && rnd > rnd_value/8)
6705       MovDir[x][y] = right_dir;
6706     else
6707       MovDir[x][y] = back_dir;
6708
6709     xx = x + move_xy[MovDir[x][y]].dx;
6710     yy = y + move_xy[MovDir[x][y]].dy;
6711
6712     if (!IN_LEV_FIELD(xx, yy) ||
6713         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6714       MovDir[x][y] = old_move_dir;
6715
6716     MovDelay[x][y] = 0;
6717   }
6718   else if (element == EL_DRAGON)
6719   {
6720     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6721     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6722     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6723     int rnd_value = 24;
6724     int rnd = RND(rnd_value);
6725
6726     if (can_move_on && rnd > rnd_value / 8)
6727       MovDir[x][y] = old_move_dir;
6728     else if (can_turn_left && can_turn_right)
6729       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6730     else if (can_turn_left && rnd > rnd_value / 8)
6731       MovDir[x][y] = left_dir;
6732     else if (can_turn_right && rnd > rnd_value / 8)
6733       MovDir[x][y] = right_dir;
6734     else
6735       MovDir[x][y] = back_dir;
6736
6737     xx = x + move_xy[MovDir[x][y]].dx;
6738     yy = y + move_xy[MovDir[x][y]].dy;
6739
6740     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6741       MovDir[x][y] = old_move_dir;
6742
6743     MovDelay[x][y] = 0;
6744   }
6745   else if (element == EL_MOLE)
6746   {
6747     boolean can_move_on =
6748       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6749                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6750                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6751     if (!can_move_on)
6752     {
6753       boolean can_turn_left =
6754         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6755                               IS_AMOEBOID(Feld[left_x][left_y])));
6756
6757       boolean can_turn_right =
6758         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6759                               IS_AMOEBOID(Feld[right_x][right_y])));
6760
6761       if (can_turn_left && can_turn_right)
6762         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6763       else if (can_turn_left)
6764         MovDir[x][y] = left_dir;
6765       else
6766         MovDir[x][y] = right_dir;
6767     }
6768
6769     if (MovDir[x][y] != old_move_dir)
6770       MovDelay[x][y] = 9;
6771   }
6772   else if (element == EL_BALLOON)
6773   {
6774     MovDir[x][y] = game.wind_direction;
6775     MovDelay[x][y] = 0;
6776   }
6777   else if (element == EL_SPRING)
6778   {
6779     if (MovDir[x][y] & MV_HORIZONTAL)
6780     {
6781       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6782           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6783       {
6784         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6785         ResetGfxAnimation(move_x, move_y);
6786         TEST_DrawLevelField(move_x, move_y);
6787
6788         MovDir[x][y] = back_dir;
6789       }
6790       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6791                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6792         MovDir[x][y] = MV_NONE;
6793     }
6794
6795     MovDelay[x][y] = 0;
6796   }
6797   else if (element == EL_ROBOT ||
6798            element == EL_SATELLITE ||
6799            element == EL_PENGUIN ||
6800            element == EL_EMC_ANDROID)
6801   {
6802     int attr_x = -1, attr_y = -1;
6803
6804     if (AllPlayersGone)
6805     {
6806       attr_x = ExitX;
6807       attr_y = ExitY;
6808     }
6809     else
6810     {
6811       int i;
6812
6813       for (i = 0; i < MAX_PLAYERS; i++)
6814       {
6815         struct PlayerInfo *player = &stored_player[i];
6816         int jx = player->jx, jy = player->jy;
6817
6818         if (!player->active)
6819           continue;
6820
6821         if (attr_x == -1 ||
6822             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6823         {
6824           attr_x = jx;
6825           attr_y = jy;
6826         }
6827       }
6828     }
6829
6830     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6831         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6832          game.engine_version < VERSION_IDENT(3,1,0,0)))
6833     {
6834       attr_x = ZX;
6835       attr_y = ZY;
6836     }
6837
6838     if (element == EL_PENGUIN)
6839     {
6840       int i;
6841       static int xy[4][2] =
6842       {
6843         { 0, -1 },
6844         { -1, 0 },
6845         { +1, 0 },
6846         { 0, +1 }
6847       };
6848
6849       for (i = 0; i < NUM_DIRECTIONS; i++)
6850       {
6851         int ex = x + xy[i][0];
6852         int ey = y + xy[i][1];
6853
6854         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6855                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6856                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6857                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6858         {
6859           attr_x = ex;
6860           attr_y = ey;
6861           break;
6862         }
6863       }
6864     }
6865
6866     MovDir[x][y] = MV_NONE;
6867     if (attr_x < x)
6868       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6869     else if (attr_x > x)
6870       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6871     if (attr_y < y)
6872       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6873     else if (attr_y > y)
6874       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6875
6876     if (element == EL_ROBOT)
6877     {
6878       int newx, newy;
6879
6880       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6881         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6882       Moving2Blocked(x, y, &newx, &newy);
6883
6884       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6885         MovDelay[x][y] = 8 + 8 * !RND(3);
6886       else
6887         MovDelay[x][y] = 16;
6888     }
6889     else if (element == EL_PENGUIN)
6890     {
6891       int newx, newy;
6892
6893       MovDelay[x][y] = 1;
6894
6895       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6896       {
6897         boolean first_horiz = RND(2);
6898         int new_move_dir = MovDir[x][y];
6899
6900         MovDir[x][y] =
6901           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6902         Moving2Blocked(x, y, &newx, &newy);
6903
6904         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6905           return;
6906
6907         MovDir[x][y] =
6908           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6909         Moving2Blocked(x, y, &newx, &newy);
6910
6911         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6912           return;
6913
6914         MovDir[x][y] = old_move_dir;
6915         return;
6916       }
6917     }
6918     else if (element == EL_SATELLITE)
6919     {
6920       int newx, newy;
6921
6922       MovDelay[x][y] = 1;
6923
6924       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6925       {
6926         boolean first_horiz = RND(2);
6927         int new_move_dir = MovDir[x][y];
6928
6929         MovDir[x][y] =
6930           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6931         Moving2Blocked(x, y, &newx, &newy);
6932
6933         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6934           return;
6935
6936         MovDir[x][y] =
6937           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6938         Moving2Blocked(x, y, &newx, &newy);
6939
6940         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6941           return;
6942
6943         MovDir[x][y] = old_move_dir;
6944         return;
6945       }
6946     }
6947     else if (element == EL_EMC_ANDROID)
6948     {
6949       static int check_pos[16] =
6950       {
6951         -1,             /*  0 => (invalid)          */
6952         7,              /*  1 => MV_LEFT            */
6953         3,              /*  2 => MV_RIGHT           */
6954         -1,             /*  3 => (invalid)          */
6955         1,              /*  4 =>            MV_UP   */
6956         0,              /*  5 => MV_LEFT  | MV_UP   */
6957         2,              /*  6 => MV_RIGHT | MV_UP   */
6958         -1,             /*  7 => (invalid)          */
6959         5,              /*  8 =>            MV_DOWN */
6960         6,              /*  9 => MV_LEFT  | MV_DOWN */
6961         4,              /* 10 => MV_RIGHT | MV_DOWN */
6962         -1,             /* 11 => (invalid)          */
6963         -1,             /* 12 => (invalid)          */
6964         -1,             /* 13 => (invalid)          */
6965         -1,             /* 14 => (invalid)          */
6966         -1,             /* 15 => (invalid)          */
6967       };
6968       static struct
6969       {
6970         int dx, dy;
6971         int dir;
6972       } check_xy[8] =
6973       {
6974         { -1, -1,       MV_LEFT  | MV_UP   },
6975         {  0, -1,                  MV_UP   },
6976         { +1, -1,       MV_RIGHT | MV_UP   },
6977         { +1,  0,       MV_RIGHT           },
6978         { +1, +1,       MV_RIGHT | MV_DOWN },
6979         {  0, +1,                  MV_DOWN },
6980         { -1, +1,       MV_LEFT  | MV_DOWN },
6981         { -1,  0,       MV_LEFT            },
6982       };
6983       int start_pos, check_order;
6984       boolean can_clone = FALSE;
6985       int i;
6986
6987       /* check if there is any free field around current position */
6988       for (i = 0; i < 8; i++)
6989       {
6990         int newx = x + check_xy[i].dx;
6991         int newy = y + check_xy[i].dy;
6992
6993         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6994         {
6995           can_clone = TRUE;
6996
6997           break;
6998         }
6999       }
7000
7001       if (can_clone)            /* randomly find an element to clone */
7002       {
7003         can_clone = FALSE;
7004
7005         start_pos = check_pos[RND(8)];
7006         check_order = (RND(2) ? -1 : +1);
7007
7008         for (i = 0; i < 8; i++)
7009         {
7010           int pos_raw = start_pos + i * check_order;
7011           int pos = (pos_raw + 8) % 8;
7012           int newx = x + check_xy[pos].dx;
7013           int newy = y + check_xy[pos].dy;
7014
7015           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7016           {
7017             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7018             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7019
7020             Store[x][y] = Feld[newx][newy];
7021
7022             can_clone = TRUE;
7023
7024             break;
7025           }
7026         }
7027       }
7028
7029       if (can_clone)            /* randomly find a direction to move */
7030       {
7031         can_clone = FALSE;
7032
7033         start_pos = check_pos[RND(8)];
7034         check_order = (RND(2) ? -1 : +1);
7035
7036         for (i = 0; i < 8; i++)
7037         {
7038           int pos_raw = start_pos + i * check_order;
7039           int pos = (pos_raw + 8) % 8;
7040           int newx = x + check_xy[pos].dx;
7041           int newy = y + check_xy[pos].dy;
7042           int new_move_dir = check_xy[pos].dir;
7043
7044           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7045           {
7046             MovDir[x][y] = new_move_dir;
7047             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7048
7049             can_clone = TRUE;
7050
7051             break;
7052           }
7053         }
7054       }
7055
7056       if (can_clone)            /* cloning and moving successful */
7057         return;
7058
7059       /* cannot clone -- try to move towards player */
7060
7061       start_pos = check_pos[MovDir[x][y] & 0x0f];
7062       check_order = (RND(2) ? -1 : +1);
7063
7064       for (i = 0; i < 3; i++)
7065       {
7066         /* first check start_pos, then previous/next or (next/previous) pos */
7067         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7068         int pos = (pos_raw + 8) % 8;
7069         int newx = x + check_xy[pos].dx;
7070         int newy = y + check_xy[pos].dy;
7071         int new_move_dir = check_xy[pos].dir;
7072
7073         if (IS_PLAYER(newx, newy))
7074           break;
7075
7076         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7077         {
7078           MovDir[x][y] = new_move_dir;
7079           MovDelay[x][y] = level.android_move_time * 8 + 1;
7080
7081           break;
7082         }
7083       }
7084     }
7085   }
7086   else if (move_pattern == MV_TURNING_LEFT ||
7087            move_pattern == MV_TURNING_RIGHT ||
7088            move_pattern == MV_TURNING_LEFT_RIGHT ||
7089            move_pattern == MV_TURNING_RIGHT_LEFT ||
7090            move_pattern == MV_TURNING_RANDOM ||
7091            move_pattern == MV_ALL_DIRECTIONS)
7092   {
7093     boolean can_turn_left =
7094       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7095     boolean can_turn_right =
7096       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7097
7098     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7099       return;
7100
7101     if (move_pattern == MV_TURNING_LEFT)
7102       MovDir[x][y] = left_dir;
7103     else if (move_pattern == MV_TURNING_RIGHT)
7104       MovDir[x][y] = right_dir;
7105     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7106       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7107     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7108       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7109     else if (move_pattern == MV_TURNING_RANDOM)
7110       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7111                       can_turn_right && !can_turn_left ? right_dir :
7112                       RND(2) ? left_dir : right_dir);
7113     else if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7115     else if (can_turn_left)
7116       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7117     else if (can_turn_right)
7118       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7119     else
7120       MovDir[x][y] = back_dir;
7121
7122     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7123   }
7124   else if (move_pattern == MV_HORIZONTAL ||
7125            move_pattern == MV_VERTICAL)
7126   {
7127     if (move_pattern & old_move_dir)
7128       MovDir[x][y] = back_dir;
7129     else if (move_pattern == MV_HORIZONTAL)
7130       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7131     else if (move_pattern == MV_VERTICAL)
7132       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7133
7134     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7135   }
7136   else if (move_pattern & MV_ANY_DIRECTION)
7137   {
7138     MovDir[x][y] = move_pattern;
7139     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7140   }
7141   else if (move_pattern & MV_WIND_DIRECTION)
7142   {
7143     MovDir[x][y] = game.wind_direction;
7144     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7145   }
7146   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7147   {
7148     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7149       MovDir[x][y] = left_dir;
7150     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7151       MovDir[x][y] = right_dir;
7152
7153     if (MovDir[x][y] != old_move_dir)
7154       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7155   }
7156   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7157   {
7158     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7159       MovDir[x][y] = right_dir;
7160     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7161       MovDir[x][y] = left_dir;
7162
7163     if (MovDir[x][y] != old_move_dir)
7164       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7165   }
7166   else if (move_pattern == MV_TOWARDS_PLAYER ||
7167            move_pattern == MV_AWAY_FROM_PLAYER)
7168   {
7169     int attr_x = -1, attr_y = -1;
7170     int newx, newy;
7171     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7172
7173     if (AllPlayersGone)
7174     {
7175       attr_x = ExitX;
7176       attr_y = ExitY;
7177     }
7178     else
7179     {
7180       int i;
7181
7182       for (i = 0; i < MAX_PLAYERS; i++)
7183       {
7184         struct PlayerInfo *player = &stored_player[i];
7185         int jx = player->jx, jy = player->jy;
7186
7187         if (!player->active)
7188           continue;
7189
7190         if (attr_x == -1 ||
7191             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7192         {
7193           attr_x = jx;
7194           attr_y = jy;
7195         }
7196       }
7197     }
7198
7199     MovDir[x][y] = MV_NONE;
7200     if (attr_x < x)
7201       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7202     else if (attr_x > x)
7203       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7204     if (attr_y < y)
7205       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7206     else if (attr_y > y)
7207       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7208
7209     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7210
7211     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7212     {
7213       boolean first_horiz = RND(2);
7214       int new_move_dir = MovDir[x][y];
7215
7216       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7217       {
7218         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7219         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7220
7221         return;
7222       }
7223
7224       MovDir[x][y] =
7225         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7226       Moving2Blocked(x, y, &newx, &newy);
7227
7228       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7229         return;
7230
7231       MovDir[x][y] =
7232         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233       Moving2Blocked(x, y, &newx, &newy);
7234
7235       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7236         return;
7237
7238       MovDir[x][y] = old_move_dir;
7239     }
7240   }
7241   else if (move_pattern == MV_WHEN_PUSHED ||
7242            move_pattern == MV_WHEN_DROPPED)
7243   {
7244     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7245       MovDir[x][y] = MV_NONE;
7246
7247     MovDelay[x][y] = 0;
7248   }
7249   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7250   {
7251     static int test_xy[7][2] =
7252     {
7253       { 0, -1 },
7254       { -1, 0 },
7255       { +1, 0 },
7256       { 0, +1 },
7257       { 0, -1 },
7258       { -1, 0 },
7259       { +1, 0 },
7260     };
7261     static int test_dir[7] =
7262     {
7263       MV_UP,
7264       MV_LEFT,
7265       MV_RIGHT,
7266       MV_DOWN,
7267       MV_UP,
7268       MV_LEFT,
7269       MV_RIGHT,
7270     };
7271     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7272     int move_preference = -1000000;     /* start with very low preference */
7273     int new_move_dir = MV_NONE;
7274     int start_test = RND(4);
7275     int i;
7276
7277     for (i = 0; i < NUM_DIRECTIONS; i++)
7278     {
7279       int move_dir = test_dir[start_test + i];
7280       int move_dir_preference;
7281
7282       xx = x + test_xy[start_test + i][0];
7283       yy = y + test_xy[start_test + i][1];
7284
7285       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7286           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7287       {
7288         new_move_dir = move_dir;
7289
7290         break;
7291       }
7292
7293       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7294         continue;
7295
7296       move_dir_preference = -1 * RunnerVisit[xx][yy];
7297       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7298         move_dir_preference = PlayerVisit[xx][yy];
7299
7300       if (move_dir_preference > move_preference)
7301       {
7302         /* prefer field that has not been visited for the longest time */
7303         move_preference = move_dir_preference;
7304         new_move_dir = move_dir;
7305       }
7306       else if (move_dir_preference == move_preference &&
7307                move_dir == old_move_dir)
7308       {
7309         /* prefer last direction when all directions are preferred equally */
7310         move_preference = move_dir_preference;
7311         new_move_dir = move_dir;
7312       }
7313     }
7314
7315     MovDir[x][y] = new_move_dir;
7316     if (old_move_dir != new_move_dir)
7317       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7318   }
7319 }
7320
7321 static void TurnRound(int x, int y)
7322 {
7323   int direction = MovDir[x][y];
7324
7325   TurnRoundExt(x, y);
7326
7327   GfxDir[x][y] = MovDir[x][y];
7328
7329   if (direction != MovDir[x][y])
7330     GfxFrame[x][y] = 0;
7331
7332   if (MovDelay[x][y])
7333     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7334
7335   ResetGfxFrame(x, y);
7336 }
7337
7338 static boolean JustBeingPushed(int x, int y)
7339 {
7340   int i;
7341
7342   for (i = 0; i < MAX_PLAYERS; i++)
7343   {
7344     struct PlayerInfo *player = &stored_player[i];
7345
7346     if (player->active && player->is_pushing && player->MovPos)
7347     {
7348       int next_jx = player->jx + (player->jx - player->last_jx);
7349       int next_jy = player->jy + (player->jy - player->last_jy);
7350
7351       if (x == next_jx && y == next_jy)
7352         return TRUE;
7353     }
7354   }
7355
7356   return FALSE;
7357 }
7358
7359 void StartMoving(int x, int y)
7360 {
7361   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7362   int element = Feld[x][y];
7363
7364   if (Stop[x][y])
7365     return;
7366
7367   if (MovDelay[x][y] == 0)
7368     GfxAction[x][y] = ACTION_DEFAULT;
7369
7370   if (CAN_FALL(element) && y < lev_fieldy - 1)
7371   {
7372     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7373         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7374       if (JustBeingPushed(x, y))
7375         return;
7376
7377     if (element == EL_QUICKSAND_FULL)
7378     {
7379       if (IS_FREE(x, y + 1))
7380       {
7381         InitMovingField(x, y, MV_DOWN);
7382         started_moving = TRUE;
7383
7384         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7385 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7386         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7387           Store[x][y] = EL_ROCK;
7388 #else
7389         Store[x][y] = EL_ROCK;
7390 #endif
7391
7392         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7393       }
7394       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7395       {
7396         if (!MovDelay[x][y])
7397         {
7398           MovDelay[x][y] = TILEY + 1;
7399
7400           ResetGfxAnimation(x, y);
7401           ResetGfxAnimation(x, y + 1);
7402         }
7403
7404         if (MovDelay[x][y])
7405         {
7406           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7407           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7408
7409           MovDelay[x][y]--;
7410           if (MovDelay[x][y])
7411             return;
7412         }
7413
7414         Feld[x][y] = EL_QUICKSAND_EMPTY;
7415         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7416         Store[x][y + 1] = Store[x][y];
7417         Store[x][y] = 0;
7418
7419         PlayLevelSoundAction(x, y, ACTION_FILLING);
7420       }
7421       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7422       {
7423         if (!MovDelay[x][y])
7424         {
7425           MovDelay[x][y] = TILEY + 1;
7426
7427           ResetGfxAnimation(x, y);
7428           ResetGfxAnimation(x, y + 1);
7429         }
7430
7431         if (MovDelay[x][y])
7432         {
7433           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7434           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7435
7436           MovDelay[x][y]--;
7437           if (MovDelay[x][y])
7438             return;
7439         }
7440
7441         Feld[x][y] = EL_QUICKSAND_EMPTY;
7442         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7443         Store[x][y + 1] = Store[x][y];
7444         Store[x][y] = 0;
7445
7446         PlayLevelSoundAction(x, y, ACTION_FILLING);
7447       }
7448     }
7449     else if (element == EL_QUICKSAND_FAST_FULL)
7450     {
7451       if (IS_FREE(x, y + 1))
7452       {
7453         InitMovingField(x, y, MV_DOWN);
7454         started_moving = TRUE;
7455
7456         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7457 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7458         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7459           Store[x][y] = EL_ROCK;
7460 #else
7461         Store[x][y] = EL_ROCK;
7462 #endif
7463
7464         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7465       }
7466       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7467       {
7468         if (!MovDelay[x][y])
7469         {
7470           MovDelay[x][y] = TILEY + 1;
7471
7472           ResetGfxAnimation(x, y);
7473           ResetGfxAnimation(x, y + 1);
7474         }
7475
7476         if (MovDelay[x][y])
7477         {
7478           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7479           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7480
7481           MovDelay[x][y]--;
7482           if (MovDelay[x][y])
7483             return;
7484         }
7485
7486         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7487         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7488         Store[x][y + 1] = Store[x][y];
7489         Store[x][y] = 0;
7490
7491         PlayLevelSoundAction(x, y, ACTION_FILLING);
7492       }
7493       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7494       {
7495         if (!MovDelay[x][y])
7496         {
7497           MovDelay[x][y] = TILEY + 1;
7498
7499           ResetGfxAnimation(x, y);
7500           ResetGfxAnimation(x, y + 1);
7501         }
7502
7503         if (MovDelay[x][y])
7504         {
7505           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7506           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7507
7508           MovDelay[x][y]--;
7509           if (MovDelay[x][y])
7510             return;
7511         }
7512
7513         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7514         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7515         Store[x][y + 1] = Store[x][y];
7516         Store[x][y] = 0;
7517
7518         PlayLevelSoundAction(x, y, ACTION_FILLING);
7519       }
7520     }
7521     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7522              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7523     {
7524       InitMovingField(x, y, MV_DOWN);
7525       started_moving = TRUE;
7526
7527       Feld[x][y] = EL_QUICKSAND_FILLING;
7528       Store[x][y] = element;
7529
7530       PlayLevelSoundAction(x, y, ACTION_FILLING);
7531     }
7532     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7533              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7534     {
7535       InitMovingField(x, y, MV_DOWN);
7536       started_moving = TRUE;
7537
7538       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7539       Store[x][y] = element;
7540
7541       PlayLevelSoundAction(x, y, ACTION_FILLING);
7542     }
7543     else if (element == EL_MAGIC_WALL_FULL)
7544     {
7545       if (IS_FREE(x, y + 1))
7546       {
7547         InitMovingField(x, y, MV_DOWN);
7548         started_moving = TRUE;
7549
7550         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7551         Store[x][y] = EL_CHANGED(Store[x][y]);
7552       }
7553       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7554       {
7555         if (!MovDelay[x][y])
7556           MovDelay[x][y] = TILEY / 4 + 1;
7557
7558         if (MovDelay[x][y])
7559         {
7560           MovDelay[x][y]--;
7561           if (MovDelay[x][y])
7562             return;
7563         }
7564
7565         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7566         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7567         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7568         Store[x][y] = 0;
7569       }
7570     }
7571     else if (element == EL_BD_MAGIC_WALL_FULL)
7572     {
7573       if (IS_FREE(x, y + 1))
7574       {
7575         InitMovingField(x, y, MV_DOWN);
7576         started_moving = TRUE;
7577
7578         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7579         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7580       }
7581       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7582       {
7583         if (!MovDelay[x][y])
7584           MovDelay[x][y] = TILEY / 4 + 1;
7585
7586         if (MovDelay[x][y])
7587         {
7588           MovDelay[x][y]--;
7589           if (MovDelay[x][y])
7590             return;
7591         }
7592
7593         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7594         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7595         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7596         Store[x][y] = 0;
7597       }
7598     }
7599     else if (element == EL_DC_MAGIC_WALL_FULL)
7600     {
7601       if (IS_FREE(x, y + 1))
7602       {
7603         InitMovingField(x, y, MV_DOWN);
7604         started_moving = TRUE;
7605
7606         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7607         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7608       }
7609       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7610       {
7611         if (!MovDelay[x][y])
7612           MovDelay[x][y] = TILEY / 4 + 1;
7613
7614         if (MovDelay[x][y])
7615         {
7616           MovDelay[x][y]--;
7617           if (MovDelay[x][y])
7618             return;
7619         }
7620
7621         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7622         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7623         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7624         Store[x][y] = 0;
7625       }
7626     }
7627     else if ((CAN_PASS_MAGIC_WALL(element) &&
7628               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7629                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7630              (CAN_PASS_DC_MAGIC_WALL(element) &&
7631               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7632
7633     {
7634       InitMovingField(x, y, MV_DOWN);
7635       started_moving = TRUE;
7636
7637       Feld[x][y] =
7638         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7639          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7640          EL_DC_MAGIC_WALL_FILLING);
7641       Store[x][y] = element;
7642     }
7643     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7644     {
7645       SplashAcid(x, y + 1);
7646
7647       InitMovingField(x, y, MV_DOWN);
7648       started_moving = TRUE;
7649
7650       Store[x][y] = EL_ACID;
7651     }
7652     else if (
7653              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7654               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7655              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7656               CAN_FALL(element) && WasJustFalling[x][y] &&
7657               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7658
7659              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7660               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7661               (Feld[x][y + 1] == EL_BLOCKED)))
7662     {
7663       /* this is needed for a special case not covered by calling "Impact()"
7664          from "ContinueMoving()": if an element moves to a tile directly below
7665          another element which was just falling on that tile (which was empty
7666          in the previous frame), the falling element above would just stop
7667          instead of smashing the element below (in previous version, the above
7668          element was just checked for "moving" instead of "falling", resulting
7669          in incorrect smashes caused by horizontal movement of the above
7670          element; also, the case of the player being the element to smash was
7671          simply not covered here... :-/ ) */
7672
7673       CheckCollision[x][y] = 0;
7674       CheckImpact[x][y] = 0;
7675
7676       Impact(x, y);
7677     }
7678     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7679     {
7680       if (MovDir[x][y] == MV_NONE)
7681       {
7682         InitMovingField(x, y, MV_DOWN);
7683         started_moving = TRUE;
7684       }
7685     }
7686     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7687     {
7688       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7689         MovDir[x][y] = MV_DOWN;
7690
7691       InitMovingField(x, y, MV_DOWN);
7692       started_moving = TRUE;
7693     }
7694     else if (element == EL_AMOEBA_DROP)
7695     {
7696       Feld[x][y] = EL_AMOEBA_GROWING;
7697       Store[x][y] = EL_AMOEBA_WET;
7698     }
7699     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7700               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7701              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7702              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7703     {
7704       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7705                                 (IS_FREE(x - 1, y + 1) ||
7706                                  Feld[x - 1][y + 1] == EL_ACID));
7707       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7708                                 (IS_FREE(x + 1, y + 1) ||
7709                                  Feld[x + 1][y + 1] == EL_ACID));
7710       boolean can_fall_any  = (can_fall_left || can_fall_right);
7711       boolean can_fall_both = (can_fall_left && can_fall_right);
7712       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7713
7714       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7715       {
7716         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7717           can_fall_right = FALSE;
7718         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7719           can_fall_left = FALSE;
7720         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7721           can_fall_right = FALSE;
7722         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7723           can_fall_left = FALSE;
7724
7725         can_fall_any  = (can_fall_left || can_fall_right);
7726         can_fall_both = FALSE;
7727       }
7728
7729       if (can_fall_both)
7730       {
7731         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7732           can_fall_right = FALSE;       /* slip down on left side */
7733         else
7734           can_fall_left = !(can_fall_right = RND(2));
7735
7736         can_fall_both = FALSE;
7737       }
7738
7739       if (can_fall_any)
7740       {
7741         /* if not determined otherwise, prefer left side for slipping down */
7742         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7743         started_moving = TRUE;
7744       }
7745     }
7746     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7747     {
7748       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7749       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7750       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7751       int belt_dir = game.belt_dir[belt_nr];
7752
7753       if ((belt_dir == MV_LEFT  && left_is_free) ||
7754           (belt_dir == MV_RIGHT && right_is_free))
7755       {
7756         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7757
7758         InitMovingField(x, y, belt_dir);
7759         started_moving = TRUE;
7760
7761         Pushed[x][y] = TRUE;
7762         Pushed[nextx][y] = TRUE;
7763
7764         GfxAction[x][y] = ACTION_DEFAULT;
7765       }
7766       else
7767       {
7768         MovDir[x][y] = 0;       /* if element was moving, stop it */
7769       }
7770     }
7771   }
7772
7773   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7774   if (CAN_MOVE(element) && !started_moving)
7775   {
7776     int move_pattern = element_info[element].move_pattern;
7777     int newx, newy;
7778
7779     Moving2Blocked(x, y, &newx, &newy);
7780
7781     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7782       return;
7783
7784     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7785         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7786     {
7787       WasJustMoving[x][y] = 0;
7788       CheckCollision[x][y] = 0;
7789
7790       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7791
7792       if (Feld[x][y] != element)        /* element has changed */
7793         return;
7794     }
7795
7796     if (!MovDelay[x][y])        /* start new movement phase */
7797     {
7798       /* all objects that can change their move direction after each step
7799          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7800
7801       if (element != EL_YAMYAM &&
7802           element != EL_DARK_YAMYAM &&
7803           element != EL_PACMAN &&
7804           !(move_pattern & MV_ANY_DIRECTION) &&
7805           move_pattern != MV_TURNING_LEFT &&
7806           move_pattern != MV_TURNING_RIGHT &&
7807           move_pattern != MV_TURNING_LEFT_RIGHT &&
7808           move_pattern != MV_TURNING_RIGHT_LEFT &&
7809           move_pattern != MV_TURNING_RANDOM)
7810       {
7811         TurnRound(x, y);
7812
7813         if (MovDelay[x][y] && (element == EL_BUG ||
7814                                element == EL_SPACESHIP ||
7815                                element == EL_SP_SNIKSNAK ||
7816                                element == EL_SP_ELECTRON ||
7817                                element == EL_MOLE))
7818           TEST_DrawLevelField(x, y);
7819       }
7820     }
7821
7822     if (MovDelay[x][y])         /* wait some time before next movement */
7823     {
7824       MovDelay[x][y]--;
7825
7826       if (element == EL_ROBOT ||
7827           element == EL_YAMYAM ||
7828           element == EL_DARK_YAMYAM)
7829       {
7830         DrawLevelElementAnimationIfNeeded(x, y, element);
7831         PlayLevelSoundAction(x, y, ACTION_WAITING);
7832       }
7833       else if (element == EL_SP_ELECTRON)
7834         DrawLevelElementAnimationIfNeeded(x, y, element);
7835       else if (element == EL_DRAGON)
7836       {
7837         int i;
7838         int dir = MovDir[x][y];
7839         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7840         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7841         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7842                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7843                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7844                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7845         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7846
7847         GfxAction[x][y] = ACTION_ATTACKING;
7848
7849         if (IS_PLAYER(x, y))
7850           DrawPlayerField(x, y);
7851         else
7852           TEST_DrawLevelField(x, y);
7853
7854         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7855
7856         for (i = 1; i <= 3; i++)
7857         {
7858           int xx = x + i * dx;
7859           int yy = y + i * dy;
7860           int sx = SCREENX(xx);
7861           int sy = SCREENY(yy);
7862           int flame_graphic = graphic + (i - 1);
7863
7864           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7865             break;
7866
7867           if (MovDelay[x][y])
7868           {
7869             int flamed = MovingOrBlocked2Element(xx, yy);
7870
7871             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7872               Bang(xx, yy);
7873             else
7874               RemoveMovingField(xx, yy);
7875
7876             ChangeDelay[xx][yy] = 0;
7877
7878             Feld[xx][yy] = EL_FLAMES;
7879
7880             if (IN_SCR_FIELD(sx, sy))
7881             {
7882               TEST_DrawLevelFieldCrumbled(xx, yy);
7883               DrawGraphic(sx, sy, flame_graphic, frame);
7884             }
7885           }
7886           else
7887           {
7888             if (Feld[xx][yy] == EL_FLAMES)
7889               Feld[xx][yy] = EL_EMPTY;
7890             TEST_DrawLevelField(xx, yy);
7891           }
7892         }
7893       }
7894
7895       if (MovDelay[x][y])       /* element still has to wait some time */
7896       {
7897         PlayLevelSoundAction(x, y, ACTION_WAITING);
7898
7899         return;
7900       }
7901     }
7902
7903     /* now make next step */
7904
7905     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7906
7907     if (DONT_COLLIDE_WITH(element) &&
7908         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7909         !PLAYER_ENEMY_PROTECTED(newx, newy))
7910     {
7911       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7912
7913       return;
7914     }
7915
7916     else if (CAN_MOVE_INTO_ACID(element) &&
7917              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7918              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7919              (MovDir[x][y] == MV_DOWN ||
7920               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7921     {
7922       SplashAcid(newx, newy);
7923       Store[x][y] = EL_ACID;
7924     }
7925     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7926     {
7927       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7928           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7929           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7930           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7931       {
7932         RemoveField(x, y);
7933         TEST_DrawLevelField(x, y);
7934
7935         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7936         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7937           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7938
7939         local_player->friends_still_needed--;
7940         if (!local_player->friends_still_needed &&
7941             !local_player->GameOver && AllPlayersGone)
7942           PlayerWins(local_player);
7943
7944         return;
7945       }
7946       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7947       {
7948         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7949           TEST_DrawLevelField(newx, newy);
7950         else
7951           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7952       }
7953       else if (!IS_FREE(newx, newy))
7954       {
7955         GfxAction[x][y] = ACTION_WAITING;
7956
7957         if (IS_PLAYER(x, y))
7958           DrawPlayerField(x, y);
7959         else
7960           TEST_DrawLevelField(x, y);
7961
7962         return;
7963       }
7964     }
7965     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7966     {
7967       if (IS_FOOD_PIG(Feld[newx][newy]))
7968       {
7969         if (IS_MOVING(newx, newy))
7970           RemoveMovingField(newx, newy);
7971         else
7972         {
7973           Feld[newx][newy] = EL_EMPTY;
7974           TEST_DrawLevelField(newx, newy);
7975         }
7976
7977         PlayLevelSound(x, y, SND_PIG_DIGGING);
7978       }
7979       else if (!IS_FREE(newx, newy))
7980       {
7981         if (IS_PLAYER(x, y))
7982           DrawPlayerField(x, y);
7983         else
7984           TEST_DrawLevelField(x, y);
7985
7986         return;
7987       }
7988     }
7989     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7990     {
7991       if (Store[x][y] != EL_EMPTY)
7992       {
7993         boolean can_clone = FALSE;
7994         int xx, yy;
7995
7996         /* check if element to clone is still there */
7997         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7998         {
7999           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8000           {
8001             can_clone = TRUE;
8002
8003             break;
8004           }
8005         }
8006
8007         /* cannot clone or target field not free anymore -- do not clone */
8008         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8009           Store[x][y] = EL_EMPTY;
8010       }
8011
8012       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8013       {
8014         if (IS_MV_DIAGONAL(MovDir[x][y]))
8015         {
8016           int diagonal_move_dir = MovDir[x][y];
8017           int stored = Store[x][y];
8018           int change_delay = 8;
8019           int graphic;
8020
8021           /* android is moving diagonally */
8022
8023           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8024
8025           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8026           GfxElement[x][y] = EL_EMC_ANDROID;
8027           GfxAction[x][y] = ACTION_SHRINKING;
8028           GfxDir[x][y] = diagonal_move_dir;
8029           ChangeDelay[x][y] = change_delay;
8030
8031           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8032                                    GfxDir[x][y]);
8033
8034           DrawLevelGraphicAnimation(x, y, graphic);
8035           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8036
8037           if (Feld[newx][newy] == EL_ACID)
8038           {
8039             SplashAcid(newx, newy);
8040
8041             return;
8042           }
8043
8044           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8045
8046           Store[newx][newy] = EL_EMC_ANDROID;
8047           GfxElement[newx][newy] = EL_EMC_ANDROID;
8048           GfxAction[newx][newy] = ACTION_GROWING;
8049           GfxDir[newx][newy] = diagonal_move_dir;
8050           ChangeDelay[newx][newy] = change_delay;
8051
8052           graphic = el_act_dir2img(GfxElement[newx][newy],
8053                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8054
8055           DrawLevelGraphicAnimation(newx, newy, graphic);
8056           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8057
8058           return;
8059         }
8060         else
8061         {
8062           Feld[newx][newy] = EL_EMPTY;
8063           TEST_DrawLevelField(newx, newy);
8064
8065           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8066         }
8067       }
8068       else if (!IS_FREE(newx, newy))
8069       {
8070         return;
8071       }
8072     }
8073     else if (IS_CUSTOM_ELEMENT(element) &&
8074              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8075     {
8076       if (!DigFieldByCE(newx, newy, element))
8077         return;
8078
8079       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8080       {
8081         RunnerVisit[x][y] = FrameCounter;
8082         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8083       }
8084     }
8085     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8086     {
8087       if (!IS_FREE(newx, newy))
8088       {
8089         if (IS_PLAYER(x, y))
8090           DrawPlayerField(x, y);
8091         else
8092           TEST_DrawLevelField(x, y);
8093
8094         return;
8095       }
8096       else
8097       {
8098         boolean wanna_flame = !RND(10);
8099         int dx = newx - x, dy = newy - y;
8100         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8101         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8102         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8103                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8104         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8105                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8106
8107         if ((wanna_flame ||
8108              IS_CLASSIC_ENEMY(element1) ||
8109              IS_CLASSIC_ENEMY(element2)) &&
8110             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8111             element1 != EL_FLAMES && element2 != EL_FLAMES)
8112         {
8113           ResetGfxAnimation(x, y);
8114           GfxAction[x][y] = ACTION_ATTACKING;
8115
8116           if (IS_PLAYER(x, y))
8117             DrawPlayerField(x, y);
8118           else
8119             TEST_DrawLevelField(x, y);
8120
8121           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8122
8123           MovDelay[x][y] = 50;
8124
8125           Feld[newx][newy] = EL_FLAMES;
8126           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8127             Feld[newx1][newy1] = EL_FLAMES;
8128           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8129             Feld[newx2][newy2] = EL_FLAMES;
8130
8131           return;
8132         }
8133       }
8134     }
8135     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8136              Feld[newx][newy] == EL_DIAMOND)
8137     {
8138       if (IS_MOVING(newx, newy))
8139         RemoveMovingField(newx, newy);
8140       else
8141       {
8142         Feld[newx][newy] = EL_EMPTY;
8143         TEST_DrawLevelField(newx, newy);
8144       }
8145
8146       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8147     }
8148     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8149              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8150     {
8151       if (AmoebaNr[newx][newy])
8152       {
8153         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8154         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8155             Feld[newx][newy] == EL_BD_AMOEBA)
8156           AmoebaCnt[AmoebaNr[newx][newy]]--;
8157       }
8158
8159       if (IS_MOVING(newx, newy))
8160       {
8161         RemoveMovingField(newx, newy);
8162       }
8163       else
8164       {
8165         Feld[newx][newy] = EL_EMPTY;
8166         TEST_DrawLevelField(newx, newy);
8167       }
8168
8169       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8170     }
8171     else if ((element == EL_PACMAN || element == EL_MOLE)
8172              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8173     {
8174       if (AmoebaNr[newx][newy])
8175       {
8176         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8177         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8178             Feld[newx][newy] == EL_BD_AMOEBA)
8179           AmoebaCnt[AmoebaNr[newx][newy]]--;
8180       }
8181
8182       if (element == EL_MOLE)
8183       {
8184         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8185         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8186
8187         ResetGfxAnimation(x, y);
8188         GfxAction[x][y] = ACTION_DIGGING;
8189         TEST_DrawLevelField(x, y);
8190
8191         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8192
8193         return;                         /* wait for shrinking amoeba */
8194       }
8195       else      /* element == EL_PACMAN */
8196       {
8197         Feld[newx][newy] = EL_EMPTY;
8198         TEST_DrawLevelField(newx, newy);
8199         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8200       }
8201     }
8202     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8203              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8204               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8205     {
8206       /* wait for shrinking amoeba to completely disappear */
8207       return;
8208     }
8209     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8210     {
8211       /* object was running against a wall */
8212
8213       TurnRound(x, y);
8214
8215       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8216         DrawLevelElementAnimation(x, y, element);
8217
8218       if (DONT_TOUCH(element))
8219         TestIfBadThingTouchesPlayer(x, y);
8220
8221       return;
8222     }
8223
8224     InitMovingField(x, y, MovDir[x][y]);
8225
8226     PlayLevelSoundAction(x, y, ACTION_MOVING);
8227   }
8228
8229   if (MovDir[x][y])
8230     ContinueMoving(x, y);
8231 }
8232
8233 void ContinueMoving(int x, int y)
8234 {
8235   int element = Feld[x][y];
8236   struct ElementInfo *ei = &element_info[element];
8237   int direction = MovDir[x][y];
8238   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8239   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8240   int newx = x + dx, newy = y + dy;
8241   int stored = Store[x][y];
8242   int stored_new = Store[newx][newy];
8243   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8244   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8245   boolean last_line = (newy == lev_fieldy - 1);
8246
8247   MovPos[x][y] += getElementMoveStepsize(x, y);
8248
8249   if (pushed_by_player) /* special case: moving object pushed by player */
8250     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8251
8252   if (ABS(MovPos[x][y]) < TILEX)
8253   {
8254     TEST_DrawLevelField(x, y);
8255
8256     return;     /* element is still moving */
8257   }
8258
8259   /* element reached destination field */
8260
8261   Feld[x][y] = EL_EMPTY;
8262   Feld[newx][newy] = element;
8263   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8264
8265   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8266   {
8267     element = Feld[newx][newy] = EL_ACID;
8268   }
8269   else if (element == EL_MOLE)
8270   {
8271     Feld[x][y] = EL_SAND;
8272
8273     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8274   }
8275   else if (element == EL_QUICKSAND_FILLING)
8276   {
8277     element = Feld[newx][newy] = get_next_element(element);
8278     Store[newx][newy] = Store[x][y];
8279   }
8280   else if (element == EL_QUICKSAND_EMPTYING)
8281   {
8282     Feld[x][y] = get_next_element(element);
8283     element = Feld[newx][newy] = Store[x][y];
8284   }
8285   else if (element == EL_QUICKSAND_FAST_FILLING)
8286   {
8287     element = Feld[newx][newy] = get_next_element(element);
8288     Store[newx][newy] = Store[x][y];
8289   }
8290   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8291   {
8292     Feld[x][y] = get_next_element(element);
8293     element = Feld[newx][newy] = Store[x][y];
8294   }
8295   else if (element == EL_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_MAGIC_WALL_DEAD;
8300     Store[newx][newy] = Store[x][y];
8301   }
8302   else if (element == EL_MAGIC_WALL_EMPTYING)
8303   {
8304     Feld[x][y] = get_next_element(element);
8305     if (!game.magic_wall_active)
8306       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8307     element = Feld[newx][newy] = Store[x][y];
8308
8309     InitField(newx, newy, FALSE);
8310   }
8311   else if (element == EL_BD_MAGIC_WALL_FILLING)
8312   {
8313     element = Feld[newx][newy] = get_next_element(element);
8314     if (!game.magic_wall_active)
8315       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8316     Store[newx][newy] = Store[x][y];
8317   }
8318   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8319   {
8320     Feld[x][y] = get_next_element(element);
8321     if (!game.magic_wall_active)
8322       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8323     element = Feld[newx][newy] = Store[x][y];
8324
8325     InitField(newx, newy, FALSE);
8326   }
8327   else if (element == EL_DC_MAGIC_WALL_FILLING)
8328   {
8329     element = Feld[newx][newy] = get_next_element(element);
8330     if (!game.magic_wall_active)
8331       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8332     Store[newx][newy] = Store[x][y];
8333   }
8334   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8335   {
8336     Feld[x][y] = get_next_element(element);
8337     if (!game.magic_wall_active)
8338       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8339     element = Feld[newx][newy] = Store[x][y];
8340
8341     InitField(newx, newy, FALSE);
8342   }
8343   else if (element == EL_AMOEBA_DROPPING)
8344   {
8345     Feld[x][y] = get_next_element(element);
8346     element = Feld[newx][newy] = Store[x][y];
8347   }
8348   else if (element == EL_SOKOBAN_OBJECT)
8349   {
8350     if (Back[x][y])
8351       Feld[x][y] = Back[x][y];
8352
8353     if (Back[newx][newy])
8354       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8355
8356     Back[x][y] = Back[newx][newy] = 0;
8357   }
8358
8359   Store[x][y] = EL_EMPTY;
8360   MovPos[x][y] = 0;
8361   MovDir[x][y] = 0;
8362   MovDelay[x][y] = 0;
8363
8364   MovDelay[newx][newy] = 0;
8365
8366   if (CAN_CHANGE_OR_HAS_ACTION(element))
8367   {
8368     /* copy element change control values to new field */
8369     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8370     ChangePage[newx][newy]  = ChangePage[x][y];
8371     ChangeCount[newx][newy] = ChangeCount[x][y];
8372     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8373   }
8374
8375   CustomValue[newx][newy] = CustomValue[x][y];
8376
8377   ChangeDelay[x][y] = 0;
8378   ChangePage[x][y] = -1;
8379   ChangeCount[x][y] = 0;
8380   ChangeEvent[x][y] = -1;
8381
8382   CustomValue[x][y] = 0;
8383
8384   /* copy animation control values to new field */
8385   GfxFrame[newx][newy]  = GfxFrame[x][y];
8386   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8387   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8388   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8389
8390   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8391
8392   /* some elements can leave other elements behind after moving */
8393   if (ei->move_leave_element != EL_EMPTY &&
8394       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8395       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8396   {
8397     int move_leave_element = ei->move_leave_element;
8398
8399     /* this makes it possible to leave the removed element again */
8400     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8401       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8402
8403     Feld[x][y] = move_leave_element;
8404
8405     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8406       MovDir[x][y] = direction;
8407
8408     InitField(x, y, FALSE);
8409
8410     if (GFX_CRUMBLED(Feld[x][y]))
8411       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8412
8413     if (ELEM_IS_PLAYER(move_leave_element))
8414       RelocatePlayer(x, y, move_leave_element);
8415   }
8416
8417   /* do this after checking for left-behind element */
8418   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8419
8420   if (!CAN_MOVE(element) ||
8421       (CAN_FALL(element) && direction == MV_DOWN &&
8422        (element == EL_SPRING ||
8423         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8424         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8425     GfxDir[x][y] = MovDir[newx][newy] = 0;
8426
8427   TEST_DrawLevelField(x, y);
8428   TEST_DrawLevelField(newx, newy);
8429
8430   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8431
8432   /* prevent pushed element from moving on in pushed direction */
8433   if (pushed_by_player && CAN_MOVE(element) &&
8434       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8435       !(element_info[element].move_pattern & direction))
8436     TurnRound(newx, newy);
8437
8438   /* prevent elements on conveyor belt from moving on in last direction */
8439   if (pushed_by_conveyor && CAN_FALL(element) &&
8440       direction & MV_HORIZONTAL)
8441     MovDir[newx][newy] = 0;
8442
8443   if (!pushed_by_player)
8444   {
8445     int nextx = newx + dx, nexty = newy + dy;
8446     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8447
8448     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8449
8450     if (CAN_FALL(element) && direction == MV_DOWN)
8451       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8452
8453     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8454       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8455
8456     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8457       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8458   }
8459
8460   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8461   {
8462     TestIfBadThingTouchesPlayer(newx, newy);
8463     TestIfBadThingTouchesFriend(newx, newy);
8464
8465     if (!IS_CUSTOM_ELEMENT(element))
8466       TestIfBadThingTouchesOtherBadThing(newx, newy);
8467   }
8468   else if (element == EL_PENGUIN)
8469     TestIfFriendTouchesBadThing(newx, newy);
8470
8471   if (DONT_GET_HIT_BY(element))
8472   {
8473     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8474   }
8475
8476   /* give the player one last chance (one more frame) to move away */
8477   if (CAN_FALL(element) && direction == MV_DOWN &&
8478       (last_line || (!IS_FREE(x, newy + 1) &&
8479                      (!IS_PLAYER(x, newy + 1) ||
8480                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8481     Impact(x, newy);
8482
8483   if (pushed_by_player && !game.use_change_when_pushing_bug)
8484   {
8485     int push_side = MV_DIR_OPPOSITE(direction);
8486     struct PlayerInfo *player = PLAYERINFO(x, y);
8487
8488     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8489                                player->index_bit, push_side);
8490     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8491                                         player->index_bit, push_side);
8492   }
8493
8494   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8495     MovDelay[newx][newy] = 1;
8496
8497   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8498
8499   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8500   TestIfElementHitsCustomElement(newx, newy, direction);
8501   TestIfPlayerTouchesCustomElement(newx, newy);
8502   TestIfElementTouchesCustomElement(newx, newy);
8503
8504   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8505       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8506     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8507                              MV_DIR_OPPOSITE(direction));
8508 }
8509
8510 int AmoebeNachbarNr(int ax, int ay)
8511 {
8512   int i;
8513   int element = Feld[ax][ay];
8514   int group_nr = 0;
8515   static int xy[4][2] =
8516   {
8517     { 0, -1 },
8518     { -1, 0 },
8519     { +1, 0 },
8520     { 0, +1 }
8521   };
8522
8523   for (i = 0; i < NUM_DIRECTIONS; i++)
8524   {
8525     int x = ax + xy[i][0];
8526     int y = ay + xy[i][1];
8527
8528     if (!IN_LEV_FIELD(x, y))
8529       continue;
8530
8531     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8532       group_nr = AmoebaNr[x][y];
8533   }
8534
8535   return group_nr;
8536 }
8537
8538 void AmoebenVereinigen(int ax, int ay)
8539 {
8540   int i, x, y, xx, yy;
8541   int new_group_nr = AmoebaNr[ax][ay];
8542   static int xy[4][2] =
8543   {
8544     { 0, -1 },
8545     { -1, 0 },
8546     { +1, 0 },
8547     { 0, +1 }
8548   };
8549
8550   if (new_group_nr == 0)
8551     return;
8552
8553   for (i = 0; i < NUM_DIRECTIONS; i++)
8554   {
8555     x = ax + xy[i][0];
8556     y = ay + xy[i][1];
8557
8558     if (!IN_LEV_FIELD(x, y))
8559       continue;
8560
8561     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8562          Feld[x][y] == EL_BD_AMOEBA ||
8563          Feld[x][y] == EL_AMOEBA_DEAD) &&
8564         AmoebaNr[x][y] != new_group_nr)
8565     {
8566       int old_group_nr = AmoebaNr[x][y];
8567
8568       if (old_group_nr == 0)
8569         return;
8570
8571       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8572       AmoebaCnt[old_group_nr] = 0;
8573       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8574       AmoebaCnt2[old_group_nr] = 0;
8575
8576       SCAN_PLAYFIELD(xx, yy)
8577       {
8578         if (AmoebaNr[xx][yy] == old_group_nr)
8579           AmoebaNr[xx][yy] = new_group_nr;
8580       }
8581     }
8582   }
8583 }
8584
8585 void AmoebeUmwandeln(int ax, int ay)
8586 {
8587   int i, x, y;
8588
8589   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8590   {
8591     int group_nr = AmoebaNr[ax][ay];
8592
8593 #ifdef DEBUG
8594     if (group_nr == 0)
8595     {
8596       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8597       printf("AmoebeUmwandeln(): This should never happen!\n");
8598       return;
8599     }
8600 #endif
8601
8602     SCAN_PLAYFIELD(x, y)
8603     {
8604       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8605       {
8606         AmoebaNr[x][y] = 0;
8607         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8608       }
8609     }
8610
8611     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8612                             SND_AMOEBA_TURNING_TO_GEM :
8613                             SND_AMOEBA_TURNING_TO_ROCK));
8614     Bang(ax, ay);
8615   }
8616   else
8617   {
8618     static int xy[4][2] =
8619     {
8620       { 0, -1 },
8621       { -1, 0 },
8622       { +1, 0 },
8623       { 0, +1 }
8624     };
8625
8626     for (i = 0; i < NUM_DIRECTIONS; i++)
8627     {
8628       x = ax + xy[i][0];
8629       y = ay + xy[i][1];
8630
8631       if (!IN_LEV_FIELD(x, y))
8632         continue;
8633
8634       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8635       {
8636         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8637                               SND_AMOEBA_TURNING_TO_GEM :
8638                               SND_AMOEBA_TURNING_TO_ROCK));
8639         Bang(x, y);
8640       }
8641     }
8642   }
8643 }
8644
8645 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8646 {
8647   int x, y;
8648   int group_nr = AmoebaNr[ax][ay];
8649   boolean done = FALSE;
8650
8651 #ifdef DEBUG
8652   if (group_nr == 0)
8653   {
8654     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8655     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8656     return;
8657   }
8658 #endif
8659
8660   SCAN_PLAYFIELD(x, y)
8661   {
8662     if (AmoebaNr[x][y] == group_nr &&
8663         (Feld[x][y] == EL_AMOEBA_DEAD ||
8664          Feld[x][y] == EL_BD_AMOEBA ||
8665          Feld[x][y] == EL_AMOEBA_GROWING))
8666     {
8667       AmoebaNr[x][y] = 0;
8668       Feld[x][y] = new_element;
8669       InitField(x, y, FALSE);
8670       TEST_DrawLevelField(x, y);
8671       done = TRUE;
8672     }
8673   }
8674
8675   if (done)
8676     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8677                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8678                             SND_BD_AMOEBA_TURNING_TO_GEM));
8679 }
8680
8681 void AmoebeWaechst(int x, int y)
8682 {
8683   static unsigned int sound_delay = 0;
8684   static unsigned int sound_delay_value = 0;
8685
8686   if (!MovDelay[x][y])          /* start new growing cycle */
8687   {
8688     MovDelay[x][y] = 7;
8689
8690     if (DelayReached(&sound_delay, sound_delay_value))
8691     {
8692       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8693       sound_delay_value = 30;
8694     }
8695   }
8696
8697   if (MovDelay[x][y])           /* wait some time before growing bigger */
8698   {
8699     MovDelay[x][y]--;
8700     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8701     {
8702       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8703                                            6 - MovDelay[x][y]);
8704
8705       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8706     }
8707
8708     if (!MovDelay[x][y])
8709     {
8710       Feld[x][y] = Store[x][y];
8711       Store[x][y] = 0;
8712       TEST_DrawLevelField(x, y);
8713     }
8714   }
8715 }
8716
8717 void AmoebaDisappearing(int x, int y)
8718 {
8719   static unsigned int sound_delay = 0;
8720   static unsigned int sound_delay_value = 0;
8721
8722   if (!MovDelay[x][y])          /* start new shrinking cycle */
8723   {
8724     MovDelay[x][y] = 7;
8725
8726     if (DelayReached(&sound_delay, sound_delay_value))
8727       sound_delay_value = 30;
8728   }
8729
8730   if (MovDelay[x][y])           /* wait some time before shrinking */
8731   {
8732     MovDelay[x][y]--;
8733     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8734     {
8735       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8736                                            6 - MovDelay[x][y]);
8737
8738       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8739     }
8740
8741     if (!MovDelay[x][y])
8742     {
8743       Feld[x][y] = EL_EMPTY;
8744       TEST_DrawLevelField(x, y);
8745
8746       /* don't let mole enter this field in this cycle;
8747          (give priority to objects falling to this field from above) */
8748       Stop[x][y] = TRUE;
8749     }
8750   }
8751 }
8752
8753 void AmoebeAbleger(int ax, int ay)
8754 {
8755   int i;
8756   int element = Feld[ax][ay];
8757   int graphic = el2img(element);
8758   int newax = ax, neway = ay;
8759   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8760   static int xy[4][2] =
8761   {
8762     { 0, -1 },
8763     { -1, 0 },
8764     { +1, 0 },
8765     { 0, +1 }
8766   };
8767
8768   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8769   {
8770     Feld[ax][ay] = EL_AMOEBA_DEAD;
8771     TEST_DrawLevelField(ax, ay);
8772     return;
8773   }
8774
8775   if (IS_ANIMATED(graphic))
8776     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8777
8778   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8779     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8780
8781   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8782   {
8783     MovDelay[ax][ay]--;
8784     if (MovDelay[ax][ay])
8785       return;
8786   }
8787
8788   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8789   {
8790     int start = RND(4);
8791     int x = ax + xy[start][0];
8792     int y = ay + xy[start][1];
8793
8794     if (!IN_LEV_FIELD(x, y))
8795       return;
8796
8797     if (IS_FREE(x, y) ||
8798         CAN_GROW_INTO(Feld[x][y]) ||
8799         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8800         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8801     {
8802       newax = x;
8803       neway = y;
8804     }
8805
8806     if (newax == ax && neway == ay)
8807       return;
8808   }
8809   else                          /* normal or "filled" (BD style) amoeba */
8810   {
8811     int start = RND(4);
8812     boolean waiting_for_player = FALSE;
8813
8814     for (i = 0; i < NUM_DIRECTIONS; i++)
8815     {
8816       int j = (start + i) % 4;
8817       int x = ax + xy[j][0];
8818       int y = ay + xy[j][1];
8819
8820       if (!IN_LEV_FIELD(x, y))
8821         continue;
8822
8823       if (IS_FREE(x, y) ||
8824           CAN_GROW_INTO(Feld[x][y]) ||
8825           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8826           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8827       {
8828         newax = x;
8829         neway = y;
8830         break;
8831       }
8832       else if (IS_PLAYER(x, y))
8833         waiting_for_player = TRUE;
8834     }
8835
8836     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8837     {
8838       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8839       {
8840         Feld[ax][ay] = EL_AMOEBA_DEAD;
8841         TEST_DrawLevelField(ax, ay);
8842         AmoebaCnt[AmoebaNr[ax][ay]]--;
8843
8844         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8845         {
8846           if (element == EL_AMOEBA_FULL)
8847             AmoebeUmwandeln(ax, ay);
8848           else if (element == EL_BD_AMOEBA)
8849             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8850         }
8851       }
8852       return;
8853     }
8854     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8855     {
8856       /* amoeba gets larger by growing in some direction */
8857
8858       int new_group_nr = AmoebaNr[ax][ay];
8859
8860 #ifdef DEBUG
8861   if (new_group_nr == 0)
8862   {
8863     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8864     printf("AmoebeAbleger(): This should never happen!\n");
8865     return;
8866   }
8867 #endif
8868
8869       AmoebaNr[newax][neway] = new_group_nr;
8870       AmoebaCnt[new_group_nr]++;
8871       AmoebaCnt2[new_group_nr]++;
8872
8873       /* if amoeba touches other amoeba(s) after growing, unify them */
8874       AmoebenVereinigen(newax, neway);
8875
8876       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8877       {
8878         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8879         return;
8880       }
8881     }
8882   }
8883
8884   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8885       (neway == lev_fieldy - 1 && newax != ax))
8886   {
8887     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8888     Store[newax][neway] = element;
8889   }
8890   else if (neway == ay || element == EL_EMC_DRIPPER)
8891   {
8892     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8893
8894     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8895   }
8896   else
8897   {
8898     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8899     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8900     Store[ax][ay] = EL_AMOEBA_DROP;
8901     ContinueMoving(ax, ay);
8902     return;
8903   }
8904
8905   TEST_DrawLevelField(newax, neway);
8906 }
8907
8908 void Life(int ax, int ay)
8909 {
8910   int x1, y1, x2, y2;
8911   int life_time = 40;
8912   int element = Feld[ax][ay];
8913   int graphic = el2img(element);
8914   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8915                          level.biomaze);
8916   boolean changed = FALSE;
8917
8918   if (IS_ANIMATED(graphic))
8919     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8920
8921   if (Stop[ax][ay])
8922     return;
8923
8924   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8925     MovDelay[ax][ay] = life_time;
8926
8927   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8928   {
8929     MovDelay[ax][ay]--;
8930     if (MovDelay[ax][ay])
8931       return;
8932   }
8933
8934   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8935   {
8936     int xx = ax+x1, yy = ay+y1;
8937     int nachbarn = 0;
8938
8939     if (!IN_LEV_FIELD(xx, yy))
8940       continue;
8941
8942     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8943     {
8944       int x = xx+x2, y = yy+y2;
8945
8946       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8947         continue;
8948
8949       if (((Feld[x][y] == element ||
8950             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8951            !Stop[x][y]) ||
8952           (IS_FREE(x, y) && Stop[x][y]))
8953         nachbarn++;
8954     }
8955
8956     if (xx == ax && yy == ay)           /* field in the middle */
8957     {
8958       if (nachbarn < life_parameter[0] ||
8959           nachbarn > life_parameter[1])
8960       {
8961         Feld[xx][yy] = EL_EMPTY;
8962         if (!Stop[xx][yy])
8963           TEST_DrawLevelField(xx, yy);
8964         Stop[xx][yy] = TRUE;
8965         changed = TRUE;
8966       }
8967     }
8968     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8969     {                                   /* free border field */
8970       if (nachbarn >= life_parameter[2] &&
8971           nachbarn <= life_parameter[3])
8972       {
8973         Feld[xx][yy] = element;
8974         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8975         if (!Stop[xx][yy])
8976           TEST_DrawLevelField(xx, yy);
8977         Stop[xx][yy] = TRUE;
8978         changed = TRUE;
8979       }
8980     }
8981   }
8982
8983   if (changed)
8984     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8985                    SND_GAME_OF_LIFE_GROWING);
8986 }
8987
8988 static void InitRobotWheel(int x, int y)
8989 {
8990   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8991 }
8992
8993 static void RunRobotWheel(int x, int y)
8994 {
8995   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8996 }
8997
8998 static void StopRobotWheel(int x, int y)
8999 {
9000   if (ZX == x && ZY == y)
9001   {
9002     ZX = ZY = -1;
9003
9004     game.robot_wheel_active = FALSE;
9005   }
9006 }
9007
9008 static void InitTimegateWheel(int x, int y)
9009 {
9010   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9011 }
9012
9013 static void RunTimegateWheel(int x, int y)
9014 {
9015   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9016 }
9017
9018 static void InitMagicBallDelay(int x, int y)
9019 {
9020   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9021 }
9022
9023 static void ActivateMagicBall(int bx, int by)
9024 {
9025   int x, y;
9026
9027   if (level.ball_random)
9028   {
9029     int pos_border = RND(8);    /* select one of the eight border elements */
9030     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9031     int xx = pos_content % 3;
9032     int yy = pos_content / 3;
9033
9034     x = bx - 1 + xx;
9035     y = by - 1 + yy;
9036
9037     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9038       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9039   }
9040   else
9041   {
9042     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9043     {
9044       int xx = x - bx + 1;
9045       int yy = y - by + 1;
9046
9047       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9048         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9049     }
9050   }
9051
9052   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9053 }
9054
9055 void CheckExit(int x, int y)
9056 {
9057   if (local_player->gems_still_needed > 0 ||
9058       local_player->sokobanfields_still_needed > 0 ||
9059       local_player->lights_still_needed > 0)
9060   {
9061     int element = Feld[x][y];
9062     int graphic = el2img(element);
9063
9064     if (IS_ANIMATED(graphic))
9065       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9066
9067     return;
9068   }
9069
9070   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9071     return;
9072
9073   Feld[x][y] = EL_EXIT_OPENING;
9074
9075   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9076 }
9077
9078 void CheckExitEM(int x, int y)
9079 {
9080   if (local_player->gems_still_needed > 0 ||
9081       local_player->sokobanfields_still_needed > 0 ||
9082       local_player->lights_still_needed > 0)
9083   {
9084     int element = Feld[x][y];
9085     int graphic = el2img(element);
9086
9087     if (IS_ANIMATED(graphic))
9088       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9089
9090     return;
9091   }
9092
9093   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9094     return;
9095
9096   Feld[x][y] = EL_EM_EXIT_OPENING;
9097
9098   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9099 }
9100
9101 void CheckExitSteel(int x, int y)
9102 {
9103   if (local_player->gems_still_needed > 0 ||
9104       local_player->sokobanfields_still_needed > 0 ||
9105       local_player->lights_still_needed > 0)
9106   {
9107     int element = Feld[x][y];
9108     int graphic = el2img(element);
9109
9110     if (IS_ANIMATED(graphic))
9111       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9112
9113     return;
9114   }
9115
9116   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9117     return;
9118
9119   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9120
9121   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9122 }
9123
9124 void CheckExitSteelEM(int x, int y)
9125 {
9126   if (local_player->gems_still_needed > 0 ||
9127       local_player->sokobanfields_still_needed > 0 ||
9128       local_player->lights_still_needed > 0)
9129   {
9130     int element = Feld[x][y];
9131     int graphic = el2img(element);
9132
9133     if (IS_ANIMATED(graphic))
9134       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9135
9136     return;
9137   }
9138
9139   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9140     return;
9141
9142   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9143
9144   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9145 }
9146
9147 void CheckExitSP(int x, int y)
9148 {
9149   if (local_player->gems_still_needed > 0)
9150   {
9151     int element = Feld[x][y];
9152     int graphic = el2img(element);
9153
9154     if (IS_ANIMATED(graphic))
9155       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156
9157     return;
9158   }
9159
9160   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9161     return;
9162
9163   Feld[x][y] = EL_SP_EXIT_OPENING;
9164
9165   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9166 }
9167
9168 static void CloseAllOpenTimegates()
9169 {
9170   int x, y;
9171
9172   SCAN_PLAYFIELD(x, y)
9173   {
9174     int element = Feld[x][y];
9175
9176     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9177     {
9178       Feld[x][y] = EL_TIMEGATE_CLOSING;
9179
9180       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9181     }
9182   }
9183 }
9184
9185 void DrawTwinkleOnField(int x, int y)
9186 {
9187   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9188     return;
9189
9190   if (Feld[x][y] == EL_BD_DIAMOND)
9191     return;
9192
9193   if (MovDelay[x][y] == 0)      /* next animation frame */
9194     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9195
9196   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9197   {
9198     MovDelay[x][y]--;
9199
9200     DrawLevelElementAnimation(x, y, Feld[x][y]);
9201
9202     if (MovDelay[x][y] != 0)
9203     {
9204       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9205                                            10 - MovDelay[x][y]);
9206
9207       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9208     }
9209   }
9210 }
9211
9212 void MauerWaechst(int x, int y)
9213 {
9214   int delay = 6;
9215
9216   if (!MovDelay[x][y])          /* next animation frame */
9217     MovDelay[x][y] = 3 * delay;
9218
9219   if (MovDelay[x][y])           /* wait some time before next frame */
9220   {
9221     MovDelay[x][y]--;
9222
9223     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9224     {
9225       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9226       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9227
9228       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9229     }
9230
9231     if (!MovDelay[x][y])
9232     {
9233       if (MovDir[x][y] == MV_LEFT)
9234       {
9235         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9236           TEST_DrawLevelField(x - 1, y);
9237       }
9238       else if (MovDir[x][y] == MV_RIGHT)
9239       {
9240         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9241           TEST_DrawLevelField(x + 1, y);
9242       }
9243       else if (MovDir[x][y] == MV_UP)
9244       {
9245         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9246           TEST_DrawLevelField(x, y - 1);
9247       }
9248       else
9249       {
9250         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9251           TEST_DrawLevelField(x, y + 1);
9252       }
9253
9254       Feld[x][y] = Store[x][y];
9255       Store[x][y] = 0;
9256       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9257       TEST_DrawLevelField(x, y);
9258     }
9259   }
9260 }
9261
9262 void MauerAbleger(int ax, int ay)
9263 {
9264   int element = Feld[ax][ay];
9265   int graphic = el2img(element);
9266   boolean oben_frei = FALSE, unten_frei = FALSE;
9267   boolean links_frei = FALSE, rechts_frei = FALSE;
9268   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9269   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9270   boolean new_wall = FALSE;
9271
9272   if (IS_ANIMATED(graphic))
9273     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9274
9275   if (!MovDelay[ax][ay])        /* start building new wall */
9276     MovDelay[ax][ay] = 6;
9277
9278   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9279   {
9280     MovDelay[ax][ay]--;
9281     if (MovDelay[ax][ay])
9282       return;
9283   }
9284
9285   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9286     oben_frei = TRUE;
9287   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9288     unten_frei = TRUE;
9289   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9290     links_frei = TRUE;
9291   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9292     rechts_frei = TRUE;
9293
9294   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9295       element == EL_EXPANDABLE_WALL_ANY)
9296   {
9297     if (oben_frei)
9298     {
9299       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9300       Store[ax][ay-1] = element;
9301       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9302       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9303         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9304                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9305       new_wall = TRUE;
9306     }
9307     if (unten_frei)
9308     {
9309       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9310       Store[ax][ay+1] = element;
9311       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9312       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9313         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9314                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9315       new_wall = TRUE;
9316     }
9317   }
9318
9319   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9320       element == EL_EXPANDABLE_WALL_ANY ||
9321       element == EL_EXPANDABLE_WALL ||
9322       element == EL_BD_EXPANDABLE_WALL)
9323   {
9324     if (links_frei)
9325     {
9326       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9327       Store[ax-1][ay] = element;
9328       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9329       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9330         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9331                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9332       new_wall = TRUE;
9333     }
9334
9335     if (rechts_frei)
9336     {
9337       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9338       Store[ax+1][ay] = element;
9339       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9340       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9341         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9342                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9343       new_wall = TRUE;
9344     }
9345   }
9346
9347   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9348     TEST_DrawLevelField(ax, ay);
9349
9350   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9351     oben_massiv = TRUE;
9352   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9353     unten_massiv = TRUE;
9354   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9355     links_massiv = TRUE;
9356   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9357     rechts_massiv = TRUE;
9358
9359   if (((oben_massiv && unten_massiv) ||
9360        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9361        element == EL_EXPANDABLE_WALL) &&
9362       ((links_massiv && rechts_massiv) ||
9363        element == EL_EXPANDABLE_WALL_VERTICAL))
9364     Feld[ax][ay] = EL_WALL;
9365
9366   if (new_wall)
9367     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9368 }
9369
9370 void MauerAblegerStahl(int ax, int ay)
9371 {
9372   int element = Feld[ax][ay];
9373   int graphic = el2img(element);
9374   boolean oben_frei = FALSE, unten_frei = FALSE;
9375   boolean links_frei = FALSE, rechts_frei = FALSE;
9376   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9377   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9378   boolean new_wall = FALSE;
9379
9380   if (IS_ANIMATED(graphic))
9381     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9382
9383   if (!MovDelay[ax][ay])        /* start building new wall */
9384     MovDelay[ax][ay] = 6;
9385
9386   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9387   {
9388     MovDelay[ax][ay]--;
9389     if (MovDelay[ax][ay])
9390       return;
9391   }
9392
9393   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9394     oben_frei = TRUE;
9395   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9396     unten_frei = TRUE;
9397   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9398     links_frei = TRUE;
9399   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9400     rechts_frei = TRUE;
9401
9402   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9403       element == EL_EXPANDABLE_STEELWALL_ANY)
9404   {
9405     if (oben_frei)
9406     {
9407       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9408       Store[ax][ay-1] = element;
9409       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9410       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9411         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9412                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9413       new_wall = TRUE;
9414     }
9415     if (unten_frei)
9416     {
9417       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9418       Store[ax][ay+1] = element;
9419       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9420       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9421         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9422                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9423       new_wall = TRUE;
9424     }
9425   }
9426
9427   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9428       element == EL_EXPANDABLE_STEELWALL_ANY)
9429   {
9430     if (links_frei)
9431     {
9432       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9433       Store[ax-1][ay] = element;
9434       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9435       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9436         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9437                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9438       new_wall = TRUE;
9439     }
9440
9441     if (rechts_frei)
9442     {
9443       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9444       Store[ax+1][ay] = element;
9445       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9446       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9447         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9448                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9449       new_wall = TRUE;
9450     }
9451   }
9452
9453   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9454     oben_massiv = TRUE;
9455   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9456     unten_massiv = TRUE;
9457   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9458     links_massiv = TRUE;
9459   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9460     rechts_massiv = TRUE;
9461
9462   if (((oben_massiv && unten_massiv) ||
9463        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9464       ((links_massiv && rechts_massiv) ||
9465        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9466     Feld[ax][ay] = EL_STEELWALL;
9467
9468   if (new_wall)
9469     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9470 }
9471
9472 void CheckForDragon(int x, int y)
9473 {
9474   int i, j;
9475   boolean dragon_found = FALSE;
9476   static int xy[4][2] =
9477   {
9478     { 0, -1 },
9479     { -1, 0 },
9480     { +1, 0 },
9481     { 0, +1 }
9482   };
9483
9484   for (i = 0; i < NUM_DIRECTIONS; i++)
9485   {
9486     for (j = 0; j < 4; j++)
9487     {
9488       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9489
9490       if (IN_LEV_FIELD(xx, yy) &&
9491           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9492       {
9493         if (Feld[xx][yy] == EL_DRAGON)
9494           dragon_found = TRUE;
9495       }
9496       else
9497         break;
9498     }
9499   }
9500
9501   if (!dragon_found)
9502   {
9503     for (i = 0; i < NUM_DIRECTIONS; i++)
9504     {
9505       for (j = 0; j < 3; j++)
9506       {
9507         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9508   
9509         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9510         {
9511           Feld[xx][yy] = EL_EMPTY;
9512           TEST_DrawLevelField(xx, yy);
9513         }
9514         else
9515           break;
9516       }
9517     }
9518   }
9519 }
9520
9521 static void InitBuggyBase(int x, int y)
9522 {
9523   int element = Feld[x][y];
9524   int activating_delay = FRAMES_PER_SECOND / 4;
9525
9526   ChangeDelay[x][y] =
9527     (element == EL_SP_BUGGY_BASE ?
9528      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9529      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9530      activating_delay :
9531      element == EL_SP_BUGGY_BASE_ACTIVE ?
9532      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9533 }
9534
9535 static void WarnBuggyBase(int x, int y)
9536 {
9537   int i;
9538   static int xy[4][2] =
9539   {
9540     { 0, -1 },
9541     { -1, 0 },
9542     { +1, 0 },
9543     { 0, +1 }
9544   };
9545
9546   for (i = 0; i < NUM_DIRECTIONS; i++)
9547   {
9548     int xx = x + xy[i][0];
9549     int yy = y + xy[i][1];
9550
9551     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9552     {
9553       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9554
9555       break;
9556     }
9557   }
9558 }
9559
9560 static void InitTrap(int x, int y)
9561 {
9562   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9563 }
9564
9565 static void ActivateTrap(int x, int y)
9566 {
9567   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9568 }
9569
9570 static void ChangeActiveTrap(int x, int y)
9571 {
9572   int graphic = IMG_TRAP_ACTIVE;
9573
9574   /* if new animation frame was drawn, correct crumbled sand border */
9575   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9576     TEST_DrawLevelFieldCrumbled(x, y);
9577 }
9578
9579 static int getSpecialActionElement(int element, int number, int base_element)
9580 {
9581   return (element != EL_EMPTY ? element :
9582           number != -1 ? base_element + number - 1 :
9583           EL_EMPTY);
9584 }
9585
9586 static int getModifiedActionNumber(int value_old, int operator, int operand,
9587                                    int value_min, int value_max)
9588 {
9589   int value_new = (operator == CA_MODE_SET      ? operand :
9590                    operator == CA_MODE_ADD      ? value_old + operand :
9591                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9592                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9593                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9594                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9595                    value_old);
9596
9597   return (value_new < value_min ? value_min :
9598           value_new > value_max ? value_max :
9599           value_new);
9600 }
9601
9602 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9603 {
9604   struct ElementInfo *ei = &element_info[element];
9605   struct ElementChangeInfo *change = &ei->change_page[page];
9606   int target_element = change->target_element;
9607   int action_type = change->action_type;
9608   int action_mode = change->action_mode;
9609   int action_arg = change->action_arg;
9610   int action_element = change->action_element;
9611   int i;
9612
9613   if (!change->has_action)
9614     return;
9615
9616   /* ---------- determine action paramater values -------------------------- */
9617
9618   int level_time_value =
9619     (level.time > 0 ? TimeLeft :
9620      TimePlayed);
9621
9622   int action_arg_element_raw =
9623     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9624      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9625      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9626      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9627      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9628      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9629      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9630      EL_EMPTY);
9631   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9632
9633   int action_arg_direction =
9634     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9635      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9636      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9637      change->actual_trigger_side :
9638      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9639      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9640      MV_NONE);
9641
9642   int action_arg_number_min =
9643     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9644      CA_ARG_MIN);
9645
9646   int action_arg_number_max =
9647     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9648      action_type == CA_SET_LEVEL_GEMS ? 999 :
9649      action_type == CA_SET_LEVEL_TIME ? 9999 :
9650      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9651      action_type == CA_SET_CE_VALUE ? 9999 :
9652      action_type == CA_SET_CE_SCORE ? 9999 :
9653      CA_ARG_MAX);
9654
9655   int action_arg_number_reset =
9656     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9657      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9658      action_type == CA_SET_LEVEL_TIME ? level.time :
9659      action_type == CA_SET_LEVEL_SCORE ? 0 :
9660      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9661      action_type == CA_SET_CE_SCORE ? 0 :
9662      0);
9663
9664   int action_arg_number =
9665     (action_arg <= CA_ARG_MAX ? action_arg :
9666      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9667      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9668      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9669      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9670      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9671      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9672      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9673      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9674      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9675      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9676      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9677      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9678      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9679      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9680      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9681      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9682      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9683      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9684      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9685      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9686      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9687      -1);
9688
9689   int action_arg_number_old =
9690     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9691      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9692      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9693      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9694      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9695      0);
9696
9697   int action_arg_number_new =
9698     getModifiedActionNumber(action_arg_number_old,
9699                             action_mode, action_arg_number,
9700                             action_arg_number_min, action_arg_number_max);
9701
9702   int trigger_player_bits =
9703     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9704      change->actual_trigger_player_bits : change->trigger_player);
9705
9706   int action_arg_player_bits =
9707     (action_arg >= CA_ARG_PLAYER_1 &&
9708      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9709      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9710      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9711      PLAYER_BITS_ANY);
9712
9713   /* ---------- execute action  -------------------------------------------- */
9714
9715   switch (action_type)
9716   {
9717     case CA_NO_ACTION:
9718     {
9719       return;
9720     }
9721
9722     /* ---------- level actions  ------------------------------------------- */
9723
9724     case CA_RESTART_LEVEL:
9725     {
9726       game.restart_level = TRUE;
9727
9728       break;
9729     }
9730
9731     case CA_SHOW_ENVELOPE:
9732     {
9733       int element = getSpecialActionElement(action_arg_element,
9734                                             action_arg_number, EL_ENVELOPE_1);
9735
9736       if (IS_ENVELOPE(element))
9737         local_player->show_envelope = element;
9738
9739       break;
9740     }
9741
9742     case CA_SET_LEVEL_TIME:
9743     {
9744       if (level.time > 0)       /* only modify limited time value */
9745       {
9746         TimeLeft = action_arg_number_new;
9747
9748         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9749
9750         DisplayGameControlValues();
9751
9752         if (!TimeLeft && setup.time_limit)
9753           for (i = 0; i < MAX_PLAYERS; i++)
9754             KillPlayer(&stored_player[i]);
9755       }
9756
9757       break;
9758     }
9759
9760     case CA_SET_LEVEL_SCORE:
9761     {
9762       local_player->score = action_arg_number_new;
9763
9764       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9765
9766       DisplayGameControlValues();
9767
9768       break;
9769     }
9770
9771     case CA_SET_LEVEL_GEMS:
9772     {
9773       local_player->gems_still_needed = action_arg_number_new;
9774
9775       game.snapshot.collected_item = TRUE;
9776
9777       game_panel_controls[GAME_PANEL_GEMS].value =
9778         local_player->gems_still_needed;
9779
9780       DisplayGameControlValues();
9781
9782       break;
9783     }
9784
9785     case CA_SET_LEVEL_WIND:
9786     {
9787       game.wind_direction = action_arg_direction;
9788
9789       break;
9790     }
9791
9792     case CA_SET_LEVEL_RANDOM_SEED:
9793     {
9794       /* ensure that setting a new random seed while playing is predictable */
9795       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9796
9797       break;
9798     }
9799
9800     /* ---------- player actions  ------------------------------------------ */
9801
9802     case CA_MOVE_PLAYER:
9803     {
9804       /* automatically move to the next field in specified direction */
9805       for (i = 0; i < MAX_PLAYERS; i++)
9806         if (trigger_player_bits & (1 << i))
9807           stored_player[i].programmed_action = action_arg_direction;
9808
9809       break;
9810     }
9811
9812     case CA_EXIT_PLAYER:
9813     {
9814       for (i = 0; i < MAX_PLAYERS; i++)
9815         if (action_arg_player_bits & (1 << i))
9816           PlayerWins(&stored_player[i]);
9817
9818       break;
9819     }
9820
9821     case CA_KILL_PLAYER:
9822     {
9823       for (i = 0; i < MAX_PLAYERS; i++)
9824         if (action_arg_player_bits & (1 << i))
9825           KillPlayer(&stored_player[i]);
9826
9827       break;
9828     }
9829
9830     case CA_SET_PLAYER_KEYS:
9831     {
9832       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9833       int element = getSpecialActionElement(action_arg_element,
9834                                             action_arg_number, EL_KEY_1);
9835
9836       if (IS_KEY(element))
9837       {
9838         for (i = 0; i < MAX_PLAYERS; i++)
9839         {
9840           if (trigger_player_bits & (1 << i))
9841           {
9842             stored_player[i].key[KEY_NR(element)] = key_state;
9843
9844             DrawGameDoorValues();
9845           }
9846         }
9847       }
9848
9849       break;
9850     }
9851
9852     case CA_SET_PLAYER_SPEED:
9853     {
9854       for (i = 0; i < MAX_PLAYERS; i++)
9855       {
9856         if (trigger_player_bits & (1 << i))
9857         {
9858           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9859
9860           if (action_arg == CA_ARG_SPEED_FASTER &&
9861               stored_player[i].cannot_move)
9862           {
9863             action_arg_number = STEPSIZE_VERY_SLOW;
9864           }
9865           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9866                    action_arg == CA_ARG_SPEED_FASTER)
9867           {
9868             action_arg_number = 2;
9869             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9870                            CA_MODE_MULTIPLY);
9871           }
9872           else if (action_arg == CA_ARG_NUMBER_RESET)
9873           {
9874             action_arg_number = level.initial_player_stepsize[i];
9875           }
9876
9877           move_stepsize =
9878             getModifiedActionNumber(move_stepsize,
9879                                     action_mode,
9880                                     action_arg_number,
9881                                     action_arg_number_min,
9882                                     action_arg_number_max);
9883
9884           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9885         }
9886       }
9887
9888       break;
9889     }
9890
9891     case CA_SET_PLAYER_SHIELD:
9892     {
9893       for (i = 0; i < MAX_PLAYERS; i++)
9894       {
9895         if (trigger_player_bits & (1 << i))
9896         {
9897           if (action_arg == CA_ARG_SHIELD_OFF)
9898           {
9899             stored_player[i].shield_normal_time_left = 0;
9900             stored_player[i].shield_deadly_time_left = 0;
9901           }
9902           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9903           {
9904             stored_player[i].shield_normal_time_left = 999999;
9905           }
9906           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9907           {
9908             stored_player[i].shield_normal_time_left = 999999;
9909             stored_player[i].shield_deadly_time_left = 999999;
9910           }
9911         }
9912       }
9913
9914       break;
9915     }
9916
9917     case CA_SET_PLAYER_GRAVITY:
9918     {
9919       for (i = 0; i < MAX_PLAYERS; i++)
9920       {
9921         if (trigger_player_bits & (1 << i))
9922         {
9923           stored_player[i].gravity =
9924             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9925              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9926              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9927              stored_player[i].gravity);
9928         }
9929       }
9930
9931       break;
9932     }
9933
9934     case CA_SET_PLAYER_ARTWORK:
9935     {
9936       for (i = 0; i < MAX_PLAYERS; i++)
9937       {
9938         if (trigger_player_bits & (1 << i))
9939         {
9940           int artwork_element = action_arg_element;
9941
9942           if (action_arg == CA_ARG_ELEMENT_RESET)
9943             artwork_element =
9944               (level.use_artwork_element[i] ? level.artwork_element[i] :
9945                stored_player[i].element_nr);
9946
9947           if (stored_player[i].artwork_element != artwork_element)
9948             stored_player[i].Frame = 0;
9949
9950           stored_player[i].artwork_element = artwork_element;
9951
9952           SetPlayerWaiting(&stored_player[i], FALSE);
9953
9954           /* set number of special actions for bored and sleeping animation */
9955           stored_player[i].num_special_action_bored =
9956             get_num_special_action(artwork_element,
9957                                    ACTION_BORING_1, ACTION_BORING_LAST);
9958           stored_player[i].num_special_action_sleeping =
9959             get_num_special_action(artwork_element,
9960                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9961         }
9962       }
9963
9964       break;
9965     }
9966
9967     case CA_SET_PLAYER_INVENTORY:
9968     {
9969       for (i = 0; i < MAX_PLAYERS; i++)
9970       {
9971         struct PlayerInfo *player = &stored_player[i];
9972         int j, k;
9973
9974         if (trigger_player_bits & (1 << i))
9975         {
9976           int inventory_element = action_arg_element;
9977
9978           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9979               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9980               action_arg == CA_ARG_ELEMENT_ACTION)
9981           {
9982             int element = inventory_element;
9983             int collect_count = element_info[element].collect_count_initial;
9984
9985             if (!IS_CUSTOM_ELEMENT(element))
9986               collect_count = 1;
9987
9988             if (collect_count == 0)
9989               player->inventory_infinite_element = element;
9990             else
9991               for (k = 0; k < collect_count; k++)
9992                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9993                   player->inventory_element[player->inventory_size++] =
9994                     element;
9995           }
9996           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9997                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9998                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9999           {
10000             if (player->inventory_infinite_element != EL_UNDEFINED &&
10001                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10002                                      action_arg_element_raw))
10003               player->inventory_infinite_element = EL_UNDEFINED;
10004
10005             for (k = 0, j = 0; j < player->inventory_size; j++)
10006             {
10007               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10008                                         action_arg_element_raw))
10009                 player->inventory_element[k++] = player->inventory_element[j];
10010             }
10011
10012             player->inventory_size = k;
10013           }
10014           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10015           {
10016             if (player->inventory_size > 0)
10017             {
10018               for (j = 0; j < player->inventory_size - 1; j++)
10019                 player->inventory_element[j] = player->inventory_element[j + 1];
10020
10021               player->inventory_size--;
10022             }
10023           }
10024           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10025           {
10026             if (player->inventory_size > 0)
10027               player->inventory_size--;
10028           }
10029           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10030           {
10031             player->inventory_infinite_element = EL_UNDEFINED;
10032             player->inventory_size = 0;
10033           }
10034           else if (action_arg == CA_ARG_INVENTORY_RESET)
10035           {
10036             player->inventory_infinite_element = EL_UNDEFINED;
10037             player->inventory_size = 0;
10038
10039             if (level.use_initial_inventory[i])
10040             {
10041               for (j = 0; j < level.initial_inventory_size[i]; j++)
10042               {
10043                 int element = level.initial_inventory_content[i][j];
10044                 int collect_count = element_info[element].collect_count_initial;
10045
10046                 if (!IS_CUSTOM_ELEMENT(element))
10047                   collect_count = 1;
10048
10049                 if (collect_count == 0)
10050                   player->inventory_infinite_element = element;
10051                 else
10052                   for (k = 0; k < collect_count; k++)
10053                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10054                       player->inventory_element[player->inventory_size++] =
10055                         element;
10056               }
10057             }
10058           }
10059         }
10060       }
10061
10062       break;
10063     }
10064
10065     /* ---------- CE actions  ---------------------------------------------- */
10066
10067     case CA_SET_CE_VALUE:
10068     {
10069       int last_ce_value = CustomValue[x][y];
10070
10071       CustomValue[x][y] = action_arg_number_new;
10072
10073       if (CustomValue[x][y] != last_ce_value)
10074       {
10075         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10076         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10077
10078         if (CustomValue[x][y] == 0)
10079         {
10080           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10081           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10082         }
10083       }
10084
10085       break;
10086     }
10087
10088     case CA_SET_CE_SCORE:
10089     {
10090       int last_ce_score = ei->collect_score;
10091
10092       ei->collect_score = action_arg_number_new;
10093
10094       if (ei->collect_score != last_ce_score)
10095       {
10096         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10097         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10098
10099         if (ei->collect_score == 0)
10100         {
10101           int xx, yy;
10102
10103           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10104           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10105
10106           /*
10107             This is a very special case that seems to be a mixture between
10108             CheckElementChange() and CheckTriggeredElementChange(): while
10109             the first one only affects single elements that are triggered
10110             directly, the second one affects multiple elements in the playfield
10111             that are triggered indirectly by another element. This is a third
10112             case: Changing the CE score always affects multiple identical CEs,
10113             so every affected CE must be checked, not only the single CE for
10114             which the CE score was changed in the first place (as every instance
10115             of that CE shares the same CE score, and therefore also can change)!
10116           */
10117           SCAN_PLAYFIELD(xx, yy)
10118           {
10119             if (Feld[xx][yy] == element)
10120               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10121                                  CE_SCORE_GETS_ZERO);
10122           }
10123         }
10124       }
10125
10126       break;
10127     }
10128
10129     case CA_SET_CE_ARTWORK:
10130     {
10131       int artwork_element = action_arg_element;
10132       boolean reset_frame = FALSE;
10133       int xx, yy;
10134
10135       if (action_arg == CA_ARG_ELEMENT_RESET)
10136         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10137                            element);
10138
10139       if (ei->gfx_element != artwork_element)
10140         reset_frame = TRUE;
10141
10142       ei->gfx_element = artwork_element;
10143
10144       SCAN_PLAYFIELD(xx, yy)
10145       {
10146         if (Feld[xx][yy] == element)
10147         {
10148           if (reset_frame)
10149           {
10150             ResetGfxAnimation(xx, yy);
10151             ResetRandomAnimationValue(xx, yy);
10152           }
10153
10154           TEST_DrawLevelField(xx, yy);
10155         }
10156       }
10157
10158       break;
10159     }
10160
10161     /* ---------- engine actions  ------------------------------------------ */
10162
10163     case CA_SET_ENGINE_SCAN_MODE:
10164     {
10165       InitPlayfieldScanMode(action_arg);
10166
10167       break;
10168     }
10169
10170     default:
10171       break;
10172   }
10173 }
10174
10175 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10176 {
10177   int old_element = Feld[x][y];
10178   int new_element = GetElementFromGroupElement(element);
10179   int previous_move_direction = MovDir[x][y];
10180   int last_ce_value = CustomValue[x][y];
10181   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10182   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10183   boolean add_player_onto_element = (new_element_is_player &&
10184                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10185                                      IS_WALKABLE(old_element));
10186
10187   if (!add_player_onto_element)
10188   {
10189     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10190       RemoveMovingField(x, y);
10191     else
10192       RemoveField(x, y);
10193
10194     Feld[x][y] = new_element;
10195
10196     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10197       MovDir[x][y] = previous_move_direction;
10198
10199     if (element_info[new_element].use_last_ce_value)
10200       CustomValue[x][y] = last_ce_value;
10201
10202     InitField_WithBug1(x, y, FALSE);
10203
10204     new_element = Feld[x][y];   /* element may have changed */
10205
10206     ResetGfxAnimation(x, y);
10207     ResetRandomAnimationValue(x, y);
10208
10209     TEST_DrawLevelField(x, y);
10210
10211     if (GFX_CRUMBLED(new_element))
10212       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10213   }
10214
10215   /* check if element under the player changes from accessible to unaccessible
10216      (needed for special case of dropping element which then changes) */
10217   /* (must be checked after creating new element for walkable group elements) */
10218   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10219       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10220   {
10221     Bang(x, y);
10222
10223     return;
10224   }
10225
10226   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10227   if (new_element_is_player)
10228     RelocatePlayer(x, y, new_element);
10229
10230   if (is_change)
10231     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10232
10233   TestIfBadThingTouchesPlayer(x, y);
10234   TestIfPlayerTouchesCustomElement(x, y);
10235   TestIfElementTouchesCustomElement(x, y);
10236 }
10237
10238 static void CreateField(int x, int y, int element)
10239 {
10240   CreateFieldExt(x, y, element, FALSE);
10241 }
10242
10243 static void CreateElementFromChange(int x, int y, int element)
10244 {
10245   element = GET_VALID_RUNTIME_ELEMENT(element);
10246
10247   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10248   {
10249     int old_element = Feld[x][y];
10250
10251     /* prevent changed element from moving in same engine frame
10252        unless both old and new element can either fall or move */
10253     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10254         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10255       Stop[x][y] = TRUE;
10256   }
10257
10258   CreateFieldExt(x, y, element, TRUE);
10259 }
10260
10261 static boolean ChangeElement(int x, int y, int element, int page)
10262 {
10263   struct ElementInfo *ei = &element_info[element];
10264   struct ElementChangeInfo *change = &ei->change_page[page];
10265   int ce_value = CustomValue[x][y];
10266   int ce_score = ei->collect_score;
10267   int target_element;
10268   int old_element = Feld[x][y];
10269
10270   /* always use default change event to prevent running into a loop */
10271   if (ChangeEvent[x][y] == -1)
10272     ChangeEvent[x][y] = CE_DELAY;
10273
10274   if (ChangeEvent[x][y] == CE_DELAY)
10275   {
10276     /* reset actual trigger element, trigger player and action element */
10277     change->actual_trigger_element = EL_EMPTY;
10278     change->actual_trigger_player = EL_EMPTY;
10279     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10280     change->actual_trigger_side = CH_SIDE_NONE;
10281     change->actual_trigger_ce_value = 0;
10282     change->actual_trigger_ce_score = 0;
10283   }
10284
10285   /* do not change elements more than a specified maximum number of changes */
10286   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10287     return FALSE;
10288
10289   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10290
10291   if (change->explode)
10292   {
10293     Bang(x, y);
10294
10295     return TRUE;
10296   }
10297
10298   if (change->use_target_content)
10299   {
10300     boolean complete_replace = TRUE;
10301     boolean can_replace[3][3];
10302     int xx, yy;
10303
10304     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10305     {
10306       boolean is_empty;
10307       boolean is_walkable;
10308       boolean is_diggable;
10309       boolean is_collectible;
10310       boolean is_removable;
10311       boolean is_destructible;
10312       int ex = x + xx - 1;
10313       int ey = y + yy - 1;
10314       int content_element = change->target_content.e[xx][yy];
10315       int e;
10316
10317       can_replace[xx][yy] = TRUE;
10318
10319       if (ex == x && ey == y)   /* do not check changing element itself */
10320         continue;
10321
10322       if (content_element == EL_EMPTY_SPACE)
10323       {
10324         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10325
10326         continue;
10327       }
10328
10329       if (!IN_LEV_FIELD(ex, ey))
10330       {
10331         can_replace[xx][yy] = FALSE;
10332         complete_replace = FALSE;
10333
10334         continue;
10335       }
10336
10337       e = Feld[ex][ey];
10338
10339       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10340         e = MovingOrBlocked2Element(ex, ey);
10341
10342       is_empty = (IS_FREE(ex, ey) ||
10343                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10344
10345       is_walkable     = (is_empty || IS_WALKABLE(e));
10346       is_diggable     = (is_empty || IS_DIGGABLE(e));
10347       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10348       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10349       is_removable    = (is_diggable || is_collectible);
10350
10351       can_replace[xx][yy] =
10352         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10353           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10354           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10355           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10356           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10357           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10358          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10359
10360       if (!can_replace[xx][yy])
10361         complete_replace = FALSE;
10362     }
10363
10364     if (!change->only_if_complete || complete_replace)
10365     {
10366       boolean something_has_changed = FALSE;
10367
10368       if (change->only_if_complete && change->use_random_replace &&
10369           RND(100) < change->random_percentage)
10370         return FALSE;
10371
10372       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10373       {
10374         int ex = x + xx - 1;
10375         int ey = y + yy - 1;
10376         int content_element;
10377
10378         if (can_replace[xx][yy] && (!change->use_random_replace ||
10379                                     RND(100) < change->random_percentage))
10380         {
10381           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10382             RemoveMovingField(ex, ey);
10383
10384           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10385
10386           content_element = change->target_content.e[xx][yy];
10387           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10388                                               ce_value, ce_score);
10389
10390           CreateElementFromChange(ex, ey, target_element);
10391
10392           something_has_changed = TRUE;
10393
10394           /* for symmetry reasons, freeze newly created border elements */
10395           if (ex != x || ey != y)
10396             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10397         }
10398       }
10399
10400       if (something_has_changed)
10401       {
10402         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10403         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10404       }
10405     }
10406   }
10407   else
10408   {
10409     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10410                                         ce_value, ce_score);
10411
10412     if (element == EL_DIAGONAL_GROWING ||
10413         element == EL_DIAGONAL_SHRINKING)
10414     {
10415       target_element = Store[x][y];
10416
10417       Store[x][y] = EL_EMPTY;
10418     }
10419
10420     CreateElementFromChange(x, y, target_element);
10421
10422     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10423     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10424   }
10425
10426   /* this uses direct change before indirect change */
10427   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10428
10429   return TRUE;
10430 }
10431
10432 static void HandleElementChange(int x, int y, int page)
10433 {
10434   int element = MovingOrBlocked2Element(x, y);
10435   struct ElementInfo *ei = &element_info[element];
10436   struct ElementChangeInfo *change = &ei->change_page[page];
10437   boolean handle_action_before_change = FALSE;
10438
10439 #ifdef DEBUG
10440   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10441       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10442   {
10443     printf("\n\n");
10444     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10445            x, y, element, element_info[element].token_name);
10446     printf("HandleElementChange(): This should never happen!\n");
10447     printf("\n\n");
10448   }
10449 #endif
10450
10451   /* this can happen with classic bombs on walkable, changing elements */
10452   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10453   {
10454     return;
10455   }
10456
10457   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10458   {
10459     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10460
10461     if (change->can_change)
10462     {
10463       /* !!! not clear why graphic animation should be reset at all here !!! */
10464       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10465       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10466
10467       /*
10468         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10469
10470         When using an animation frame delay of 1 (this only happens with
10471         "sp_zonk.moving.left/right" in the classic graphics), the default
10472         (non-moving) animation shows wrong animation frames (while the
10473         moving animation, like "sp_zonk.moving.left/right", is correct,
10474         so this graphical bug never shows up with the classic graphics).
10475         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10476         be drawn instead of the correct frames 0,1,2,3. This is caused by
10477         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10478         an element change: First when the change delay ("ChangeDelay[][]")
10479         counter has reached zero after decrementing, then a second time in
10480         the next frame (after "GfxFrame[][]" was already incremented) when
10481         "ChangeDelay[][]" is reset to the initial delay value again.
10482
10483         This causes frame 0 to be drawn twice, while the last frame won't
10484         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10485
10486         As some animations may already be cleverly designed around this bug
10487         (at least the "Snake Bite" snake tail animation does this), it cannot
10488         simply be fixed here without breaking such existing animations.
10489         Unfortunately, it cannot easily be detected if a graphics set was
10490         designed "before" or "after" the bug was fixed. As a workaround,
10491         a new graphics set option "game.graphics_engine_version" was added
10492         to be able to specify the game's major release version for which the
10493         graphics set was designed, which can then be used to decide if the
10494         bugfix should be used (version 4 and above) or not (version 3 or
10495         below, or if no version was specified at all, as with old sets).
10496
10497         (The wrong/fixed animation frames can be tested with the test level set
10498         "test_gfxframe" and level "000", which contains a specially prepared
10499         custom element at level position (x/y) == (11/9) which uses the zonk
10500         animation mentioned above. Using "game.graphics_engine_version: 4"
10501         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10502         This can also be seen from the debug output for this test element.)
10503       */
10504
10505       /* when a custom element is about to change (for example by change delay),
10506          do not reset graphic animation when the custom element is moving */
10507       if (game.graphics_engine_version < 4 &&
10508           !IS_MOVING(x, y))
10509       {
10510         ResetGfxAnimation(x, y);
10511         ResetRandomAnimationValue(x, y);
10512       }
10513
10514       if (change->pre_change_function)
10515         change->pre_change_function(x, y);
10516     }
10517   }
10518
10519   ChangeDelay[x][y]--;
10520
10521   if (ChangeDelay[x][y] != 0)           /* continue element change */
10522   {
10523     if (change->can_change)
10524     {
10525       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10526
10527       if (IS_ANIMATED(graphic))
10528         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10529
10530       if (change->change_function)
10531         change->change_function(x, y);
10532     }
10533   }
10534   else                                  /* finish element change */
10535   {
10536     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10537     {
10538       page = ChangePage[x][y];
10539       ChangePage[x][y] = -1;
10540
10541       change = &ei->change_page[page];
10542     }
10543
10544     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10545     {
10546       ChangeDelay[x][y] = 1;            /* try change after next move step */
10547       ChangePage[x][y] = page;          /* remember page to use for change */
10548
10549       return;
10550     }
10551
10552     /* special case: set new level random seed before changing element */
10553     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10554       handle_action_before_change = TRUE;
10555
10556     if (change->has_action && handle_action_before_change)
10557       ExecuteCustomElementAction(x, y, element, page);
10558
10559     if (change->can_change)
10560     {
10561       if (ChangeElement(x, y, element, page))
10562       {
10563         if (change->post_change_function)
10564           change->post_change_function(x, y);
10565       }
10566     }
10567
10568     if (change->has_action && !handle_action_before_change)
10569       ExecuteCustomElementAction(x, y, element, page);
10570   }
10571 }
10572
10573 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10574                                               int trigger_element,
10575                                               int trigger_event,
10576                                               int trigger_player,
10577                                               int trigger_side,
10578                                               int trigger_page)
10579 {
10580   boolean change_done_any = FALSE;
10581   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10582   int i;
10583
10584   if (!(trigger_events[trigger_element][trigger_event]))
10585     return FALSE;
10586
10587   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10588
10589   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10590   {
10591     int element = EL_CUSTOM_START + i;
10592     boolean change_done = FALSE;
10593     int p;
10594
10595     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10596         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10597       continue;
10598
10599     for (p = 0; p < element_info[element].num_change_pages; p++)
10600     {
10601       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10602
10603       if (change->can_change_or_has_action &&
10604           change->has_event[trigger_event] &&
10605           change->trigger_side & trigger_side &&
10606           change->trigger_player & trigger_player &&
10607           change->trigger_page & trigger_page_bits &&
10608           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10609       {
10610         change->actual_trigger_element = trigger_element;
10611         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10612         change->actual_trigger_player_bits = trigger_player;
10613         change->actual_trigger_side = trigger_side;
10614         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10615         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10616
10617         if ((change->can_change && !change_done) || change->has_action)
10618         {
10619           int x, y;
10620
10621           SCAN_PLAYFIELD(x, y)
10622           {
10623             if (Feld[x][y] == element)
10624             {
10625               if (change->can_change && !change_done)
10626               {
10627                 /* if element already changed in this frame, not only prevent
10628                    another element change (checked in ChangeElement()), but
10629                    also prevent additional element actions for this element */
10630
10631                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10632                     !level.use_action_after_change_bug)
10633                   continue;
10634
10635                 ChangeDelay[x][y] = 1;
10636                 ChangeEvent[x][y] = trigger_event;
10637
10638                 HandleElementChange(x, y, p);
10639               }
10640               else if (change->has_action)
10641               {
10642                 /* if element already changed in this frame, not only prevent
10643                    another element change (checked in ChangeElement()), but
10644                    also prevent additional element actions for this element */
10645
10646                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10647                     !level.use_action_after_change_bug)
10648                   continue;
10649
10650                 ExecuteCustomElementAction(x, y, element, p);
10651                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10652               }
10653             }
10654           }
10655
10656           if (change->can_change)
10657           {
10658             change_done = TRUE;
10659             change_done_any = TRUE;
10660           }
10661         }
10662       }
10663     }
10664   }
10665
10666   RECURSION_LOOP_DETECTION_END();
10667
10668   return change_done_any;
10669 }
10670
10671 static boolean CheckElementChangeExt(int x, int y,
10672                                      int element,
10673                                      int trigger_element,
10674                                      int trigger_event,
10675                                      int trigger_player,
10676                                      int trigger_side)
10677 {
10678   boolean change_done = FALSE;
10679   int p;
10680
10681   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10682       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10683     return FALSE;
10684
10685   if (Feld[x][y] == EL_BLOCKED)
10686   {
10687     Blocked2Moving(x, y, &x, &y);
10688     element = Feld[x][y];
10689   }
10690
10691   /* check if element has already changed or is about to change after moving */
10692   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10693        Feld[x][y] != element) ||
10694
10695       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10696        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10697         ChangePage[x][y] != -1)))
10698     return FALSE;
10699
10700   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10701
10702   for (p = 0; p < element_info[element].num_change_pages; p++)
10703   {
10704     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10705
10706     /* check trigger element for all events where the element that is checked
10707        for changing interacts with a directly adjacent element -- this is
10708        different to element changes that affect other elements to change on the
10709        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10710     boolean check_trigger_element =
10711       (trigger_event == CE_TOUCHING_X ||
10712        trigger_event == CE_HITTING_X ||
10713        trigger_event == CE_HIT_BY_X ||
10714        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10715
10716     if (change->can_change_or_has_action &&
10717         change->has_event[trigger_event] &&
10718         change->trigger_side & trigger_side &&
10719         change->trigger_player & trigger_player &&
10720         (!check_trigger_element ||
10721          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10722     {
10723       change->actual_trigger_element = trigger_element;
10724       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10725       change->actual_trigger_player_bits = trigger_player;
10726       change->actual_trigger_side = trigger_side;
10727       change->actual_trigger_ce_value = CustomValue[x][y];
10728       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10729
10730       /* special case: trigger element not at (x,y) position for some events */
10731       if (check_trigger_element)
10732       {
10733         static struct
10734         {
10735           int dx, dy;
10736         } move_xy[] =
10737           {
10738             {  0,  0 },
10739             { -1,  0 },
10740             { +1,  0 },
10741             {  0,  0 },
10742             {  0, -1 },
10743             {  0,  0 }, { 0, 0 }, { 0, 0 },
10744             {  0, +1 }
10745           };
10746
10747         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10748         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10749
10750         change->actual_trigger_ce_value = CustomValue[xx][yy];
10751         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10752       }
10753
10754       if (change->can_change && !change_done)
10755       {
10756         ChangeDelay[x][y] = 1;
10757         ChangeEvent[x][y] = trigger_event;
10758
10759         HandleElementChange(x, y, p);
10760
10761         change_done = TRUE;
10762       }
10763       else if (change->has_action)
10764       {
10765         ExecuteCustomElementAction(x, y, element, p);
10766         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10767       }
10768     }
10769   }
10770
10771   RECURSION_LOOP_DETECTION_END();
10772
10773   return change_done;
10774 }
10775
10776 static void PlayPlayerSound(struct PlayerInfo *player)
10777 {
10778   int jx = player->jx, jy = player->jy;
10779   int sound_element = player->artwork_element;
10780   int last_action = player->last_action_waiting;
10781   int action = player->action_waiting;
10782
10783   if (player->is_waiting)
10784   {
10785     if (action != last_action)
10786       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10787     else
10788       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10789   }
10790   else
10791   {
10792     if (action != last_action)
10793       StopSound(element_info[sound_element].sound[last_action]);
10794
10795     if (last_action == ACTION_SLEEPING)
10796       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10797   }
10798 }
10799
10800 static void PlayAllPlayersSound()
10801 {
10802   int i;
10803
10804   for (i = 0; i < MAX_PLAYERS; i++)
10805     if (stored_player[i].active)
10806       PlayPlayerSound(&stored_player[i]);
10807 }
10808
10809 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10810 {
10811   boolean last_waiting = player->is_waiting;
10812   int move_dir = player->MovDir;
10813
10814   player->dir_waiting = move_dir;
10815   player->last_action_waiting = player->action_waiting;
10816
10817   if (is_waiting)
10818   {
10819     if (!last_waiting)          /* not waiting -> waiting */
10820     {
10821       player->is_waiting = TRUE;
10822
10823       player->frame_counter_bored =
10824         FrameCounter +
10825         game.player_boring_delay_fixed +
10826         GetSimpleRandom(game.player_boring_delay_random);
10827       player->frame_counter_sleeping =
10828         FrameCounter +
10829         game.player_sleeping_delay_fixed +
10830         GetSimpleRandom(game.player_sleeping_delay_random);
10831
10832       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10833     }
10834
10835     if (game.player_sleeping_delay_fixed +
10836         game.player_sleeping_delay_random > 0 &&
10837         player->anim_delay_counter == 0 &&
10838         player->post_delay_counter == 0 &&
10839         FrameCounter >= player->frame_counter_sleeping)
10840       player->is_sleeping = TRUE;
10841     else if (game.player_boring_delay_fixed +
10842              game.player_boring_delay_random > 0 &&
10843              FrameCounter >= player->frame_counter_bored)
10844       player->is_bored = TRUE;
10845
10846     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10847                               player->is_bored ? ACTION_BORING :
10848                               ACTION_WAITING);
10849
10850     if (player->is_sleeping && player->use_murphy)
10851     {
10852       /* special case for sleeping Murphy when leaning against non-free tile */
10853
10854       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10855           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10856            !IS_MOVING(player->jx - 1, player->jy)))
10857         move_dir = MV_LEFT;
10858       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10859                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10860                 !IS_MOVING(player->jx + 1, player->jy)))
10861         move_dir = MV_RIGHT;
10862       else
10863         player->is_sleeping = FALSE;
10864
10865       player->dir_waiting = move_dir;
10866     }
10867
10868     if (player->is_sleeping)
10869     {
10870       if (player->num_special_action_sleeping > 0)
10871       {
10872         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10873         {
10874           int last_special_action = player->special_action_sleeping;
10875           int num_special_action = player->num_special_action_sleeping;
10876           int special_action =
10877             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10878              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10879              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10880              last_special_action + 1 : ACTION_SLEEPING);
10881           int special_graphic =
10882             el_act_dir2img(player->artwork_element, special_action, move_dir);
10883
10884           player->anim_delay_counter =
10885             graphic_info[special_graphic].anim_delay_fixed +
10886             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10887           player->post_delay_counter =
10888             graphic_info[special_graphic].post_delay_fixed +
10889             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10890
10891           player->special_action_sleeping = special_action;
10892         }
10893
10894         if (player->anim_delay_counter > 0)
10895         {
10896           player->action_waiting = player->special_action_sleeping;
10897           player->anim_delay_counter--;
10898         }
10899         else if (player->post_delay_counter > 0)
10900         {
10901           player->post_delay_counter--;
10902         }
10903       }
10904     }
10905     else if (player->is_bored)
10906     {
10907       if (player->num_special_action_bored > 0)
10908       {
10909         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10910         {
10911           int special_action =
10912             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10913           int special_graphic =
10914             el_act_dir2img(player->artwork_element, special_action, move_dir);
10915
10916           player->anim_delay_counter =
10917             graphic_info[special_graphic].anim_delay_fixed +
10918             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10919           player->post_delay_counter =
10920             graphic_info[special_graphic].post_delay_fixed +
10921             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10922
10923           player->special_action_bored = special_action;
10924         }
10925
10926         if (player->anim_delay_counter > 0)
10927         {
10928           player->action_waiting = player->special_action_bored;
10929           player->anim_delay_counter--;
10930         }
10931         else if (player->post_delay_counter > 0)
10932         {
10933           player->post_delay_counter--;
10934         }
10935       }
10936     }
10937   }
10938   else if (last_waiting)        /* waiting -> not waiting */
10939   {
10940     player->is_waiting = FALSE;
10941     player->is_bored = FALSE;
10942     player->is_sleeping = FALSE;
10943
10944     player->frame_counter_bored = -1;
10945     player->frame_counter_sleeping = -1;
10946
10947     player->anim_delay_counter = 0;
10948     player->post_delay_counter = 0;
10949
10950     player->dir_waiting = player->MovDir;
10951     player->action_waiting = ACTION_DEFAULT;
10952
10953     player->special_action_bored = ACTION_DEFAULT;
10954     player->special_action_sleeping = ACTION_DEFAULT;
10955   }
10956 }
10957
10958 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10959 {
10960   if ((!player->is_moving  && player->was_moving) ||
10961       (player->MovPos == 0 && player->was_moving) ||
10962       (player->is_snapping && !player->was_snapping) ||
10963       (player->is_dropping && !player->was_dropping))
10964   {
10965     if (!CheckSaveEngineSnapshotToList())
10966       return;
10967
10968     player->was_moving = FALSE;
10969     player->was_snapping = TRUE;
10970     player->was_dropping = TRUE;
10971   }
10972   else
10973   {
10974     if (player->is_moving)
10975       player->was_moving = TRUE;
10976
10977     if (!player->is_snapping)
10978       player->was_snapping = FALSE;
10979
10980     if (!player->is_dropping)
10981       player->was_dropping = FALSE;
10982   }
10983 }
10984
10985 static void CheckSingleStepMode(struct PlayerInfo *player)
10986 {
10987   if (tape.single_step && tape.recording && !tape.pausing)
10988   {
10989     /* as it is called "single step mode", just return to pause mode when the
10990        player stopped moving after one tile (or never starts moving at all) */
10991     if (!player->is_moving &&
10992         !player->is_pushing &&
10993         !player->is_dropping_pressed)
10994     {
10995       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10996       SnapField(player, 0, 0);                  /* stop snapping */
10997     }
10998   }
10999
11000   CheckSaveEngineSnapshot(player);
11001 }
11002
11003 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11004 {
11005   int left      = player_action & JOY_LEFT;
11006   int right     = player_action & JOY_RIGHT;
11007   int up        = player_action & JOY_UP;
11008   int down      = player_action & JOY_DOWN;
11009   int button1   = player_action & JOY_BUTTON_1;
11010   int button2   = player_action & JOY_BUTTON_2;
11011   int dx        = (left ? -1 : right ? 1 : 0);
11012   int dy        = (up   ? -1 : down  ? 1 : 0);
11013
11014   if (!player->active || tape.pausing)
11015     return 0;
11016
11017   if (player_action)
11018   {
11019     if (button1)
11020       SnapField(player, dx, dy);
11021     else
11022     {
11023       if (button2)
11024         DropElement(player);
11025
11026       MovePlayer(player, dx, dy);
11027     }
11028
11029     CheckSingleStepMode(player);
11030
11031     SetPlayerWaiting(player, FALSE);
11032
11033     return player_action;
11034   }
11035   else
11036   {
11037     /* no actions for this player (no input at player's configured device) */
11038
11039     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11040     SnapField(player, 0, 0);
11041     CheckGravityMovementWhenNotMoving(player);
11042
11043     if (player->MovPos == 0)
11044       SetPlayerWaiting(player, TRUE);
11045
11046     if (player->MovPos == 0)    /* needed for tape.playing */
11047       player->is_moving = FALSE;
11048
11049     player->is_dropping = FALSE;
11050     player->is_dropping_pressed = FALSE;
11051     player->drop_pressed_delay = 0;
11052
11053     CheckSingleStepMode(player);
11054
11055     return 0;
11056   }
11057 }
11058
11059 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11060                                          byte *tape_action)
11061 {
11062   if (!tape.use_mouse)
11063     return;
11064
11065   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11066   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11067   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11068 }
11069
11070 static void SetTapeActionFromMouseAction(byte *tape_action,
11071                                          struct MouseActionInfo *mouse_action)
11072 {
11073   if (!tape.use_mouse)
11074     return;
11075
11076   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11077   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11078   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11079 }
11080
11081 static void CheckLevelTime()
11082 {
11083   int i;
11084
11085   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11086   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11087   {
11088     if (level.native_em_level->lev->home == 0)  /* all players at home */
11089     {
11090       PlayerWins(local_player);
11091
11092       AllPlayersGone = TRUE;
11093
11094       level.native_em_level->lev->home = -1;
11095     }
11096
11097     if (level.native_em_level->ply[0]->alive == 0 &&
11098         level.native_em_level->ply[1]->alive == 0 &&
11099         level.native_em_level->ply[2]->alive == 0 &&
11100         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11101       AllPlayersGone = TRUE;
11102   }
11103   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11104   {
11105     if (game_sp.LevelSolved &&
11106         !game_sp.GameOver)                              /* game won */
11107     {
11108       PlayerWins(local_player);
11109
11110       game_sp.GameOver = TRUE;
11111
11112       AllPlayersGone = TRUE;
11113     }
11114
11115     if (game_sp.GameOver)                               /* game lost */
11116       AllPlayersGone = TRUE;
11117   }
11118   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11119   {
11120     if (game_mm.level_solved &&
11121         !game_mm.game_over)                             /* game won */
11122     {
11123       PlayerWins(local_player);
11124
11125       game_mm.game_over = TRUE;
11126
11127       AllPlayersGone = TRUE;
11128     }
11129
11130     if (game_mm.game_over)                              /* game lost */
11131       AllPlayersGone = TRUE;
11132   }
11133
11134   if (TimeFrames >= FRAMES_PER_SECOND)
11135   {
11136     TimeFrames = 0;
11137     TapeTime++;
11138
11139     for (i = 0; i < MAX_PLAYERS; i++)
11140     {
11141       struct PlayerInfo *player = &stored_player[i];
11142
11143       if (SHIELD_ON(player))
11144       {
11145         player->shield_normal_time_left--;
11146
11147         if (player->shield_deadly_time_left > 0)
11148           player->shield_deadly_time_left--;
11149       }
11150     }
11151
11152     if (!local_player->LevelSolved && !level.use_step_counter)
11153     {
11154       TimePlayed++;
11155
11156       if (TimeLeft > 0)
11157       {
11158         TimeLeft--;
11159
11160         if (TimeLeft <= 10 && setup.time_limit)
11161           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11162
11163         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11164            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11165
11166         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11167
11168         if (!TimeLeft && setup.time_limit)
11169         {
11170           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11171             level.native_em_level->lev->killed_out_of_time = TRUE;
11172           else
11173             for (i = 0; i < MAX_PLAYERS; i++)
11174               KillPlayer(&stored_player[i]);
11175         }
11176       }
11177       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11178       {
11179         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11180       }
11181
11182       level.native_em_level->lev->time =
11183         (game.no_time_limit ? TimePlayed : TimeLeft);
11184     }
11185
11186     if (tape.recording || tape.playing)
11187       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11188   }
11189
11190   if (tape.recording || tape.playing)
11191     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11192
11193   UpdateAndDisplayGameControlValues();
11194 }
11195
11196 void AdvanceFrameAndPlayerCounters(int player_nr)
11197 {
11198   int i;
11199
11200   /* advance frame counters (global frame counter and time frame counter) */
11201   FrameCounter++;
11202   TimeFrames++;
11203
11204   /* advance player counters (counters for move delay, move animation etc.) */
11205   for (i = 0; i < MAX_PLAYERS; i++)
11206   {
11207     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11208     int move_delay_value = stored_player[i].move_delay_value;
11209     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11210
11211     if (!advance_player_counters)       /* not all players may be affected */
11212       continue;
11213
11214     if (move_frames == 0)       /* less than one move per game frame */
11215     {
11216       int stepsize = TILEX / move_delay_value;
11217       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11218       int count = (stored_player[i].is_moving ?
11219                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11220
11221       if (count % delay == 0)
11222         move_frames = 1;
11223     }
11224
11225     stored_player[i].Frame += move_frames;
11226
11227     if (stored_player[i].MovPos != 0)
11228       stored_player[i].StepFrame += move_frames;
11229
11230     if (stored_player[i].move_delay > 0)
11231       stored_player[i].move_delay--;
11232
11233     /* due to bugs in previous versions, counter must count up, not down */
11234     if (stored_player[i].push_delay != -1)
11235       stored_player[i].push_delay++;
11236
11237     if (stored_player[i].drop_delay > 0)
11238       stored_player[i].drop_delay--;
11239
11240     if (stored_player[i].is_dropping_pressed)
11241       stored_player[i].drop_pressed_delay++;
11242   }
11243 }
11244
11245 void StartGameActions(boolean init_network_game, boolean record_tape,
11246                       int random_seed)
11247 {
11248   unsigned int new_random_seed = InitRND(random_seed);
11249
11250   if (record_tape)
11251     TapeStartRecording(new_random_seed);
11252
11253 #if defined(NETWORK_AVALIABLE)
11254   if (init_network_game)
11255   {
11256     SendToServer_StartPlaying();
11257
11258     return;
11259   }
11260 #endif
11261
11262   InitGame();
11263 }
11264
11265 void GameActionsExt()
11266 {
11267 #if 0
11268   static unsigned int game_frame_delay = 0;
11269 #endif
11270   unsigned int game_frame_delay_value;
11271   byte *recorded_player_action;
11272   byte summarized_player_action = 0;
11273   byte tape_action[MAX_PLAYERS];
11274   int i;
11275
11276   /* detect endless loops, caused by custom element programming */
11277   if (recursion_loop_detected && recursion_loop_depth == 0)
11278   {
11279     char *message = getStringCat3("Internal Error! Element ",
11280                                   EL_NAME(recursion_loop_element),
11281                                   " caused endless loop! Quit the game?");
11282
11283     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11284           EL_NAME(recursion_loop_element));
11285
11286     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11287
11288     recursion_loop_detected = FALSE;    /* if game should be continued */
11289
11290     free(message);
11291
11292     return;
11293   }
11294
11295   if (game.restart_level)
11296     StartGameActions(options.network, setup.autorecord, level.random_seed);
11297
11298   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11299   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11300   {
11301     if (level.native_em_level->lev->home == 0)  /* all players at home */
11302     {
11303       PlayerWins(local_player);
11304
11305       AllPlayersGone = TRUE;
11306
11307       level.native_em_level->lev->home = -1;
11308     }
11309
11310     if (level.native_em_level->ply[0]->alive == 0 &&
11311         level.native_em_level->ply[1]->alive == 0 &&
11312         level.native_em_level->ply[2]->alive == 0 &&
11313         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11314       AllPlayersGone = TRUE;
11315   }
11316   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11317   {
11318     if (game_sp.LevelSolved &&
11319         !game_sp.GameOver)                              /* game won */
11320     {
11321       PlayerWins(local_player);
11322
11323       game_sp.GameOver = TRUE;
11324
11325       AllPlayersGone = TRUE;
11326     }
11327
11328     if (game_sp.GameOver)                               /* game lost */
11329       AllPlayersGone = TRUE;
11330   }
11331   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11332   {
11333     if (game_mm.level_solved &&
11334         !game_mm.game_over)                             /* game won */
11335     {
11336       PlayerWins(local_player);
11337
11338       game_mm.game_over = TRUE;
11339
11340       AllPlayersGone = TRUE;
11341     }
11342
11343     if (game_mm.game_over)                              /* game lost */
11344       AllPlayersGone = TRUE;
11345   }
11346
11347   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11348     GameWon();
11349
11350   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11351     TapeStop();
11352
11353   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11354     return;
11355
11356   game_frame_delay_value =
11357     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11358
11359   if (tape.playing && tape.warp_forward && !tape.pausing)
11360     game_frame_delay_value = 0;
11361
11362   SetVideoFrameDelay(game_frame_delay_value);
11363
11364 #if 0
11365 #if 0
11366   /* ---------- main game synchronization point ---------- */
11367
11368   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11369
11370   printf("::: skip == %d\n", skip);
11371
11372 #else
11373   /* ---------- main game synchronization point ---------- */
11374
11375   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11376 #endif
11377 #endif
11378
11379   if (network_playing && !network_player_action_received)
11380   {
11381     /* try to get network player actions in time */
11382
11383 #if defined(NETWORK_AVALIABLE)
11384     /* last chance to get network player actions without main loop delay */
11385     HandleNetworking();
11386 #endif
11387
11388     /* game was quit by network peer */
11389     if (game_status != GAME_MODE_PLAYING)
11390       return;
11391
11392     if (!network_player_action_received)
11393       return;           /* failed to get network player actions in time */
11394
11395     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11396   }
11397
11398   if (tape.pausing)
11399     return;
11400
11401   /* at this point we know that we really continue executing the game */
11402
11403   network_player_action_received = FALSE;
11404
11405   /* when playing tape, read previously recorded player input from tape data */
11406   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11407
11408   local_player->effective_mouse_action = local_player->mouse_action;
11409
11410   if (recorded_player_action != NULL)
11411     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11412                                  recorded_player_action);
11413
11414   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11415   if (tape.pausing)
11416     return;
11417
11418   if (tape.set_centered_player)
11419   {
11420     game.centered_player_nr_next = tape.centered_player_nr_next;
11421     game.set_centered_player = TRUE;
11422   }
11423
11424   for (i = 0; i < MAX_PLAYERS; i++)
11425   {
11426     summarized_player_action |= stored_player[i].action;
11427
11428     if (!network_playing && (game.team_mode || tape.playing))
11429       stored_player[i].effective_action = stored_player[i].action;
11430   }
11431
11432 #if defined(NETWORK_AVALIABLE)
11433   if (network_playing)
11434     SendToServer_MovePlayer(summarized_player_action);
11435 #endif
11436
11437   // summarize all actions at local players mapped input device position
11438   // (this allows using different input devices in single player mode)
11439   if (!options.network && !game.team_mode)
11440     stored_player[map_player_action[local_player->index_nr]].effective_action =
11441       summarized_player_action;
11442
11443   if (tape.recording &&
11444       setup.team_mode &&
11445       setup.input_on_focus &&
11446       game.centered_player_nr != -1)
11447   {
11448     for (i = 0; i < MAX_PLAYERS; i++)
11449       stored_player[i].effective_action =
11450         (i == game.centered_player_nr ? summarized_player_action : 0);
11451   }
11452
11453   if (recorded_player_action != NULL)
11454     for (i = 0; i < MAX_PLAYERS; i++)
11455       stored_player[i].effective_action = recorded_player_action[i];
11456
11457   for (i = 0; i < MAX_PLAYERS; i++)
11458   {
11459     tape_action[i] = stored_player[i].effective_action;
11460
11461     /* (this may happen in the RND game engine if a player was not present on
11462        the playfield on level start, but appeared later from a custom element */
11463     if (setup.team_mode &&
11464         tape.recording &&
11465         tape_action[i] &&
11466         !tape.player_participates[i])
11467       tape.player_participates[i] = TRUE;
11468   }
11469
11470   SetTapeActionFromMouseAction(tape_action,
11471                                &local_player->effective_mouse_action);
11472
11473   /* only record actions from input devices, but not programmed actions */
11474   if (tape.recording)
11475     TapeRecordAction(tape_action);
11476
11477 #if USE_NEW_PLAYER_ASSIGNMENTS
11478   // !!! also map player actions in single player mode !!!
11479   // if (game.team_mode)
11480   if (1)
11481   {
11482     byte mapped_action[MAX_PLAYERS];
11483
11484 #if DEBUG_PLAYER_ACTIONS
11485     printf(":::");
11486     for (i = 0; i < MAX_PLAYERS; i++)
11487       printf(" %d, ", stored_player[i].effective_action);
11488 #endif
11489
11490     for (i = 0; i < MAX_PLAYERS; i++)
11491       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11492
11493     for (i = 0; i < MAX_PLAYERS; i++)
11494       stored_player[i].effective_action = mapped_action[i];
11495
11496 #if DEBUG_PLAYER_ACTIONS
11497     printf(" =>");
11498     for (i = 0; i < MAX_PLAYERS; i++)
11499       printf(" %d, ", stored_player[i].effective_action);
11500     printf("\n");
11501 #endif
11502   }
11503 #if DEBUG_PLAYER_ACTIONS
11504   else
11505   {
11506     printf(":::");
11507     for (i = 0; i < MAX_PLAYERS; i++)
11508       printf(" %d, ", stored_player[i].effective_action);
11509     printf("\n");
11510   }
11511 #endif
11512 #endif
11513
11514   for (i = 0; i < MAX_PLAYERS; i++)
11515   {
11516     // allow engine snapshot in case of changed movement attempt
11517     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11518         (stored_player[i].effective_action & KEY_MOTION))
11519       game.snapshot.changed_action = TRUE;
11520
11521     // allow engine snapshot in case of snapping/dropping attempt
11522     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11523         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11524       game.snapshot.changed_action = TRUE;
11525
11526     game.snapshot.last_action[i] = stored_player[i].effective_action;
11527   }
11528
11529   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11530   {
11531     GameActions_EM_Main();
11532   }
11533   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11534   {
11535     GameActions_SP_Main();
11536   }
11537   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11538   {
11539     GameActions_MM_Main();
11540   }
11541   else
11542   {
11543     GameActions_RND_Main();
11544   }
11545
11546   BlitScreenToBitmap(backbuffer);
11547
11548   CheckLevelTime();
11549
11550   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11551
11552   if (global.show_frames_per_second)
11553   {
11554     static unsigned int fps_counter = 0;
11555     static int fps_frames = 0;
11556     unsigned int fps_delay_ms = Counter() - fps_counter;
11557
11558     fps_frames++;
11559
11560     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11561     {
11562       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11563
11564       fps_frames = 0;
11565       fps_counter = Counter();
11566
11567       /* always draw FPS to screen after FPS value was updated */
11568       redraw_mask |= REDRAW_FPS;
11569     }
11570
11571     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11572     if (GetDrawDeactivationMask() == REDRAW_NONE)
11573       redraw_mask |= REDRAW_FPS;
11574   }
11575 }
11576
11577 static void GameActions_CheckSaveEngineSnapshot()
11578 {
11579   if (!game.snapshot.save_snapshot)
11580     return;
11581
11582   // clear flag for saving snapshot _before_ saving snapshot
11583   game.snapshot.save_snapshot = FALSE;
11584
11585   SaveEngineSnapshotToList();
11586 }
11587
11588 void GameActions()
11589 {
11590   GameActionsExt();
11591
11592   GameActions_CheckSaveEngineSnapshot();
11593 }
11594
11595 void GameActions_EM_Main()
11596 {
11597   byte effective_action[MAX_PLAYERS];
11598   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11599   int i;
11600
11601   for (i = 0; i < MAX_PLAYERS; i++)
11602     effective_action[i] = stored_player[i].effective_action;
11603
11604   GameActions_EM(effective_action, warp_mode);
11605 }
11606
11607 void GameActions_SP_Main()
11608 {
11609   byte effective_action[MAX_PLAYERS];
11610   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11611   int i;
11612
11613   for (i = 0; i < MAX_PLAYERS; i++)
11614     effective_action[i] = stored_player[i].effective_action;
11615
11616   GameActions_SP(effective_action, warp_mode);
11617
11618   for (i = 0; i < MAX_PLAYERS; i++)
11619   {
11620     if (stored_player[i].force_dropping)
11621       stored_player[i].action |= KEY_BUTTON_DROP;
11622
11623     stored_player[i].force_dropping = FALSE;
11624   }
11625 }
11626
11627 void GameActions_MM_Main()
11628 {
11629   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11630
11631   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11632 }
11633
11634 void GameActions_RND_Main()
11635 {
11636   GameActions_RND();
11637 }
11638
11639 void GameActions_RND()
11640 {
11641   int magic_wall_x = 0, magic_wall_y = 0;
11642   int i, x, y, element, graphic, last_gfx_frame;
11643
11644   InitPlayfieldScanModeVars();
11645
11646   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11647   {
11648     SCAN_PLAYFIELD(x, y)
11649     {
11650       ChangeCount[x][y] = 0;
11651       ChangeEvent[x][y] = -1;
11652     }
11653   }
11654
11655   if (game.set_centered_player)
11656   {
11657     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11658
11659     /* switching to "all players" only possible if all players fit to screen */
11660     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11661     {
11662       game.centered_player_nr_next = game.centered_player_nr;
11663       game.set_centered_player = FALSE;
11664     }
11665
11666     /* do not switch focus to non-existing (or non-active) player */
11667     if (game.centered_player_nr_next >= 0 &&
11668         !stored_player[game.centered_player_nr_next].active)
11669     {
11670       game.centered_player_nr_next = game.centered_player_nr;
11671       game.set_centered_player = FALSE;
11672     }
11673   }
11674
11675   if (game.set_centered_player &&
11676       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11677   {
11678     int sx, sy;
11679
11680     if (game.centered_player_nr_next == -1)
11681     {
11682       setScreenCenteredToAllPlayers(&sx, &sy);
11683     }
11684     else
11685     {
11686       sx = stored_player[game.centered_player_nr_next].jx;
11687       sy = stored_player[game.centered_player_nr_next].jy;
11688     }
11689
11690     game.centered_player_nr = game.centered_player_nr_next;
11691     game.set_centered_player = FALSE;
11692
11693     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11694     DrawGameDoorValues();
11695   }
11696
11697   for (i = 0; i < MAX_PLAYERS; i++)
11698   {
11699     int actual_player_action = stored_player[i].effective_action;
11700
11701 #if 1
11702     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11703        - rnd_equinox_tetrachloride 048
11704        - rnd_equinox_tetrachloride_ii 096
11705        - rnd_emanuel_schmieg 002
11706        - doctor_sloan_ww 001, 020
11707     */
11708     if (stored_player[i].MovPos == 0)
11709       CheckGravityMovement(&stored_player[i]);
11710 #endif
11711
11712     /* overwrite programmed action with tape action */
11713     if (stored_player[i].programmed_action)
11714       actual_player_action = stored_player[i].programmed_action;
11715
11716     PlayerActions(&stored_player[i], actual_player_action);
11717
11718     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11719   }
11720
11721   ScrollScreen(NULL, SCROLL_GO_ON);
11722
11723   /* for backwards compatibility, the following code emulates a fixed bug that
11724      occured when pushing elements (causing elements that just made their last
11725      pushing step to already (if possible) make their first falling step in the
11726      same game frame, which is bad); this code is also needed to use the famous
11727      "spring push bug" which is used in older levels and might be wanted to be
11728      used also in newer levels, but in this case the buggy pushing code is only
11729      affecting the "spring" element and no other elements */
11730
11731   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11732   {
11733     for (i = 0; i < MAX_PLAYERS; i++)
11734     {
11735       struct PlayerInfo *player = &stored_player[i];
11736       int x = player->jx;
11737       int y = player->jy;
11738
11739       if (player->active && player->is_pushing && player->is_moving &&
11740           IS_MOVING(x, y) &&
11741           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11742            Feld[x][y] == EL_SPRING))
11743       {
11744         ContinueMoving(x, y);
11745
11746         /* continue moving after pushing (this is actually a bug) */
11747         if (!IS_MOVING(x, y))
11748           Stop[x][y] = FALSE;
11749       }
11750     }
11751   }
11752
11753   SCAN_PLAYFIELD(x, y)
11754   {
11755     ChangeCount[x][y] = 0;
11756     ChangeEvent[x][y] = -1;
11757
11758     /* this must be handled before main playfield loop */
11759     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11760     {
11761       MovDelay[x][y]--;
11762       if (MovDelay[x][y] <= 0)
11763         RemoveField(x, y);
11764     }
11765
11766     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11767     {
11768       MovDelay[x][y]--;
11769       if (MovDelay[x][y] <= 0)
11770       {
11771         RemoveField(x, y);
11772         TEST_DrawLevelField(x, y);
11773
11774         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11775       }
11776     }
11777
11778 #if DEBUG
11779     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11780     {
11781       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11782       printf("GameActions(): This should never happen!\n");
11783
11784       ChangePage[x][y] = -1;
11785     }
11786 #endif
11787
11788     Stop[x][y] = FALSE;
11789     if (WasJustMoving[x][y] > 0)
11790       WasJustMoving[x][y]--;
11791     if (WasJustFalling[x][y] > 0)
11792       WasJustFalling[x][y]--;
11793     if (CheckCollision[x][y] > 0)
11794       CheckCollision[x][y]--;
11795     if (CheckImpact[x][y] > 0)
11796       CheckImpact[x][y]--;
11797
11798     GfxFrame[x][y]++;
11799
11800     /* reset finished pushing action (not done in ContinueMoving() to allow
11801        continuous pushing animation for elements with zero push delay) */
11802     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11803     {
11804       ResetGfxAnimation(x, y);
11805       TEST_DrawLevelField(x, y);
11806     }
11807
11808 #if DEBUG
11809     if (IS_BLOCKED(x, y))
11810     {
11811       int oldx, oldy;
11812
11813       Blocked2Moving(x, y, &oldx, &oldy);
11814       if (!IS_MOVING(oldx, oldy))
11815       {
11816         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11817         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11818         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11819         printf("GameActions(): This should never happen!\n");
11820       }
11821     }
11822 #endif
11823   }
11824
11825   SCAN_PLAYFIELD(x, y)
11826   {
11827     element = Feld[x][y];
11828     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11829     last_gfx_frame = GfxFrame[x][y];
11830
11831     ResetGfxFrame(x, y);
11832
11833     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11834       DrawLevelGraphicAnimation(x, y, graphic);
11835
11836     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11837         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11838       ResetRandomAnimationValue(x, y);
11839
11840     SetRandomAnimationValue(x, y);
11841
11842     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11843
11844     if (IS_INACTIVE(element))
11845     {
11846       if (IS_ANIMATED(graphic))
11847         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11848
11849       continue;
11850     }
11851
11852     /* this may take place after moving, so 'element' may have changed */
11853     if (IS_CHANGING(x, y) &&
11854         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11855     {
11856       int page = element_info[element].event_page_nr[CE_DELAY];
11857
11858       HandleElementChange(x, y, page);
11859
11860       element = Feld[x][y];
11861       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11862     }
11863
11864     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11865     {
11866       StartMoving(x, y);
11867
11868       element = Feld[x][y];
11869       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11870
11871       if (IS_ANIMATED(graphic) &&
11872           !IS_MOVING(x, y) &&
11873           !Stop[x][y])
11874         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11875
11876       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11877         TEST_DrawTwinkleOnField(x, y);
11878     }
11879     else if (element == EL_ACID)
11880     {
11881       if (!Stop[x][y])
11882         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11883     }
11884     else if ((element == EL_EXIT_OPEN ||
11885               element == EL_EM_EXIT_OPEN ||
11886               element == EL_SP_EXIT_OPEN ||
11887               element == EL_STEEL_EXIT_OPEN ||
11888               element == EL_EM_STEEL_EXIT_OPEN ||
11889               element == EL_SP_TERMINAL ||
11890               element == EL_SP_TERMINAL_ACTIVE ||
11891               element == EL_EXTRA_TIME ||
11892               element == EL_SHIELD_NORMAL ||
11893               element == EL_SHIELD_DEADLY) &&
11894              IS_ANIMATED(graphic))
11895       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11896     else if (IS_MOVING(x, y))
11897       ContinueMoving(x, y);
11898     else if (IS_ACTIVE_BOMB(element))
11899       CheckDynamite(x, y);
11900     else if (element == EL_AMOEBA_GROWING)
11901       AmoebeWaechst(x, y);
11902     else if (element == EL_AMOEBA_SHRINKING)
11903       AmoebaDisappearing(x, y);
11904
11905 #if !USE_NEW_AMOEBA_CODE
11906     else if (IS_AMOEBALIVE(element))
11907       AmoebeAbleger(x, y);
11908 #endif
11909
11910     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11911       Life(x, y);
11912     else if (element == EL_EXIT_CLOSED)
11913       CheckExit(x, y);
11914     else if (element == EL_EM_EXIT_CLOSED)
11915       CheckExitEM(x, y);
11916     else if (element == EL_STEEL_EXIT_CLOSED)
11917       CheckExitSteel(x, y);
11918     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11919       CheckExitSteelEM(x, y);
11920     else if (element == EL_SP_EXIT_CLOSED)
11921       CheckExitSP(x, y);
11922     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11923              element == EL_EXPANDABLE_STEELWALL_GROWING)
11924       MauerWaechst(x, y);
11925     else if (element == EL_EXPANDABLE_WALL ||
11926              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11927              element == EL_EXPANDABLE_WALL_VERTICAL ||
11928              element == EL_EXPANDABLE_WALL_ANY ||
11929              element == EL_BD_EXPANDABLE_WALL)
11930       MauerAbleger(x, y);
11931     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11932              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11933              element == EL_EXPANDABLE_STEELWALL_ANY)
11934       MauerAblegerStahl(x, y);
11935     else if (element == EL_FLAMES)
11936       CheckForDragon(x, y);
11937     else if (element == EL_EXPLOSION)
11938       ; /* drawing of correct explosion animation is handled separately */
11939     else if (element == EL_ELEMENT_SNAPPING ||
11940              element == EL_DIAGONAL_SHRINKING ||
11941              element == EL_DIAGONAL_GROWING)
11942     {
11943       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11944
11945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11946     }
11947     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11948       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11949
11950     if (IS_BELT_ACTIVE(element))
11951       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11952
11953     if (game.magic_wall_active)
11954     {
11955       int jx = local_player->jx, jy = local_player->jy;
11956
11957       /* play the element sound at the position nearest to the player */
11958       if ((element == EL_MAGIC_WALL_FULL ||
11959            element == EL_MAGIC_WALL_ACTIVE ||
11960            element == EL_MAGIC_WALL_EMPTYING ||
11961            element == EL_BD_MAGIC_WALL_FULL ||
11962            element == EL_BD_MAGIC_WALL_ACTIVE ||
11963            element == EL_BD_MAGIC_WALL_EMPTYING ||
11964            element == EL_DC_MAGIC_WALL_FULL ||
11965            element == EL_DC_MAGIC_WALL_ACTIVE ||
11966            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11967           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11968       {
11969         magic_wall_x = x;
11970         magic_wall_y = y;
11971       }
11972     }
11973   }
11974
11975 #if USE_NEW_AMOEBA_CODE
11976   /* new experimental amoeba growth stuff */
11977   if (!(FrameCounter % 8))
11978   {
11979     static unsigned int random = 1684108901;
11980
11981     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11982     {
11983       x = RND(lev_fieldx);
11984       y = RND(lev_fieldy);
11985       element = Feld[x][y];
11986
11987       if (!IS_PLAYER(x,y) &&
11988           (element == EL_EMPTY ||
11989            CAN_GROW_INTO(element) ||
11990            element == EL_QUICKSAND_EMPTY ||
11991            element == EL_QUICKSAND_FAST_EMPTY ||
11992            element == EL_ACID_SPLASH_LEFT ||
11993            element == EL_ACID_SPLASH_RIGHT))
11994       {
11995         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11996             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11997             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11998             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11999           Feld[x][y] = EL_AMOEBA_DROP;
12000       }
12001
12002       random = random * 129 + 1;
12003     }
12004   }
12005 #endif
12006
12007   game.explosions_delayed = FALSE;
12008
12009   SCAN_PLAYFIELD(x, y)
12010   {
12011     element = Feld[x][y];
12012
12013     if (ExplodeField[x][y])
12014       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12015     else if (element == EL_EXPLOSION)
12016       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12017
12018     ExplodeField[x][y] = EX_TYPE_NONE;
12019   }
12020
12021   game.explosions_delayed = TRUE;
12022
12023   if (game.magic_wall_active)
12024   {
12025     if (!(game.magic_wall_time_left % 4))
12026     {
12027       int element = Feld[magic_wall_x][magic_wall_y];
12028
12029       if (element == EL_BD_MAGIC_WALL_FULL ||
12030           element == EL_BD_MAGIC_WALL_ACTIVE ||
12031           element == EL_BD_MAGIC_WALL_EMPTYING)
12032         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12033       else if (element == EL_DC_MAGIC_WALL_FULL ||
12034                element == EL_DC_MAGIC_WALL_ACTIVE ||
12035                element == EL_DC_MAGIC_WALL_EMPTYING)
12036         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12037       else
12038         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12039     }
12040
12041     if (game.magic_wall_time_left > 0)
12042     {
12043       game.magic_wall_time_left--;
12044
12045       if (!game.magic_wall_time_left)
12046       {
12047         SCAN_PLAYFIELD(x, y)
12048         {
12049           element = Feld[x][y];
12050
12051           if (element == EL_MAGIC_WALL_ACTIVE ||
12052               element == EL_MAGIC_WALL_FULL)
12053           {
12054             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12055             TEST_DrawLevelField(x, y);
12056           }
12057           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12058                    element == EL_BD_MAGIC_WALL_FULL)
12059           {
12060             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12061             TEST_DrawLevelField(x, y);
12062           }
12063           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12064                    element == EL_DC_MAGIC_WALL_FULL)
12065           {
12066             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12067             TEST_DrawLevelField(x, y);
12068           }
12069         }
12070
12071         game.magic_wall_active = FALSE;
12072       }
12073     }
12074   }
12075
12076   if (game.light_time_left > 0)
12077   {
12078     game.light_time_left--;
12079
12080     if (game.light_time_left == 0)
12081       RedrawAllLightSwitchesAndInvisibleElements();
12082   }
12083
12084   if (game.timegate_time_left > 0)
12085   {
12086     game.timegate_time_left--;
12087
12088     if (game.timegate_time_left == 0)
12089       CloseAllOpenTimegates();
12090   }
12091
12092   if (game.lenses_time_left > 0)
12093   {
12094     game.lenses_time_left--;
12095
12096     if (game.lenses_time_left == 0)
12097       RedrawAllInvisibleElementsForLenses();
12098   }
12099
12100   if (game.magnify_time_left > 0)
12101   {
12102     game.magnify_time_left--;
12103
12104     if (game.magnify_time_left == 0)
12105       RedrawAllInvisibleElementsForMagnifier();
12106   }
12107
12108   for (i = 0; i < MAX_PLAYERS; i++)
12109   {
12110     struct PlayerInfo *player = &stored_player[i];
12111
12112     if (SHIELD_ON(player))
12113     {
12114       if (player->shield_deadly_time_left)
12115         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12116       else if (player->shield_normal_time_left)
12117         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12118     }
12119   }
12120
12121 #if USE_DELAYED_GFX_REDRAW
12122   SCAN_PLAYFIELD(x, y)
12123   {
12124     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12125     {
12126       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12127          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12128
12129       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12130         DrawLevelField(x, y);
12131
12132       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12133         DrawLevelFieldCrumbled(x, y);
12134
12135       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12136         DrawLevelFieldCrumbledNeighbours(x, y);
12137
12138       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12139         DrawTwinkleOnField(x, y);
12140     }
12141
12142     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12143   }
12144 #endif
12145
12146   DrawAllPlayers();
12147   PlayAllPlayersSound();
12148
12149   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12150   {
12151     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12152
12153     local_player->show_envelope = 0;
12154   }
12155
12156   /* use random number generator in every frame to make it less predictable */
12157   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12158     RND(1);
12159 }
12160
12161 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12162 {
12163   int min_x = x, min_y = y, max_x = x, max_y = y;
12164   int i;
12165
12166   for (i = 0; i < MAX_PLAYERS; i++)
12167   {
12168     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12169
12170     if (!stored_player[i].active || &stored_player[i] == player)
12171       continue;
12172
12173     min_x = MIN(min_x, jx);
12174     min_y = MIN(min_y, jy);
12175     max_x = MAX(max_x, jx);
12176     max_y = MAX(max_y, jy);
12177   }
12178
12179   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12180 }
12181
12182 static boolean AllPlayersInVisibleScreen()
12183 {
12184   int i;
12185
12186   for (i = 0; i < MAX_PLAYERS; i++)
12187   {
12188     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12189
12190     if (!stored_player[i].active)
12191       continue;
12192
12193     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12194       return FALSE;
12195   }
12196
12197   return TRUE;
12198 }
12199
12200 void ScrollLevel(int dx, int dy)
12201 {
12202   int scroll_offset = 2 * TILEX_VAR;
12203   int x, y;
12204
12205   BlitBitmap(drawto_field, drawto_field,
12206              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12207              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12208              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12209              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12210              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12211              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12212
12213   if (dx != 0)
12214   {
12215     x = (dx == 1 ? BX1 : BX2);
12216     for (y = BY1; y <= BY2; y++)
12217       DrawScreenField(x, y);
12218   }
12219
12220   if (dy != 0)
12221   {
12222     y = (dy == 1 ? BY1 : BY2);
12223     for (x = BX1; x <= BX2; x++)
12224       DrawScreenField(x, y);
12225   }
12226
12227   redraw_mask |= REDRAW_FIELD;
12228 }
12229
12230 static boolean canFallDown(struct PlayerInfo *player)
12231 {
12232   int jx = player->jx, jy = player->jy;
12233
12234   return (IN_LEV_FIELD(jx, jy + 1) &&
12235           (IS_FREE(jx, jy + 1) ||
12236            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12237           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12238           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12239 }
12240
12241 static boolean canPassField(int x, int y, int move_dir)
12242 {
12243   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12244   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12245   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12246   int nextx = x + dx;
12247   int nexty = y + dy;
12248   int element = Feld[x][y];
12249
12250   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12251           !CAN_MOVE(element) &&
12252           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12253           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12254           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12255 }
12256
12257 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12258 {
12259   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12260   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12261   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12262   int newx = x + dx;
12263   int newy = y + dy;
12264
12265   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12266           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12267           (IS_DIGGABLE(Feld[newx][newy]) ||
12268            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12269            canPassField(newx, newy, move_dir)));
12270 }
12271
12272 static void CheckGravityMovement(struct PlayerInfo *player)
12273 {
12274   if (player->gravity && !player->programmed_action)
12275   {
12276     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12277     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12278     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12279     int jx = player->jx, jy = player->jy;
12280     boolean player_is_moving_to_valid_field =
12281       (!player_is_snapping &&
12282        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12283         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12284     boolean player_can_fall_down = canFallDown(player);
12285
12286     if (player_can_fall_down &&
12287         !player_is_moving_to_valid_field)
12288       player->programmed_action = MV_DOWN;
12289   }
12290 }
12291
12292 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12293 {
12294   return CheckGravityMovement(player);
12295
12296   if (player->gravity && !player->programmed_action)
12297   {
12298     int jx = player->jx, jy = player->jy;
12299     boolean field_under_player_is_free =
12300       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12301     boolean player_is_standing_on_valid_field =
12302       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12303        (IS_WALKABLE(Feld[jx][jy]) &&
12304         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12305
12306     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12307       player->programmed_action = MV_DOWN;
12308   }
12309 }
12310
12311 /*
12312   MovePlayerOneStep()
12313   -----------------------------------------------------------------------------
12314   dx, dy:               direction (non-diagonal) to try to move the player to
12315   real_dx, real_dy:     direction as read from input device (can be diagonal)
12316 */
12317
12318 boolean MovePlayerOneStep(struct PlayerInfo *player,
12319                           int dx, int dy, int real_dx, int real_dy)
12320 {
12321   int jx = player->jx, jy = player->jy;
12322   int new_jx = jx + dx, new_jy = jy + dy;
12323   int can_move;
12324   boolean player_can_move = !player->cannot_move;
12325
12326   if (!player->active || (!dx && !dy))
12327     return MP_NO_ACTION;
12328
12329   player->MovDir = (dx < 0 ? MV_LEFT :
12330                     dx > 0 ? MV_RIGHT :
12331                     dy < 0 ? MV_UP :
12332                     dy > 0 ? MV_DOWN :  MV_NONE);
12333
12334   if (!IN_LEV_FIELD(new_jx, new_jy))
12335     return MP_NO_ACTION;
12336
12337   if (!player_can_move)
12338   {
12339     if (player->MovPos == 0)
12340     {
12341       player->is_moving = FALSE;
12342       player->is_digging = FALSE;
12343       player->is_collecting = FALSE;
12344       player->is_snapping = FALSE;
12345       player->is_pushing = FALSE;
12346     }
12347   }
12348
12349   if (!options.network && game.centered_player_nr == -1 &&
12350       !AllPlayersInSight(player, new_jx, new_jy))
12351     return MP_NO_ACTION;
12352
12353   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12354   if (can_move != MP_MOVING)
12355     return can_move;
12356
12357   /* check if DigField() has caused relocation of the player */
12358   if (player->jx != jx || player->jy != jy)
12359     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12360
12361   StorePlayer[jx][jy] = 0;
12362   player->last_jx = jx;
12363   player->last_jy = jy;
12364   player->jx = new_jx;
12365   player->jy = new_jy;
12366   StorePlayer[new_jx][new_jy] = player->element_nr;
12367
12368   if (player->move_delay_value_next != -1)
12369   {
12370     player->move_delay_value = player->move_delay_value_next;
12371     player->move_delay_value_next = -1;
12372   }
12373
12374   player->MovPos =
12375     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12376
12377   player->step_counter++;
12378
12379   PlayerVisit[jx][jy] = FrameCounter;
12380
12381   player->is_moving = TRUE;
12382
12383 #if 1
12384   /* should better be called in MovePlayer(), but this breaks some tapes */
12385   ScrollPlayer(player, SCROLL_INIT);
12386 #endif
12387
12388   return MP_MOVING;
12389 }
12390
12391 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12392 {
12393   int jx = player->jx, jy = player->jy;
12394   int old_jx = jx, old_jy = jy;
12395   int moved = MP_NO_ACTION;
12396
12397   if (!player->active)
12398     return FALSE;
12399
12400   if (!dx && !dy)
12401   {
12402     if (player->MovPos == 0)
12403     {
12404       player->is_moving = FALSE;
12405       player->is_digging = FALSE;
12406       player->is_collecting = FALSE;
12407       player->is_snapping = FALSE;
12408       player->is_pushing = FALSE;
12409     }
12410
12411     return FALSE;
12412   }
12413
12414   if (player->move_delay > 0)
12415     return FALSE;
12416
12417   player->move_delay = -1;              /* set to "uninitialized" value */
12418
12419   /* store if player is automatically moved to next field */
12420   player->is_auto_moving = (player->programmed_action != MV_NONE);
12421
12422   /* remove the last programmed player action */
12423   player->programmed_action = 0;
12424
12425   if (player->MovPos)
12426   {
12427     /* should only happen if pre-1.2 tape recordings are played */
12428     /* this is only for backward compatibility */
12429
12430     int original_move_delay_value = player->move_delay_value;
12431
12432 #if DEBUG
12433     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12434            tape.counter);
12435 #endif
12436
12437     /* scroll remaining steps with finest movement resolution */
12438     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12439
12440     while (player->MovPos)
12441     {
12442       ScrollPlayer(player, SCROLL_GO_ON);
12443       ScrollScreen(NULL, SCROLL_GO_ON);
12444
12445       AdvanceFrameAndPlayerCounters(player->index_nr);
12446
12447       DrawAllPlayers();
12448       BackToFront_WithFrameDelay(0);
12449     }
12450
12451     player->move_delay_value = original_move_delay_value;
12452   }
12453
12454   player->is_active = FALSE;
12455
12456   if (player->last_move_dir & MV_HORIZONTAL)
12457   {
12458     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12459       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12460   }
12461   else
12462   {
12463     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12464       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12465   }
12466
12467   if (!moved && !player->is_active)
12468   {
12469     player->is_moving = FALSE;
12470     player->is_digging = FALSE;
12471     player->is_collecting = FALSE;
12472     player->is_snapping = FALSE;
12473     player->is_pushing = FALSE;
12474   }
12475
12476   jx = player->jx;
12477   jy = player->jy;
12478
12479   if (moved & MP_MOVING && !ScreenMovPos &&
12480       (player->index_nr == game.centered_player_nr ||
12481        game.centered_player_nr == -1))
12482   {
12483     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12484     int offset = game.scroll_delay_value;
12485
12486     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12487     {
12488       /* actual player has left the screen -- scroll in that direction */
12489       if (jx != old_jx)         /* player has moved horizontally */
12490         scroll_x += (jx - old_jx);
12491       else                      /* player has moved vertically */
12492         scroll_y += (jy - old_jy);
12493     }
12494     else
12495     {
12496       if (jx != old_jx)         /* player has moved horizontally */
12497       {
12498         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12499             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12500           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12501
12502         /* don't scroll over playfield boundaries */
12503         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12504           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12505
12506         /* don't scroll more than one field at a time */
12507         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12508
12509         /* don't scroll against the player's moving direction */
12510         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12511             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12512           scroll_x = old_scroll_x;
12513       }
12514       else                      /* player has moved vertically */
12515       {
12516         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12517             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12518           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12519
12520         /* don't scroll over playfield boundaries */
12521         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12522           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12523
12524         /* don't scroll more than one field at a time */
12525         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12526
12527         /* don't scroll against the player's moving direction */
12528         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12529             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12530           scroll_y = old_scroll_y;
12531       }
12532     }
12533
12534     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12535     {
12536       if (!options.network && game.centered_player_nr == -1 &&
12537           !AllPlayersInVisibleScreen())
12538       {
12539         scroll_x = old_scroll_x;
12540         scroll_y = old_scroll_y;
12541       }
12542       else
12543       {
12544         ScrollScreen(player, SCROLL_INIT);
12545         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12546       }
12547     }
12548   }
12549
12550   player->StepFrame = 0;
12551
12552   if (moved & MP_MOVING)
12553   {
12554     if (old_jx != jx && old_jy == jy)
12555       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12556     else if (old_jx == jx && old_jy != jy)
12557       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12558
12559     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12560
12561     player->last_move_dir = player->MovDir;
12562     player->is_moving = TRUE;
12563     player->is_snapping = FALSE;
12564     player->is_switching = FALSE;
12565     player->is_dropping = FALSE;
12566     player->is_dropping_pressed = FALSE;
12567     player->drop_pressed_delay = 0;
12568
12569 #if 0
12570     /* should better be called here than above, but this breaks some tapes */
12571     ScrollPlayer(player, SCROLL_INIT);
12572 #endif
12573   }
12574   else
12575   {
12576     CheckGravityMovementWhenNotMoving(player);
12577
12578     player->is_moving = FALSE;
12579
12580     /* at this point, the player is allowed to move, but cannot move right now
12581        (e.g. because of something blocking the way) -- ensure that the player
12582        is also allowed to move in the next frame (in old versions before 3.1.1,
12583        the player was forced to wait again for eight frames before next try) */
12584
12585     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12586       player->move_delay = 0;   /* allow direct movement in the next frame */
12587   }
12588
12589   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12590     player->move_delay = player->move_delay_value;
12591
12592   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12593   {
12594     TestIfPlayerTouchesBadThing(jx, jy);
12595     TestIfPlayerTouchesCustomElement(jx, jy);
12596   }
12597
12598   if (!player->active)
12599     RemovePlayer(player);
12600
12601   return moved;
12602 }
12603
12604 void ScrollPlayer(struct PlayerInfo *player, int mode)
12605 {
12606   int jx = player->jx, jy = player->jy;
12607   int last_jx = player->last_jx, last_jy = player->last_jy;
12608   int move_stepsize = TILEX / player->move_delay_value;
12609
12610   if (!player->active)
12611     return;
12612
12613   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12614     return;
12615
12616   if (mode == SCROLL_INIT)
12617   {
12618     player->actual_frame_counter = FrameCounter;
12619     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12620
12621     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12622         Feld[last_jx][last_jy] == EL_EMPTY)
12623     {
12624       int last_field_block_delay = 0;   /* start with no blocking at all */
12625       int block_delay_adjustment = player->block_delay_adjustment;
12626
12627       /* if player blocks last field, add delay for exactly one move */
12628       if (player->block_last_field)
12629       {
12630         last_field_block_delay += player->move_delay_value;
12631
12632         /* when blocking enabled, prevent moving up despite gravity */
12633         if (player->gravity && player->MovDir == MV_UP)
12634           block_delay_adjustment = -1;
12635       }
12636
12637       /* add block delay adjustment (also possible when not blocking) */
12638       last_field_block_delay += block_delay_adjustment;
12639
12640       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12641       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12642     }
12643
12644     if (player->MovPos != 0)    /* player has not yet reached destination */
12645       return;
12646   }
12647   else if (!FrameReached(&player->actual_frame_counter, 1))
12648     return;
12649
12650   if (player->MovPos != 0)
12651   {
12652     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12653     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12654
12655     /* before DrawPlayer() to draw correct player graphic for this case */
12656     if (player->MovPos == 0)
12657       CheckGravityMovement(player);
12658   }
12659
12660   if (player->MovPos == 0)      /* player reached destination field */
12661   {
12662     if (player->move_delay_reset_counter > 0)
12663     {
12664       player->move_delay_reset_counter--;
12665
12666       if (player->move_delay_reset_counter == 0)
12667       {
12668         /* continue with normal speed after quickly moving through gate */
12669         HALVE_PLAYER_SPEED(player);
12670
12671         /* be able to make the next move without delay */
12672         player->move_delay = 0;
12673       }
12674     }
12675
12676     player->last_jx = jx;
12677     player->last_jy = jy;
12678
12679     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12680         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12681         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12682         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12684         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12685         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12686         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12687     {
12688       DrawPlayer(player);       /* needed here only to cleanup last field */
12689       RemovePlayer(player);
12690
12691       if (local_player->friends_still_needed == 0 ||
12692           IS_SP_ELEMENT(Feld[jx][jy]))
12693         PlayerWins(player);
12694     }
12695
12696     /* this breaks one level: "machine", level 000 */
12697     {
12698       int move_direction = player->MovDir;
12699       int enter_side = MV_DIR_OPPOSITE(move_direction);
12700       int leave_side = move_direction;
12701       int old_jx = last_jx;
12702       int old_jy = last_jy;
12703       int old_element = Feld[old_jx][old_jy];
12704       int new_element = Feld[jx][jy];
12705
12706       if (IS_CUSTOM_ELEMENT(old_element))
12707         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12708                                    CE_LEFT_BY_PLAYER,
12709                                    player->index_bit, leave_side);
12710
12711       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12712                                           CE_PLAYER_LEAVES_X,
12713                                           player->index_bit, leave_side);
12714
12715       if (IS_CUSTOM_ELEMENT(new_element))
12716         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12717                                    player->index_bit, enter_side);
12718
12719       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12720                                           CE_PLAYER_ENTERS_X,
12721                                           player->index_bit, enter_side);
12722
12723       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12724                                         CE_MOVE_OF_X, move_direction);
12725     }
12726
12727     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12728     {
12729       TestIfPlayerTouchesBadThing(jx, jy);
12730       TestIfPlayerTouchesCustomElement(jx, jy);
12731
12732       /* needed because pushed element has not yet reached its destination,
12733          so it would trigger a change event at its previous field location */
12734       if (!player->is_pushing)
12735         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12736
12737       if (!player->active)
12738         RemovePlayer(player);
12739     }
12740
12741     if (!local_player->LevelSolved && level.use_step_counter)
12742     {
12743       int i;
12744
12745       TimePlayed++;
12746
12747       if (TimeLeft > 0)
12748       {
12749         TimeLeft--;
12750
12751         if (TimeLeft <= 10 && setup.time_limit)
12752           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12753
12754         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12755
12756         DisplayGameControlValues();
12757
12758         if (!TimeLeft && setup.time_limit)
12759           for (i = 0; i < MAX_PLAYERS; i++)
12760             KillPlayer(&stored_player[i]);
12761       }
12762       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12763       {
12764         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12765
12766         DisplayGameControlValues();
12767       }
12768     }
12769
12770     if (tape.single_step && tape.recording && !tape.pausing &&
12771         !player->programmed_action)
12772       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12773
12774     if (!player->programmed_action)
12775       CheckSaveEngineSnapshot(player);
12776   }
12777 }
12778
12779 void ScrollScreen(struct PlayerInfo *player, int mode)
12780 {
12781   static unsigned int screen_frame_counter = 0;
12782
12783   if (mode == SCROLL_INIT)
12784   {
12785     /* set scrolling step size according to actual player's moving speed */
12786     ScrollStepSize = TILEX / player->move_delay_value;
12787
12788     screen_frame_counter = FrameCounter;
12789     ScreenMovDir = player->MovDir;
12790     ScreenMovPos = player->MovPos;
12791     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12792     return;
12793   }
12794   else if (!FrameReached(&screen_frame_counter, 1))
12795     return;
12796
12797   if (ScreenMovPos)
12798   {
12799     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12800     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12801     redraw_mask |= REDRAW_FIELD;
12802   }
12803   else
12804     ScreenMovDir = MV_NONE;
12805 }
12806
12807 void TestIfPlayerTouchesCustomElement(int x, int y)
12808 {
12809   static int xy[4][2] =
12810   {
12811     { 0, -1 },
12812     { -1, 0 },
12813     { +1, 0 },
12814     { 0, +1 }
12815   };
12816   static int trigger_sides[4][2] =
12817   {
12818     /* center side       border side */
12819     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12820     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12821     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12822     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12823   };
12824   static int touch_dir[4] =
12825   {
12826     MV_LEFT | MV_RIGHT,
12827     MV_UP   | MV_DOWN,
12828     MV_UP   | MV_DOWN,
12829     MV_LEFT | MV_RIGHT
12830   };
12831   int center_element = Feld[x][y];      /* should always be non-moving! */
12832   int i;
12833
12834   for (i = 0; i < NUM_DIRECTIONS; i++)
12835   {
12836     int xx = x + xy[i][0];
12837     int yy = y + xy[i][1];
12838     int center_side = trigger_sides[i][0];
12839     int border_side = trigger_sides[i][1];
12840     int border_element;
12841
12842     if (!IN_LEV_FIELD(xx, yy))
12843       continue;
12844
12845     if (IS_PLAYER(x, y))                /* player found at center element */
12846     {
12847       struct PlayerInfo *player = PLAYERINFO(x, y);
12848
12849       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12850         border_element = Feld[xx][yy];          /* may be moving! */
12851       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12852         border_element = Feld[xx][yy];
12853       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12854         border_element = MovingOrBlocked2Element(xx, yy);
12855       else
12856         continue;               /* center and border element do not touch */
12857
12858       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12859                                  player->index_bit, border_side);
12860       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12861                                           CE_PLAYER_TOUCHES_X,
12862                                           player->index_bit, border_side);
12863
12864       {
12865         /* use player element that is initially defined in the level playfield,
12866            not the player element that corresponds to the runtime player number
12867            (example: a level that contains EL_PLAYER_3 as the only player would
12868            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12869         int player_element = PLAYERINFO(x, y)->initial_element;
12870
12871         CheckElementChangeBySide(xx, yy, border_element, player_element,
12872                                  CE_TOUCHING_X, border_side);
12873       }
12874     }
12875     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12876     {
12877       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12878
12879       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12880       {
12881         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12882           continue;             /* center and border element do not touch */
12883       }
12884
12885       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12886                                  player->index_bit, center_side);
12887       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12888                                           CE_PLAYER_TOUCHES_X,
12889                                           player->index_bit, center_side);
12890
12891       {
12892         /* use player element that is initially defined in the level playfield,
12893            not the player element that corresponds to the runtime player number
12894            (example: a level that contains EL_PLAYER_3 as the only player would
12895            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12896         int player_element = PLAYERINFO(xx, yy)->initial_element;
12897
12898         CheckElementChangeBySide(x, y, center_element, player_element,
12899                                  CE_TOUCHING_X, center_side);
12900       }
12901
12902       break;
12903     }
12904   }
12905 }
12906
12907 void TestIfElementTouchesCustomElement(int x, int y)
12908 {
12909   static int xy[4][2] =
12910   {
12911     { 0, -1 },
12912     { -1, 0 },
12913     { +1, 0 },
12914     { 0, +1 }
12915   };
12916   static int trigger_sides[4][2] =
12917   {
12918     /* center side      border side */
12919     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12920     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12921     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12922     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12923   };
12924   static int touch_dir[4] =
12925   {
12926     MV_LEFT | MV_RIGHT,
12927     MV_UP   | MV_DOWN,
12928     MV_UP   | MV_DOWN,
12929     MV_LEFT | MV_RIGHT
12930   };
12931   boolean change_center_element = FALSE;
12932   int center_element = Feld[x][y];      /* should always be non-moving! */
12933   int border_element_old[NUM_DIRECTIONS];
12934   int i;
12935
12936   for (i = 0; i < NUM_DIRECTIONS; i++)
12937   {
12938     int xx = x + xy[i][0];
12939     int yy = y + xy[i][1];
12940     int border_element;
12941
12942     border_element_old[i] = -1;
12943
12944     if (!IN_LEV_FIELD(xx, yy))
12945       continue;
12946
12947     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12948       border_element = Feld[xx][yy];    /* may be moving! */
12949     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12950       border_element = Feld[xx][yy];
12951     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12952       border_element = MovingOrBlocked2Element(xx, yy);
12953     else
12954       continue;                 /* center and border element do not touch */
12955
12956     border_element_old[i] = border_element;
12957   }
12958
12959   for (i = 0; i < NUM_DIRECTIONS; i++)
12960   {
12961     int xx = x + xy[i][0];
12962     int yy = y + xy[i][1];
12963     int center_side = trigger_sides[i][0];
12964     int border_element = border_element_old[i];
12965
12966     if (border_element == -1)
12967       continue;
12968
12969     /* check for change of border element */
12970     CheckElementChangeBySide(xx, yy, border_element, center_element,
12971                              CE_TOUCHING_X, center_side);
12972
12973     /* (center element cannot be player, so we dont have to check this here) */
12974   }
12975
12976   for (i = 0; i < NUM_DIRECTIONS; i++)
12977   {
12978     int xx = x + xy[i][0];
12979     int yy = y + xy[i][1];
12980     int border_side = trigger_sides[i][1];
12981     int border_element = border_element_old[i];
12982
12983     if (border_element == -1)
12984       continue;
12985
12986     /* check for change of center element (but change it only once) */
12987     if (!change_center_element)
12988       change_center_element =
12989         CheckElementChangeBySide(x, y, center_element, border_element,
12990                                  CE_TOUCHING_X, border_side);
12991
12992     if (IS_PLAYER(xx, yy))
12993     {
12994       /* use player element that is initially defined in the level playfield,
12995          not the player element that corresponds to the runtime player number
12996          (example: a level that contains EL_PLAYER_3 as the only player would
12997          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12998       int player_element = PLAYERINFO(xx, yy)->initial_element;
12999
13000       CheckElementChangeBySide(x, y, center_element, player_element,
13001                                CE_TOUCHING_X, border_side);
13002     }
13003   }
13004 }
13005
13006 void TestIfElementHitsCustomElement(int x, int y, int direction)
13007 {
13008   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13009   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13010   int hitx = x + dx, hity = y + dy;
13011   int hitting_element = Feld[x][y];
13012   int touched_element;
13013
13014   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13015     return;
13016
13017   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13018                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13019
13020   if (IN_LEV_FIELD(hitx, hity))
13021   {
13022     int opposite_direction = MV_DIR_OPPOSITE(direction);
13023     int hitting_side = direction;
13024     int touched_side = opposite_direction;
13025     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13026                           MovDir[hitx][hity] != direction ||
13027                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13028
13029     object_hit = TRUE;
13030
13031     if (object_hit)
13032     {
13033       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13034                                CE_HITTING_X, touched_side);
13035
13036       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13037                                CE_HIT_BY_X, hitting_side);
13038
13039       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13040                                CE_HIT_BY_SOMETHING, opposite_direction);
13041
13042       if (IS_PLAYER(hitx, hity))
13043       {
13044         /* use player element that is initially defined in the level playfield,
13045            not the player element that corresponds to the runtime player number
13046            (example: a level that contains EL_PLAYER_3 as the only player would
13047            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13048         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13049
13050         CheckElementChangeBySide(x, y, hitting_element, player_element,
13051                                  CE_HITTING_X, touched_side);
13052       }
13053     }
13054   }
13055
13056   /* "hitting something" is also true when hitting the playfield border */
13057   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13058                            CE_HITTING_SOMETHING, direction);
13059 }
13060
13061 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13062 {
13063   int i, kill_x = -1, kill_y = -1;
13064
13065   int bad_element = -1;
13066   static int test_xy[4][2] =
13067   {
13068     { 0, -1 },
13069     { -1, 0 },
13070     { +1, 0 },
13071     { 0, +1 }
13072   };
13073   static int test_dir[4] =
13074   {
13075     MV_UP,
13076     MV_LEFT,
13077     MV_RIGHT,
13078     MV_DOWN
13079   };
13080
13081   for (i = 0; i < NUM_DIRECTIONS; i++)
13082   {
13083     int test_x, test_y, test_move_dir, test_element;
13084
13085     test_x = good_x + test_xy[i][0];
13086     test_y = good_y + test_xy[i][1];
13087
13088     if (!IN_LEV_FIELD(test_x, test_y))
13089       continue;
13090
13091     test_move_dir =
13092       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13093
13094     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13095
13096     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13097        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13098     */
13099     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13100         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13101     {
13102       kill_x = test_x;
13103       kill_y = test_y;
13104       bad_element = test_element;
13105
13106       break;
13107     }
13108   }
13109
13110   if (kill_x != -1 || kill_y != -1)
13111   {
13112     if (IS_PLAYER(good_x, good_y))
13113     {
13114       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13115
13116       if (player->shield_deadly_time_left > 0 &&
13117           !IS_INDESTRUCTIBLE(bad_element))
13118         Bang(kill_x, kill_y);
13119       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13120         KillPlayer(player);
13121     }
13122     else
13123       Bang(good_x, good_y);
13124   }
13125 }
13126
13127 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13128 {
13129   int i, kill_x = -1, kill_y = -1;
13130   int bad_element = Feld[bad_x][bad_y];
13131   static int test_xy[4][2] =
13132   {
13133     { 0, -1 },
13134     { -1, 0 },
13135     { +1, 0 },
13136     { 0, +1 }
13137   };
13138   static int touch_dir[4] =
13139   {
13140     MV_LEFT | MV_RIGHT,
13141     MV_UP   | MV_DOWN,
13142     MV_UP   | MV_DOWN,
13143     MV_LEFT | MV_RIGHT
13144   };
13145   static int test_dir[4] =
13146   {
13147     MV_UP,
13148     MV_LEFT,
13149     MV_RIGHT,
13150     MV_DOWN
13151   };
13152
13153   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13154     return;
13155
13156   for (i = 0; i < NUM_DIRECTIONS; i++)
13157   {
13158     int test_x, test_y, test_move_dir, test_element;
13159
13160     test_x = bad_x + test_xy[i][0];
13161     test_y = bad_y + test_xy[i][1];
13162
13163     if (!IN_LEV_FIELD(test_x, test_y))
13164       continue;
13165
13166     test_move_dir =
13167       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13168
13169     test_element = Feld[test_x][test_y];
13170
13171     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13172        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13173     */
13174     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13175         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13176     {
13177       /* good thing is player or penguin that does not move away */
13178       if (IS_PLAYER(test_x, test_y))
13179       {
13180         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13181
13182         if (bad_element == EL_ROBOT && player->is_moving)
13183           continue;     /* robot does not kill player if he is moving */
13184
13185         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13186         {
13187           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13188             continue;           /* center and border element do not touch */
13189         }
13190
13191         kill_x = test_x;
13192         kill_y = test_y;
13193
13194         break;
13195       }
13196       else if (test_element == EL_PENGUIN)
13197       {
13198         kill_x = test_x;
13199         kill_y = test_y;
13200
13201         break;
13202       }
13203     }
13204   }
13205
13206   if (kill_x != -1 || kill_y != -1)
13207   {
13208     if (IS_PLAYER(kill_x, kill_y))
13209     {
13210       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13211
13212       if (player->shield_deadly_time_left > 0 &&
13213           !IS_INDESTRUCTIBLE(bad_element))
13214         Bang(bad_x, bad_y);
13215       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13216         KillPlayer(player);
13217     }
13218     else
13219       Bang(kill_x, kill_y);
13220   }
13221 }
13222
13223 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13224 {
13225   int bad_element = Feld[bad_x][bad_y];
13226   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13227   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13228   int test_x = bad_x + dx, test_y = bad_y + dy;
13229   int test_move_dir, test_element;
13230   int kill_x = -1, kill_y = -1;
13231
13232   if (!IN_LEV_FIELD(test_x, test_y))
13233     return;
13234
13235   test_move_dir =
13236     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13237
13238   test_element = Feld[test_x][test_y];
13239
13240   if (test_move_dir != bad_move_dir)
13241   {
13242     /* good thing can be player or penguin that does not move away */
13243     if (IS_PLAYER(test_x, test_y))
13244     {
13245       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13246
13247       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13248          player as being hit when he is moving towards the bad thing, because
13249          the "get hit by" condition would be lost after the player stops) */
13250       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13251         return;         /* player moves away from bad thing */
13252
13253       kill_x = test_x;
13254       kill_y = test_y;
13255     }
13256     else if (test_element == EL_PENGUIN)
13257     {
13258       kill_x = test_x;
13259       kill_y = test_y;
13260     }
13261   }
13262
13263   if (kill_x != -1 || kill_y != -1)
13264   {
13265     if (IS_PLAYER(kill_x, kill_y))
13266     {
13267       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13268
13269       if (player->shield_deadly_time_left > 0 &&
13270           !IS_INDESTRUCTIBLE(bad_element))
13271         Bang(bad_x, bad_y);
13272       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13273         KillPlayer(player);
13274     }
13275     else
13276       Bang(kill_x, kill_y);
13277   }
13278 }
13279
13280 void TestIfPlayerTouchesBadThing(int x, int y)
13281 {
13282   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13283 }
13284
13285 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13286 {
13287   TestIfGoodThingHitsBadThing(x, y, move_dir);
13288 }
13289
13290 void TestIfBadThingTouchesPlayer(int x, int y)
13291 {
13292   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13293 }
13294
13295 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13296 {
13297   TestIfBadThingHitsGoodThing(x, y, move_dir);
13298 }
13299
13300 void TestIfFriendTouchesBadThing(int x, int y)
13301 {
13302   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13303 }
13304
13305 void TestIfBadThingTouchesFriend(int x, int y)
13306 {
13307   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13308 }
13309
13310 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13311 {
13312   int i, kill_x = bad_x, kill_y = bad_y;
13313   static int xy[4][2] =
13314   {
13315     { 0, -1 },
13316     { -1, 0 },
13317     { +1, 0 },
13318     { 0, +1 }
13319   };
13320
13321   for (i = 0; i < NUM_DIRECTIONS; i++)
13322   {
13323     int x, y, element;
13324
13325     x = bad_x + xy[i][0];
13326     y = bad_y + xy[i][1];
13327     if (!IN_LEV_FIELD(x, y))
13328       continue;
13329
13330     element = Feld[x][y];
13331     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13332         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13333     {
13334       kill_x = x;
13335       kill_y = y;
13336       break;
13337     }
13338   }
13339
13340   if (kill_x != bad_x || kill_y != bad_y)
13341     Bang(bad_x, bad_y);
13342 }
13343
13344 void KillPlayer(struct PlayerInfo *player)
13345 {
13346   int jx = player->jx, jy = player->jy;
13347
13348   if (!player->active)
13349     return;
13350
13351 #if 0
13352   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13353          player->killed, player->active, player->reanimated);
13354 #endif
13355
13356   /* the following code was introduced to prevent an infinite loop when calling
13357      -> Bang()
13358      -> CheckTriggeredElementChangeExt()
13359      -> ExecuteCustomElementAction()
13360      -> KillPlayer()
13361      -> (infinitely repeating the above sequence of function calls)
13362      which occurs when killing the player while having a CE with the setting
13363      "kill player X when explosion of <player X>"; the solution using a new
13364      field "player->killed" was chosen for backwards compatibility, although
13365      clever use of the fields "player->active" etc. would probably also work */
13366 #if 1
13367   if (player->killed)
13368     return;
13369 #endif
13370
13371   player->killed = TRUE;
13372
13373   /* remove accessible field at the player's position */
13374   Feld[jx][jy] = EL_EMPTY;
13375
13376   /* deactivate shield (else Bang()/Explode() would not work right) */
13377   player->shield_normal_time_left = 0;
13378   player->shield_deadly_time_left = 0;
13379
13380 #if 0
13381   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13382          player->killed, player->active, player->reanimated);
13383 #endif
13384
13385   Bang(jx, jy);
13386
13387 #if 0
13388   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13389          player->killed, player->active, player->reanimated);
13390 #endif
13391
13392   if (player->reanimated)       /* killed player may have been reanimated */
13393     player->killed = player->reanimated = FALSE;
13394   else
13395     BuryPlayer(player);
13396 }
13397
13398 static void KillPlayerUnlessEnemyProtected(int x, int y)
13399 {
13400   if (!PLAYER_ENEMY_PROTECTED(x, y))
13401     KillPlayer(PLAYERINFO(x, y));
13402 }
13403
13404 static void KillPlayerUnlessExplosionProtected(int x, int y)
13405 {
13406   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13407     KillPlayer(PLAYERINFO(x, y));
13408 }
13409
13410 void BuryPlayer(struct PlayerInfo *player)
13411 {
13412   int jx = player->jx, jy = player->jy;
13413
13414   if (!player->active)
13415     return;
13416
13417   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13418   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13419
13420   player->GameOver = TRUE;
13421   RemovePlayer(player);
13422 }
13423
13424 void RemovePlayer(struct PlayerInfo *player)
13425 {
13426   int jx = player->jx, jy = player->jy;
13427   int i, found = FALSE;
13428
13429   player->present = FALSE;
13430   player->active = FALSE;
13431
13432   if (!ExplodeField[jx][jy])
13433     StorePlayer[jx][jy] = 0;
13434
13435   if (player->is_moving)
13436     TEST_DrawLevelField(player->last_jx, player->last_jy);
13437
13438   for (i = 0; i < MAX_PLAYERS; i++)
13439     if (stored_player[i].active)
13440       found = TRUE;
13441
13442   if (!found)
13443     AllPlayersGone = TRUE;
13444
13445   ExitX = ZX = jx;
13446   ExitY = ZY = jy;
13447 }
13448
13449 static void setFieldForSnapping(int x, int y, int element, int direction)
13450 {
13451   struct ElementInfo *ei = &element_info[element];
13452   int direction_bit = MV_DIR_TO_BIT(direction);
13453   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13454   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13455                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13456
13457   Feld[x][y] = EL_ELEMENT_SNAPPING;
13458   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13459
13460   ResetGfxAnimation(x, y);
13461
13462   GfxElement[x][y] = element;
13463   GfxAction[x][y] = action;
13464   GfxDir[x][y] = direction;
13465   GfxFrame[x][y] = -1;
13466 }
13467
13468 /*
13469   =============================================================================
13470   checkDiagonalPushing()
13471   -----------------------------------------------------------------------------
13472   check if diagonal input device direction results in pushing of object
13473   (by checking if the alternative direction is walkable, diggable, ...)
13474   =============================================================================
13475 */
13476
13477 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13478                                     int x, int y, int real_dx, int real_dy)
13479 {
13480   int jx, jy, dx, dy, xx, yy;
13481
13482   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13483     return TRUE;
13484
13485   /* diagonal direction: check alternative direction */
13486   jx = player->jx;
13487   jy = player->jy;
13488   dx = x - jx;
13489   dy = y - jy;
13490   xx = jx + (dx == 0 ? real_dx : 0);
13491   yy = jy + (dy == 0 ? real_dy : 0);
13492
13493   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13494 }
13495
13496 /*
13497   =============================================================================
13498   DigField()
13499   -----------------------------------------------------------------------------
13500   x, y:                 field next to player (non-diagonal) to try to dig to
13501   real_dx, real_dy:     direction as read from input device (can be diagonal)
13502   =============================================================================
13503 */
13504
13505 static int DigField(struct PlayerInfo *player,
13506                     int oldx, int oldy, int x, int y,
13507                     int real_dx, int real_dy, int mode)
13508 {
13509   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13510   boolean player_was_pushing = player->is_pushing;
13511   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13512   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13513   int jx = oldx, jy = oldy;
13514   int dx = x - jx, dy = y - jy;
13515   int nextx = x + dx, nexty = y + dy;
13516   int move_direction = (dx == -1 ? MV_LEFT  :
13517                         dx == +1 ? MV_RIGHT :
13518                         dy == -1 ? MV_UP    :
13519                         dy == +1 ? MV_DOWN  : MV_NONE);
13520   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13521   int dig_side = MV_DIR_OPPOSITE(move_direction);
13522   int old_element = Feld[jx][jy];
13523   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13524   int collect_count;
13525
13526   if (is_player)                /* function can also be called by EL_PENGUIN */
13527   {
13528     if (player->MovPos == 0)
13529     {
13530       player->is_digging = FALSE;
13531       player->is_collecting = FALSE;
13532     }
13533
13534     if (player->MovPos == 0)    /* last pushing move finished */
13535       player->is_pushing = FALSE;
13536
13537     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13538     {
13539       player->is_switching = FALSE;
13540       player->push_delay = -1;
13541
13542       return MP_NO_ACTION;
13543     }
13544   }
13545
13546   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13547     old_element = Back[jx][jy];
13548
13549   /* in case of element dropped at player position, check background */
13550   else if (Back[jx][jy] != EL_EMPTY &&
13551            game.engine_version >= VERSION_IDENT(2,2,0,0))
13552     old_element = Back[jx][jy];
13553
13554   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13555     return MP_NO_ACTION;        /* field has no opening in this direction */
13556
13557   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13558     return MP_NO_ACTION;        /* field has no opening in this direction */
13559
13560   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13561   {
13562     SplashAcid(x, y);
13563
13564     Feld[jx][jy] = player->artwork_element;
13565     InitMovingField(jx, jy, MV_DOWN);
13566     Store[jx][jy] = EL_ACID;
13567     ContinueMoving(jx, jy);
13568     BuryPlayer(player);
13569
13570     return MP_DONT_RUN_INTO;
13571   }
13572
13573   if (player_can_move && DONT_RUN_INTO(element))
13574   {
13575     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13576
13577     return MP_DONT_RUN_INTO;
13578   }
13579
13580   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13581     return MP_NO_ACTION;
13582
13583   collect_count = element_info[element].collect_count_initial;
13584
13585   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13586     return MP_NO_ACTION;
13587
13588   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13589     player_can_move = player_can_move_or_snap;
13590
13591   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13592       game.engine_version >= VERSION_IDENT(2,2,0,0))
13593   {
13594     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13595                                player->index_bit, dig_side);
13596     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13597                                         player->index_bit, dig_side);
13598
13599     if (element == EL_DC_LANDMINE)
13600       Bang(x, y);
13601
13602     if (Feld[x][y] != element)          /* field changed by snapping */
13603       return MP_ACTION;
13604
13605     return MP_NO_ACTION;
13606   }
13607
13608   if (player->gravity && is_player && !player->is_auto_moving &&
13609       canFallDown(player) && move_direction != MV_DOWN &&
13610       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13611     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13612
13613   if (player_can_move &&
13614       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13615   {
13616     int sound_element = SND_ELEMENT(element);
13617     int sound_action = ACTION_WALKING;
13618
13619     if (IS_RND_GATE(element))
13620     {
13621       if (!player->key[RND_GATE_NR(element)])
13622         return MP_NO_ACTION;
13623     }
13624     else if (IS_RND_GATE_GRAY(element))
13625     {
13626       if (!player->key[RND_GATE_GRAY_NR(element)])
13627         return MP_NO_ACTION;
13628     }
13629     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13630     {
13631       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13632         return MP_NO_ACTION;
13633     }
13634     else if (element == EL_EXIT_OPEN ||
13635              element == EL_EM_EXIT_OPEN ||
13636              element == EL_EM_EXIT_OPENING ||
13637              element == EL_STEEL_EXIT_OPEN ||
13638              element == EL_EM_STEEL_EXIT_OPEN ||
13639              element == EL_EM_STEEL_EXIT_OPENING ||
13640              element == EL_SP_EXIT_OPEN ||
13641              element == EL_SP_EXIT_OPENING)
13642     {
13643       sound_action = ACTION_PASSING;    /* player is passing exit */
13644     }
13645     else if (element == EL_EMPTY)
13646     {
13647       sound_action = ACTION_MOVING;             /* nothing to walk on */
13648     }
13649
13650     /* play sound from background or player, whatever is available */
13651     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13652       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13653     else
13654       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13655   }
13656   else if (player_can_move &&
13657            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13658   {
13659     if (!ACCESS_FROM(element, opposite_direction))
13660       return MP_NO_ACTION;      /* field not accessible from this direction */
13661
13662     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13663       return MP_NO_ACTION;
13664
13665     if (IS_EM_GATE(element))
13666     {
13667       if (!player->key[EM_GATE_NR(element)])
13668         return MP_NO_ACTION;
13669     }
13670     else if (IS_EM_GATE_GRAY(element))
13671     {
13672       if (!player->key[EM_GATE_GRAY_NR(element)])
13673         return MP_NO_ACTION;
13674     }
13675     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13676     {
13677       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13678         return MP_NO_ACTION;
13679     }
13680     else if (IS_EMC_GATE(element))
13681     {
13682       if (!player->key[EMC_GATE_NR(element)])
13683         return MP_NO_ACTION;
13684     }
13685     else if (IS_EMC_GATE_GRAY(element))
13686     {
13687       if (!player->key[EMC_GATE_GRAY_NR(element)])
13688         return MP_NO_ACTION;
13689     }
13690     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13691     {
13692       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13693         return MP_NO_ACTION;
13694     }
13695     else if (element == EL_DC_GATE_WHITE ||
13696              element == EL_DC_GATE_WHITE_GRAY ||
13697              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13698     {
13699       if (player->num_white_keys == 0)
13700         return MP_NO_ACTION;
13701
13702       player->num_white_keys--;
13703     }
13704     else if (IS_SP_PORT(element))
13705     {
13706       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13707           element == EL_SP_GRAVITY_PORT_RIGHT ||
13708           element == EL_SP_GRAVITY_PORT_UP ||
13709           element == EL_SP_GRAVITY_PORT_DOWN)
13710         player->gravity = !player->gravity;
13711       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13712                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13713                element == EL_SP_GRAVITY_ON_PORT_UP ||
13714                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13715         player->gravity = TRUE;
13716       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13717                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13718                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13719                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13720         player->gravity = FALSE;
13721     }
13722
13723     /* automatically move to the next field with double speed */
13724     player->programmed_action = move_direction;
13725
13726     if (player->move_delay_reset_counter == 0)
13727     {
13728       player->move_delay_reset_counter = 2;     /* two double speed steps */
13729
13730       DOUBLE_PLAYER_SPEED(player);
13731     }
13732
13733     PlayLevelSoundAction(x, y, ACTION_PASSING);
13734   }
13735   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13736   {
13737     RemoveField(x, y);
13738
13739     if (mode != DF_SNAP)
13740     {
13741       GfxElement[x][y] = GFX_ELEMENT(element);
13742       player->is_digging = TRUE;
13743     }
13744
13745     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13746
13747     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13748                                         player->index_bit, dig_side);
13749
13750     if (mode == DF_SNAP)
13751     {
13752       if (level.block_snap_field)
13753         setFieldForSnapping(x, y, element, move_direction);
13754       else
13755         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13756
13757       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13758                                           player->index_bit, dig_side);
13759     }
13760   }
13761   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13762   {
13763     RemoveField(x, y);
13764
13765     if (is_player && mode != DF_SNAP)
13766     {
13767       GfxElement[x][y] = element;
13768       player->is_collecting = TRUE;
13769     }
13770
13771     if (element == EL_SPEED_PILL)
13772     {
13773       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13774     }
13775     else if (element == EL_EXTRA_TIME && level.time > 0)
13776     {
13777       TimeLeft += level.extra_time;
13778
13779       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13780
13781       DisplayGameControlValues();
13782     }
13783     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13784     {
13785       player->shield_normal_time_left += level.shield_normal_time;
13786       if (element == EL_SHIELD_DEADLY)
13787         player->shield_deadly_time_left += level.shield_deadly_time;
13788     }
13789     else if (element == EL_DYNAMITE ||
13790              element == EL_EM_DYNAMITE ||
13791              element == EL_SP_DISK_RED)
13792     {
13793       if (player->inventory_size < MAX_INVENTORY_SIZE)
13794         player->inventory_element[player->inventory_size++] = element;
13795
13796       DrawGameDoorValues();
13797     }
13798     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13799     {
13800       player->dynabomb_count++;
13801       player->dynabombs_left++;
13802     }
13803     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13804     {
13805       player->dynabomb_size++;
13806     }
13807     else if (element == EL_DYNABOMB_INCREASE_POWER)
13808     {
13809       player->dynabomb_xl = TRUE;
13810     }
13811     else if (IS_KEY(element))
13812     {
13813       player->key[KEY_NR(element)] = TRUE;
13814
13815       DrawGameDoorValues();
13816     }
13817     else if (element == EL_DC_KEY_WHITE)
13818     {
13819       player->num_white_keys++;
13820
13821       /* display white keys? */
13822       /* DrawGameDoorValues(); */
13823     }
13824     else if (IS_ENVELOPE(element))
13825     {
13826       player->show_envelope = element;
13827     }
13828     else if (element == EL_EMC_LENSES)
13829     {
13830       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13831
13832       RedrawAllInvisibleElementsForLenses();
13833     }
13834     else if (element == EL_EMC_MAGNIFIER)
13835     {
13836       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13837
13838       RedrawAllInvisibleElementsForMagnifier();
13839     }
13840     else if (IS_DROPPABLE(element) ||
13841              IS_THROWABLE(element))     /* can be collected and dropped */
13842     {
13843       int i;
13844
13845       if (collect_count == 0)
13846         player->inventory_infinite_element = element;
13847       else
13848         for (i = 0; i < collect_count; i++)
13849           if (player->inventory_size < MAX_INVENTORY_SIZE)
13850             player->inventory_element[player->inventory_size++] = element;
13851
13852       DrawGameDoorValues();
13853     }
13854     else if (collect_count > 0)
13855     {
13856       local_player->gems_still_needed -= collect_count;
13857       if (local_player->gems_still_needed < 0)
13858         local_player->gems_still_needed = 0;
13859
13860       game.snapshot.collected_item = TRUE;
13861
13862       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13863
13864       DisplayGameControlValues();
13865     }
13866
13867     RaiseScoreElement(element);
13868     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13869
13870     if (is_player)
13871       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13872                                           player->index_bit, dig_side);
13873
13874     if (mode == DF_SNAP)
13875     {
13876       if (level.block_snap_field)
13877         setFieldForSnapping(x, y, element, move_direction);
13878       else
13879         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13880
13881       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13882                                           player->index_bit, dig_side);
13883     }
13884   }
13885   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13886   {
13887     if (mode == DF_SNAP && element != EL_BD_ROCK)
13888       return MP_NO_ACTION;
13889
13890     if (CAN_FALL(element) && dy)
13891       return MP_NO_ACTION;
13892
13893     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13894         !(element == EL_SPRING && level.use_spring_bug))
13895       return MP_NO_ACTION;
13896
13897     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13898         ((move_direction & MV_VERTICAL &&
13899           ((element_info[element].move_pattern & MV_LEFT &&
13900             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13901            (element_info[element].move_pattern & MV_RIGHT &&
13902             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13903          (move_direction & MV_HORIZONTAL &&
13904           ((element_info[element].move_pattern & MV_UP &&
13905             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13906            (element_info[element].move_pattern & MV_DOWN &&
13907             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13908       return MP_NO_ACTION;
13909
13910     /* do not push elements already moving away faster than player */
13911     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13912         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13913       return MP_NO_ACTION;
13914
13915     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13916     {
13917       if (player->push_delay_value == -1 || !player_was_pushing)
13918         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13919     }
13920     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13921     {
13922       if (player->push_delay_value == -1)
13923         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13924     }
13925     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13926     {
13927       if (!player->is_pushing)
13928         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13929     }
13930
13931     player->is_pushing = TRUE;
13932     player->is_active = TRUE;
13933
13934     if (!(IN_LEV_FIELD(nextx, nexty) &&
13935           (IS_FREE(nextx, nexty) ||
13936            (IS_SB_ELEMENT(element) &&
13937             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13938            (IS_CUSTOM_ELEMENT(element) &&
13939             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13940       return MP_NO_ACTION;
13941
13942     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13943       return MP_NO_ACTION;
13944
13945     if (player->push_delay == -1)       /* new pushing; restart delay */
13946       player->push_delay = 0;
13947
13948     if (player->push_delay < player->push_delay_value &&
13949         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13950         element != EL_SPRING && element != EL_BALLOON)
13951     {
13952       /* make sure that there is no move delay before next try to push */
13953       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13954         player->move_delay = 0;
13955
13956       return MP_NO_ACTION;
13957     }
13958
13959     if (IS_CUSTOM_ELEMENT(element) &&
13960         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13961     {
13962       if (!DigFieldByCE(nextx, nexty, element))
13963         return MP_NO_ACTION;
13964     }
13965
13966     if (IS_SB_ELEMENT(element))
13967     {
13968       if (element == EL_SOKOBAN_FIELD_FULL)
13969       {
13970         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13971         local_player->sokobanfields_still_needed++;
13972       }
13973
13974       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13975       {
13976         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13977         local_player->sokobanfields_still_needed--;
13978       }
13979
13980       Feld[x][y] = EL_SOKOBAN_OBJECT;
13981
13982       if (Back[x][y] == Back[nextx][nexty])
13983         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13984       else if (Back[x][y] != 0)
13985         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13986                                     ACTION_EMPTYING);
13987       else
13988         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13989                                     ACTION_FILLING);
13990
13991       if (local_player->sokobanfields_still_needed == 0 &&
13992           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13993       {
13994         PlayerWins(player);
13995
13996         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13997       }
13998     }
13999     else
14000       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14001
14002     InitMovingField(x, y, move_direction);
14003     GfxAction[x][y] = ACTION_PUSHING;
14004
14005     if (mode == DF_SNAP)
14006       ContinueMoving(x, y);
14007     else
14008       MovPos[x][y] = (dx != 0 ? dx : dy);
14009
14010     Pushed[x][y] = TRUE;
14011     Pushed[nextx][nexty] = TRUE;
14012
14013     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14014       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14015     else
14016       player->push_delay_value = -1;    /* get new value later */
14017
14018     /* check for element change _after_ element has been pushed */
14019     if (game.use_change_when_pushing_bug)
14020     {
14021       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14022                                  player->index_bit, dig_side);
14023       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14024                                           player->index_bit, dig_side);
14025     }
14026   }
14027   else if (IS_SWITCHABLE(element))
14028   {
14029     if (PLAYER_SWITCHING(player, x, y))
14030     {
14031       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14032                                           player->index_bit, dig_side);
14033
14034       return MP_ACTION;
14035     }
14036
14037     player->is_switching = TRUE;
14038     player->switch_x = x;
14039     player->switch_y = y;
14040
14041     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14042
14043     if (element == EL_ROBOT_WHEEL)
14044     {
14045       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14046       ZX = x;
14047       ZY = y;
14048
14049       game.robot_wheel_active = TRUE;
14050
14051       TEST_DrawLevelField(x, y);
14052     }
14053     else if (element == EL_SP_TERMINAL)
14054     {
14055       int xx, yy;
14056
14057       SCAN_PLAYFIELD(xx, yy)
14058       {
14059         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14060         {
14061           Bang(xx, yy);
14062         }
14063         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14064         {
14065           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14066
14067           ResetGfxAnimation(xx, yy);
14068           TEST_DrawLevelField(xx, yy);
14069         }
14070       }
14071     }
14072     else if (IS_BELT_SWITCH(element))
14073     {
14074       ToggleBeltSwitch(x, y);
14075     }
14076     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14077              element == EL_SWITCHGATE_SWITCH_DOWN ||
14078              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14079              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14080     {
14081       ToggleSwitchgateSwitch(x, y);
14082     }
14083     else if (element == EL_LIGHT_SWITCH ||
14084              element == EL_LIGHT_SWITCH_ACTIVE)
14085     {
14086       ToggleLightSwitch(x, y);
14087     }
14088     else if (element == EL_TIMEGATE_SWITCH ||
14089              element == EL_DC_TIMEGATE_SWITCH)
14090     {
14091       ActivateTimegateSwitch(x, y);
14092     }
14093     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14094              element == EL_BALLOON_SWITCH_RIGHT ||
14095              element == EL_BALLOON_SWITCH_UP    ||
14096              element == EL_BALLOON_SWITCH_DOWN  ||
14097              element == EL_BALLOON_SWITCH_NONE  ||
14098              element == EL_BALLOON_SWITCH_ANY)
14099     {
14100       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14101                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14102                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14103                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14104                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14105                              move_direction);
14106     }
14107     else if (element == EL_LAMP)
14108     {
14109       Feld[x][y] = EL_LAMP_ACTIVE;
14110       local_player->lights_still_needed--;
14111
14112       ResetGfxAnimation(x, y);
14113       TEST_DrawLevelField(x, y);
14114     }
14115     else if (element == EL_TIME_ORB_FULL)
14116     {
14117       Feld[x][y] = EL_TIME_ORB_EMPTY;
14118
14119       if (level.time > 0 || level.use_time_orb_bug)
14120       {
14121         TimeLeft += level.time_orb_time;
14122         game.no_time_limit = FALSE;
14123
14124         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14125
14126         DisplayGameControlValues();
14127       }
14128
14129       ResetGfxAnimation(x, y);
14130       TEST_DrawLevelField(x, y);
14131     }
14132     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14133              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14134     {
14135       int xx, yy;
14136
14137       game.ball_state = !game.ball_state;
14138
14139       SCAN_PLAYFIELD(xx, yy)
14140       {
14141         int e = Feld[xx][yy];
14142
14143         if (game.ball_state)
14144         {
14145           if (e == EL_EMC_MAGIC_BALL)
14146             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14147           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14148             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14149         }
14150         else
14151         {
14152           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14153             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14154           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14155             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14156         }
14157       }
14158     }
14159
14160     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14161                                         player->index_bit, dig_side);
14162
14163     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14164                                         player->index_bit, dig_side);
14165
14166     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14167                                         player->index_bit, dig_side);
14168
14169     return MP_ACTION;
14170   }
14171   else
14172   {
14173     if (!PLAYER_SWITCHING(player, x, y))
14174     {
14175       player->is_switching = TRUE;
14176       player->switch_x = x;
14177       player->switch_y = y;
14178
14179       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14180                                  player->index_bit, dig_side);
14181       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14182                                           player->index_bit, dig_side);
14183
14184       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14185                                  player->index_bit, dig_side);
14186       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14187                                           player->index_bit, dig_side);
14188     }
14189
14190     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14191                                player->index_bit, dig_side);
14192     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14193                                         player->index_bit, dig_side);
14194
14195     return MP_NO_ACTION;
14196   }
14197
14198   player->push_delay = -1;
14199
14200   if (is_player)                /* function can also be called by EL_PENGUIN */
14201   {
14202     if (Feld[x][y] != element)          /* really digged/collected something */
14203     {
14204       player->is_collecting = !player->is_digging;
14205       player->is_active = TRUE;
14206     }
14207   }
14208
14209   return MP_MOVING;
14210 }
14211
14212 static boolean DigFieldByCE(int x, int y, int digging_element)
14213 {
14214   int element = Feld[x][y];
14215
14216   if (!IS_FREE(x, y))
14217   {
14218     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14219                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14220                   ACTION_BREAKING);
14221
14222     /* no element can dig solid indestructible elements */
14223     if (IS_INDESTRUCTIBLE(element) &&
14224         !IS_DIGGABLE(element) &&
14225         !IS_COLLECTIBLE(element))
14226       return FALSE;
14227
14228     if (AmoebaNr[x][y] &&
14229         (element == EL_AMOEBA_FULL ||
14230          element == EL_BD_AMOEBA ||
14231          element == EL_AMOEBA_GROWING))
14232     {
14233       AmoebaCnt[AmoebaNr[x][y]]--;
14234       AmoebaCnt2[AmoebaNr[x][y]]--;
14235     }
14236
14237     if (IS_MOVING(x, y))
14238       RemoveMovingField(x, y);
14239     else
14240     {
14241       RemoveField(x, y);
14242       TEST_DrawLevelField(x, y);
14243     }
14244
14245     /* if digged element was about to explode, prevent the explosion */
14246     ExplodeField[x][y] = EX_TYPE_NONE;
14247
14248     PlayLevelSoundAction(x, y, action);
14249   }
14250
14251   Store[x][y] = EL_EMPTY;
14252
14253   /* this makes it possible to leave the removed element again */
14254   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14255     Store[x][y] = element;
14256
14257   return TRUE;
14258 }
14259
14260 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14261 {
14262   int jx = player->jx, jy = player->jy;
14263   int x = jx + dx, y = jy + dy;
14264   int snap_direction = (dx == -1 ? MV_LEFT  :
14265                         dx == +1 ? MV_RIGHT :
14266                         dy == -1 ? MV_UP    :
14267                         dy == +1 ? MV_DOWN  : MV_NONE);
14268   boolean can_continue_snapping = (level.continuous_snapping &&
14269                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14270
14271   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14272     return FALSE;
14273
14274   if (!player->active || !IN_LEV_FIELD(x, y))
14275     return FALSE;
14276
14277   if (dx && dy)
14278     return FALSE;
14279
14280   if (!dx && !dy)
14281   {
14282     if (player->MovPos == 0)
14283       player->is_pushing = FALSE;
14284
14285     player->is_snapping = FALSE;
14286
14287     if (player->MovPos == 0)
14288     {
14289       player->is_moving = FALSE;
14290       player->is_digging = FALSE;
14291       player->is_collecting = FALSE;
14292     }
14293
14294     return FALSE;
14295   }
14296
14297   /* prevent snapping with already pressed snap key when not allowed */
14298   if (player->is_snapping && !can_continue_snapping)
14299     return FALSE;
14300
14301   player->MovDir = snap_direction;
14302
14303   if (player->MovPos == 0)
14304   {
14305     player->is_moving = FALSE;
14306     player->is_digging = FALSE;
14307     player->is_collecting = FALSE;
14308   }
14309
14310   player->is_dropping = FALSE;
14311   player->is_dropping_pressed = FALSE;
14312   player->drop_pressed_delay = 0;
14313
14314   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14315     return FALSE;
14316
14317   player->is_snapping = TRUE;
14318   player->is_active = TRUE;
14319
14320   if (player->MovPos == 0)
14321   {
14322     player->is_moving = FALSE;
14323     player->is_digging = FALSE;
14324     player->is_collecting = FALSE;
14325   }
14326
14327   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14328     TEST_DrawLevelField(player->last_jx, player->last_jy);
14329
14330   TEST_DrawLevelField(x, y);
14331
14332   return TRUE;
14333 }
14334
14335 static boolean DropElement(struct PlayerInfo *player)
14336 {
14337   int old_element, new_element;
14338   int dropx = player->jx, dropy = player->jy;
14339   int drop_direction = player->MovDir;
14340   int drop_side = drop_direction;
14341   int drop_element = get_next_dropped_element(player);
14342
14343   /* do not drop an element on top of another element; when holding drop key
14344      pressed without moving, dropped element must move away before the next
14345      element can be dropped (this is especially important if the next element
14346      is dynamite, which can be placed on background for historical reasons) */
14347   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14348     return MP_ACTION;
14349
14350   if (IS_THROWABLE(drop_element))
14351   {
14352     dropx += GET_DX_FROM_DIR(drop_direction);
14353     dropy += GET_DY_FROM_DIR(drop_direction);
14354
14355     if (!IN_LEV_FIELD(dropx, dropy))
14356       return FALSE;
14357   }
14358
14359   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14360   new_element = drop_element;           /* default: no change when dropping */
14361
14362   /* check if player is active, not moving and ready to drop */
14363   if (!player->active || player->MovPos || player->drop_delay > 0)
14364     return FALSE;
14365
14366   /* check if player has anything that can be dropped */
14367   if (new_element == EL_UNDEFINED)
14368     return FALSE;
14369
14370   /* only set if player has anything that can be dropped */
14371   player->is_dropping_pressed = TRUE;
14372
14373   /* check if drop key was pressed long enough for EM style dynamite */
14374   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14375     return FALSE;
14376
14377   /* check if anything can be dropped at the current position */
14378   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14379     return FALSE;
14380
14381   /* collected custom elements can only be dropped on empty fields */
14382   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14383     return FALSE;
14384
14385   if (old_element != EL_EMPTY)
14386     Back[dropx][dropy] = old_element;   /* store old element on this field */
14387
14388   ResetGfxAnimation(dropx, dropy);
14389   ResetRandomAnimationValue(dropx, dropy);
14390
14391   if (player->inventory_size > 0 ||
14392       player->inventory_infinite_element != EL_UNDEFINED)
14393   {
14394     if (player->inventory_size > 0)
14395     {
14396       player->inventory_size--;
14397
14398       DrawGameDoorValues();
14399
14400       if (new_element == EL_DYNAMITE)
14401         new_element = EL_DYNAMITE_ACTIVE;
14402       else if (new_element == EL_EM_DYNAMITE)
14403         new_element = EL_EM_DYNAMITE_ACTIVE;
14404       else if (new_element == EL_SP_DISK_RED)
14405         new_element = EL_SP_DISK_RED_ACTIVE;
14406     }
14407
14408     Feld[dropx][dropy] = new_element;
14409
14410     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14411       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14412                           el2img(Feld[dropx][dropy]), 0);
14413
14414     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14415
14416     /* needed if previous element just changed to "empty" in the last frame */
14417     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14418
14419     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14420                                player->index_bit, drop_side);
14421     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14422                                         CE_PLAYER_DROPS_X,
14423                                         player->index_bit, drop_side);
14424
14425     TestIfElementTouchesCustomElement(dropx, dropy);
14426   }
14427   else          /* player is dropping a dyna bomb */
14428   {
14429     player->dynabombs_left--;
14430
14431     Feld[dropx][dropy] = new_element;
14432
14433     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14434       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14435                           el2img(Feld[dropx][dropy]), 0);
14436
14437     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14438   }
14439
14440   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14441     InitField_WithBug1(dropx, dropy, FALSE);
14442
14443   new_element = Feld[dropx][dropy];     /* element might have changed */
14444
14445   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14446       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14447   {
14448     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14449       MovDir[dropx][dropy] = drop_direction;
14450
14451     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14452
14453     /* do not cause impact style collision by dropping elements that can fall */
14454     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14455   }
14456
14457   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14458   player->is_dropping = TRUE;
14459
14460   player->drop_pressed_delay = 0;
14461   player->is_dropping_pressed = FALSE;
14462
14463   player->drop_x = dropx;
14464   player->drop_y = dropy;
14465
14466   return TRUE;
14467 }
14468
14469 /* ------------------------------------------------------------------------- */
14470 /* game sound playing functions                                              */
14471 /* ------------------------------------------------------------------------- */
14472
14473 static int *loop_sound_frame = NULL;
14474 static int *loop_sound_volume = NULL;
14475
14476 void InitPlayLevelSound()
14477 {
14478   int num_sounds = getSoundListSize();
14479
14480   checked_free(loop_sound_frame);
14481   checked_free(loop_sound_volume);
14482
14483   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14484   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14485 }
14486
14487 static void PlayLevelSound(int x, int y, int nr)
14488 {
14489   int sx = SCREENX(x), sy = SCREENY(y);
14490   int volume, stereo_position;
14491   int max_distance = 8;
14492   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14493
14494   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14495       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14496     return;
14497
14498   if (!IN_LEV_FIELD(x, y) ||
14499       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14500       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14501     return;
14502
14503   volume = SOUND_MAX_VOLUME;
14504
14505   if (!IN_SCR_FIELD(sx, sy))
14506   {
14507     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14508     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14509
14510     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14511   }
14512
14513   stereo_position = (SOUND_MAX_LEFT +
14514                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14515                      (SCR_FIELDX + 2 * max_distance));
14516
14517   if (IS_LOOP_SOUND(nr))
14518   {
14519     /* This assures that quieter loop sounds do not overwrite louder ones,
14520        while restarting sound volume comparison with each new game frame. */
14521
14522     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14523       return;
14524
14525     loop_sound_volume[nr] = volume;
14526     loop_sound_frame[nr] = FrameCounter;
14527   }
14528
14529   PlaySoundExt(nr, volume, stereo_position, type);
14530 }
14531
14532 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14533 {
14534   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14535                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14536                  y < LEVELY(BY1) ? LEVELY(BY1) :
14537                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14538                  sound_action);
14539 }
14540
14541 static void PlayLevelSoundAction(int x, int y, int action)
14542 {
14543   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14544 }
14545
14546 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14547 {
14548   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14549
14550   if (sound_effect != SND_UNDEFINED)
14551     PlayLevelSound(x, y, sound_effect);
14552 }
14553
14554 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14555                                               int action)
14556 {
14557   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14558
14559   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14560     PlayLevelSound(x, y, sound_effect);
14561 }
14562
14563 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14564 {
14565   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14566
14567   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14568     PlayLevelSound(x, y, sound_effect);
14569 }
14570
14571 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14572 {
14573   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14574
14575   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14576     StopSound(sound_effect);
14577 }
14578
14579 static int getLevelMusicNr()
14580 {
14581   if (levelset.music[level_nr] != MUS_UNDEFINED)
14582     return levelset.music[level_nr];            /* from config file */
14583   else
14584     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14585 }
14586
14587 static void FadeLevelSounds()
14588 {
14589   FadeSounds();
14590 }
14591
14592 static void FadeLevelMusic()
14593 {
14594   int music_nr = getLevelMusicNr();
14595   char *curr_music = getCurrentlyPlayingMusicFilename();
14596   char *next_music = getMusicInfoEntryFilename(music_nr);
14597
14598   if (!strEqual(curr_music, next_music))
14599     FadeMusic();
14600 }
14601
14602 void FadeLevelSoundsAndMusic()
14603 {
14604   FadeLevelSounds();
14605   FadeLevelMusic();
14606 }
14607
14608 static void PlayLevelMusic()
14609 {
14610   int music_nr = getLevelMusicNr();
14611   char *curr_music = getCurrentlyPlayingMusicFilename();
14612   char *next_music = getMusicInfoEntryFilename(music_nr);
14613
14614   if (!strEqual(curr_music, next_music))
14615     PlayMusic(music_nr);
14616 }
14617
14618 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14619 {
14620   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14621   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14622   int x = xx - 1 - offset;
14623   int y = yy - 1 - offset;
14624
14625   switch (sample)
14626   {
14627     case SAMPLE_blank:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14629       break;
14630
14631     case SAMPLE_roll:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14633       break;
14634
14635     case SAMPLE_stone:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14637       break;
14638
14639     case SAMPLE_nut:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14641       break;
14642
14643     case SAMPLE_crack:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14645       break;
14646
14647     case SAMPLE_bug:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14649       break;
14650
14651     case SAMPLE_tank:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14653       break;
14654
14655     case SAMPLE_android_clone:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14657       break;
14658
14659     case SAMPLE_android_move:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14661       break;
14662
14663     case SAMPLE_spring:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14665       break;
14666
14667     case SAMPLE_slurp:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14669       break;
14670
14671     case SAMPLE_eater:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14673       break;
14674
14675     case SAMPLE_eater_eat:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14677       break;
14678
14679     case SAMPLE_alien:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14681       break;
14682
14683     case SAMPLE_collect:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14685       break;
14686
14687     case SAMPLE_diamond:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14689       break;
14690
14691     case SAMPLE_squash:
14692       /* !!! CHECK THIS !!! */
14693 #if 1
14694       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14695 #else
14696       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14697 #endif
14698       break;
14699
14700     case SAMPLE_wonderfall:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14702       break;
14703
14704     case SAMPLE_drip:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14706       break;
14707
14708     case SAMPLE_push:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14710       break;
14711
14712     case SAMPLE_dirt:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14714       break;
14715
14716     case SAMPLE_acid:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14718       break;
14719
14720     case SAMPLE_ball:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14722       break;
14723
14724     case SAMPLE_grow:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14726       break;
14727
14728     case SAMPLE_wonder:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14730       break;
14731
14732     case SAMPLE_door:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14734       break;
14735
14736     case SAMPLE_exit_open:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14738       break;
14739
14740     case SAMPLE_exit_leave:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14742       break;
14743
14744     case SAMPLE_dynamite:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14746       break;
14747
14748     case SAMPLE_tick:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14750       break;
14751
14752     case SAMPLE_press:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14754       break;
14755
14756     case SAMPLE_wheel:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14758       break;
14759
14760     case SAMPLE_boom:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14762       break;
14763
14764     case SAMPLE_die:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14766       break;
14767
14768     case SAMPLE_time:
14769       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14770       break;
14771
14772     default:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14774       break;
14775   }
14776 }
14777
14778 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14779 {
14780   int element = map_element_SP_to_RND(element_sp);
14781   int action = map_action_SP_to_RND(action_sp);
14782   int offset = (setup.sp_show_border_elements ? 0 : 1);
14783   int x = xx - offset;
14784   int y = yy - offset;
14785
14786   PlayLevelSoundElementAction(x, y, element, action);
14787 }
14788
14789 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14790 {
14791   int element = map_element_MM_to_RND(element_mm);
14792   int action = map_action_MM_to_RND(action_mm);
14793   int offset = 0;
14794   int x = xx - offset;
14795   int y = yy - offset;
14796
14797   if (!IS_MM_ELEMENT(element))
14798     element = EL_MM_DEFAULT;
14799
14800   PlayLevelSoundElementAction(x, y, element, action);
14801 }
14802
14803 void PlaySound_MM(int sound_mm)
14804 {
14805   int sound = map_sound_MM_to_RND(sound_mm);
14806
14807   if (sound == SND_UNDEFINED)
14808     return;
14809
14810   PlaySound(sound);
14811 }
14812
14813 void PlaySoundLoop_MM(int sound_mm)
14814 {
14815   int sound = map_sound_MM_to_RND(sound_mm);
14816
14817   if (sound == SND_UNDEFINED)
14818     return;
14819
14820   PlaySoundLoop(sound);
14821 }
14822
14823 void StopSound_MM(int sound_mm)
14824 {
14825   int sound = map_sound_MM_to_RND(sound_mm);
14826
14827   if (sound == SND_UNDEFINED)
14828     return;
14829
14830   StopSound(sound);
14831 }
14832
14833 void RaiseScore(int value)
14834 {
14835   local_player->score += value;
14836
14837   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14838
14839   DisplayGameControlValues();
14840 }
14841
14842 void RaiseScoreElement(int element)
14843 {
14844   switch (element)
14845   {
14846     case EL_EMERALD:
14847     case EL_BD_DIAMOND:
14848     case EL_EMERALD_YELLOW:
14849     case EL_EMERALD_RED:
14850     case EL_EMERALD_PURPLE:
14851     case EL_SP_INFOTRON:
14852       RaiseScore(level.score[SC_EMERALD]);
14853       break;
14854     case EL_DIAMOND:
14855       RaiseScore(level.score[SC_DIAMOND]);
14856       break;
14857     case EL_CRYSTAL:
14858       RaiseScore(level.score[SC_CRYSTAL]);
14859       break;
14860     case EL_PEARL:
14861       RaiseScore(level.score[SC_PEARL]);
14862       break;
14863     case EL_BUG:
14864     case EL_BD_BUTTERFLY:
14865     case EL_SP_ELECTRON:
14866       RaiseScore(level.score[SC_BUG]);
14867       break;
14868     case EL_SPACESHIP:
14869     case EL_BD_FIREFLY:
14870     case EL_SP_SNIKSNAK:
14871       RaiseScore(level.score[SC_SPACESHIP]);
14872       break;
14873     case EL_YAMYAM:
14874     case EL_DARK_YAMYAM:
14875       RaiseScore(level.score[SC_YAMYAM]);
14876       break;
14877     case EL_ROBOT:
14878       RaiseScore(level.score[SC_ROBOT]);
14879       break;
14880     case EL_PACMAN:
14881       RaiseScore(level.score[SC_PACMAN]);
14882       break;
14883     case EL_NUT:
14884       RaiseScore(level.score[SC_NUT]);
14885       break;
14886     case EL_DYNAMITE:
14887     case EL_EM_DYNAMITE:
14888     case EL_SP_DISK_RED:
14889     case EL_DYNABOMB_INCREASE_NUMBER:
14890     case EL_DYNABOMB_INCREASE_SIZE:
14891     case EL_DYNABOMB_INCREASE_POWER:
14892       RaiseScore(level.score[SC_DYNAMITE]);
14893       break;
14894     case EL_SHIELD_NORMAL:
14895     case EL_SHIELD_DEADLY:
14896       RaiseScore(level.score[SC_SHIELD]);
14897       break;
14898     case EL_EXTRA_TIME:
14899       RaiseScore(level.extra_time_score);
14900       break;
14901     case EL_KEY_1:
14902     case EL_KEY_2:
14903     case EL_KEY_3:
14904     case EL_KEY_4:
14905     case EL_EM_KEY_1:
14906     case EL_EM_KEY_2:
14907     case EL_EM_KEY_3:
14908     case EL_EM_KEY_4:
14909     case EL_EMC_KEY_5:
14910     case EL_EMC_KEY_6:
14911     case EL_EMC_KEY_7:
14912     case EL_EMC_KEY_8:
14913     case EL_DC_KEY_WHITE:
14914       RaiseScore(level.score[SC_KEY]);
14915       break;
14916     default:
14917       RaiseScore(element_info[element].collect_score);
14918       break;
14919   }
14920 }
14921
14922 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14923 {
14924   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14925   {
14926     /* closing door required in case of envelope style request dialogs */
14927     if (!skip_request)
14928       CloseDoor(DOOR_CLOSE_1);
14929
14930 #if defined(NETWORK_AVALIABLE)
14931     if (options.network)
14932       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14933     else
14934 #endif
14935     {
14936       if (quick_quit)
14937         FadeSkipNextFadeIn();
14938
14939       SetGameStatus(GAME_MODE_MAIN);
14940
14941       DrawMainMenu();
14942     }
14943   }
14944   else          /* continue playing the game */
14945   {
14946     if (tape.playing && tape.deactivate_display)
14947       TapeDeactivateDisplayOff(TRUE);
14948
14949     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14950
14951     if (tape.playing && tape.deactivate_display)
14952       TapeDeactivateDisplayOn();
14953   }
14954 }
14955
14956 void RequestQuitGame(boolean ask_if_really_quit)
14957 {
14958   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14959   boolean skip_request = AllPlayersGone || quick_quit;
14960
14961   RequestQuitGameExt(skip_request, quick_quit,
14962                      "Do you really want to quit the game?");
14963 }
14964
14965 void RequestRestartGame(char *message)
14966 {
14967   game.restart_game_message = NULL;
14968
14969   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14970   {
14971     StartGameActions(options.network, setup.autorecord, level.random_seed);
14972   }
14973   else
14974   {
14975     SetGameStatus(GAME_MODE_MAIN);
14976
14977     DrawMainMenu();
14978   }
14979 }
14980
14981
14982 /* ------------------------------------------------------------------------- */
14983 /* random generator functions                                                */
14984 /* ------------------------------------------------------------------------- */
14985
14986 unsigned int InitEngineRandom_RND(int seed)
14987 {
14988   game.num_random_calls = 0;
14989
14990   return InitEngineRandom(seed);
14991 }
14992
14993 unsigned int RND(int max)
14994 {
14995   if (max > 0)
14996   {
14997     game.num_random_calls++;
14998
14999     return GetEngineRandom(max);
15000   }
15001
15002   return 0;
15003 }
15004
15005
15006 /* ------------------------------------------------------------------------- */
15007 /* game engine snapshot handling functions                                   */
15008 /* ------------------------------------------------------------------------- */
15009
15010 struct EngineSnapshotInfo
15011 {
15012   /* runtime values for custom element collect score */
15013   int collect_score[NUM_CUSTOM_ELEMENTS];
15014
15015   /* runtime values for group element choice position */
15016   int choice_pos[NUM_GROUP_ELEMENTS];
15017
15018   /* runtime values for belt position animations */
15019   int belt_graphic[4][NUM_BELT_PARTS];
15020   int belt_anim_mode[4][NUM_BELT_PARTS];
15021 };
15022
15023 static struct EngineSnapshotInfo engine_snapshot_rnd;
15024 static char *snapshot_level_identifier = NULL;
15025 static int snapshot_level_nr = -1;
15026
15027 static void SaveEngineSnapshotValues_RND()
15028 {
15029   static int belt_base_active_element[4] =
15030   {
15031     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15032     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15033     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15034     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15035   };
15036   int i, j;
15037
15038   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15039   {
15040     int element = EL_CUSTOM_START + i;
15041
15042     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15043   }
15044
15045   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15046   {
15047     int element = EL_GROUP_START + i;
15048
15049     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15050   }
15051
15052   for (i = 0; i < 4; i++)
15053   {
15054     for (j = 0; j < NUM_BELT_PARTS; j++)
15055     {
15056       int element = belt_base_active_element[i] + j;
15057       int graphic = el2img(element);
15058       int anim_mode = graphic_info[graphic].anim_mode;
15059
15060       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15061       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15062     }
15063   }
15064 }
15065
15066 static void LoadEngineSnapshotValues_RND()
15067 {
15068   unsigned int num_random_calls = game.num_random_calls;
15069   int i, j;
15070
15071   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15072   {
15073     int element = EL_CUSTOM_START + i;
15074
15075     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15076   }
15077
15078   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15079   {
15080     int element = EL_GROUP_START + i;
15081
15082     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15083   }
15084
15085   for (i = 0; i < 4; i++)
15086   {
15087     for (j = 0; j < NUM_BELT_PARTS; j++)
15088     {
15089       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15090       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15091
15092       graphic_info[graphic].anim_mode = anim_mode;
15093     }
15094   }
15095
15096   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15097   {
15098     InitRND(tape.random_seed);
15099     for (i = 0; i < num_random_calls; i++)
15100       RND(1);
15101   }
15102
15103   if (game.num_random_calls != num_random_calls)
15104   {
15105     Error(ERR_INFO, "number of random calls out of sync");
15106     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15107     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15108     Error(ERR_EXIT, "this should not happen -- please debug");
15109   }
15110 }
15111
15112 void FreeEngineSnapshotSingle()
15113 {
15114   FreeSnapshotSingle();
15115
15116   setString(&snapshot_level_identifier, NULL);
15117   snapshot_level_nr = -1;
15118 }
15119
15120 void FreeEngineSnapshotList()
15121 {
15122   FreeSnapshotList();
15123 }
15124
15125 ListNode *SaveEngineSnapshotBuffers()
15126 {
15127   ListNode *buffers = NULL;
15128
15129   /* copy some special values to a structure better suited for the snapshot */
15130
15131   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15132     SaveEngineSnapshotValues_RND();
15133   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15134     SaveEngineSnapshotValues_EM();
15135   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15136     SaveEngineSnapshotValues_SP(&buffers);
15137   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15138     SaveEngineSnapshotValues_MM(&buffers);
15139
15140   /* save values stored in special snapshot structure */
15141
15142   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15143     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15144   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15145     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15146   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15147     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15148   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15149     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15150
15151   /* save further RND engine values */
15152
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15156
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15161
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15167
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15171
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15175
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15178
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15197
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15200
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15204
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15207
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15213
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15216
15217 #if 0
15218   ListNode *node = engine_snapshot_list_rnd;
15219   int num_bytes = 0;
15220
15221   while (node != NULL)
15222   {
15223     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15224
15225     node = node->next;
15226   }
15227
15228   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15229 #endif
15230
15231   return buffers;
15232 }
15233
15234 void SaveEngineSnapshotSingle()
15235 {
15236   ListNode *buffers = SaveEngineSnapshotBuffers();
15237
15238   /* finally save all snapshot buffers to single snapshot */
15239   SaveSnapshotSingle(buffers);
15240
15241   /* save level identification information */
15242   setString(&snapshot_level_identifier, leveldir_current->identifier);
15243   snapshot_level_nr = level_nr;
15244 }
15245
15246 boolean CheckSaveEngineSnapshotToList()
15247 {
15248   boolean save_snapshot =
15249     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15250      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15251       game.snapshot.changed_action) ||
15252      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15253       game.snapshot.collected_item));
15254
15255   game.snapshot.changed_action = FALSE;
15256   game.snapshot.collected_item = FALSE;
15257   game.snapshot.save_snapshot = save_snapshot;
15258
15259   return save_snapshot;
15260 }
15261
15262 void SaveEngineSnapshotToList()
15263 {
15264   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15265       tape.quick_resume)
15266     return;
15267
15268   ListNode *buffers = SaveEngineSnapshotBuffers();
15269
15270   /* finally save all snapshot buffers to snapshot list */
15271   SaveSnapshotToList(buffers);
15272 }
15273
15274 void SaveEngineSnapshotToListInitial()
15275 {
15276   FreeEngineSnapshotList();
15277
15278   SaveEngineSnapshotToList();
15279 }
15280
15281 void LoadEngineSnapshotValues()
15282 {
15283   /* restore special values from snapshot structure */
15284
15285   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15286     LoadEngineSnapshotValues_RND();
15287   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15288     LoadEngineSnapshotValues_EM();
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15290     LoadEngineSnapshotValues_SP();
15291   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15292     LoadEngineSnapshotValues_MM();
15293 }
15294
15295 void LoadEngineSnapshotSingle()
15296 {
15297   LoadSnapshotSingle();
15298
15299   LoadEngineSnapshotValues();
15300 }
15301
15302 void LoadEngineSnapshot_Undo(int steps)
15303 {
15304   LoadSnapshotFromList_Older(steps);
15305
15306   LoadEngineSnapshotValues();
15307 }
15308
15309 void LoadEngineSnapshot_Redo(int steps)
15310 {
15311   LoadSnapshotFromList_Newer(steps);
15312
15313   LoadEngineSnapshotValues();
15314 }
15315
15316 boolean CheckEngineSnapshotSingle()
15317 {
15318   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15319           snapshot_level_nr == level_nr);
15320 }
15321
15322 boolean CheckEngineSnapshotList()
15323 {
15324   return CheckSnapshotList();
15325 }
15326
15327
15328 /* ---------- new game button stuff ---------------------------------------- */
15329
15330 static struct
15331 {
15332   int graphic;
15333   struct XY *pos;
15334   int gadget_id;
15335   boolean *setup_value;
15336   boolean allowed_on_tape;
15337   char *infotext;
15338 } gamebutton_info[NUM_GAME_BUTTONS] =
15339 {
15340   {
15341     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15342     GAME_CTRL_ID_STOP,                          NULL,
15343     TRUE,                                       "stop game"
15344   },
15345   {
15346     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15347     GAME_CTRL_ID_PAUSE,                         NULL,
15348     TRUE,                                       "pause game"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15352     GAME_CTRL_ID_PLAY,                          NULL,
15353     TRUE,                                       "play game"
15354   },
15355   {
15356     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15357     GAME_CTRL_ID_UNDO,                          NULL,
15358     TRUE,                                       "undo step"
15359   },
15360   {
15361     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15362     GAME_CTRL_ID_REDO,                          NULL,
15363     TRUE,                                       "redo step"
15364   },
15365   {
15366     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15367     GAME_CTRL_ID_SAVE,                          NULL,
15368     TRUE,                                       "save game"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15372     GAME_CTRL_ID_PAUSE2,                        NULL,
15373     TRUE,                                       "pause game"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15377     GAME_CTRL_ID_LOAD,                          NULL,
15378     TRUE,                                       "load game"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15382     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15383     FALSE,                                      "stop game"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15387     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15388     FALSE,                                      "pause game"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15392     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15393     FALSE,                                      "play game"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15397     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15398     TRUE,                                       "background music on/off"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15402     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15403     TRUE,                                       "sound loops on/off"
15404   },
15405   {
15406     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15407     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15408     TRUE,                                       "normal sounds on/off"
15409   },
15410   {
15411     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15412     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15413     FALSE,                                      "background music on/off"
15414   },
15415   {
15416     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15417     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15418     FALSE,                                      "sound loops on/off"
15419   },
15420   {
15421     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15422     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15423     FALSE,                                      "normal sounds on/off"
15424   }
15425 };
15426
15427 void CreateGameButtons()
15428 {
15429   int i;
15430
15431   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15432   {
15433     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15434     struct XY *pos = gamebutton_info[i].pos;
15435     struct GadgetInfo *gi;
15436     int button_type;
15437     boolean checked;
15438     unsigned int event_mask;
15439     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15440     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15441     int base_x = (on_tape ? VX : DX);
15442     int base_y = (on_tape ? VY : DY);
15443     int gd_x   = gfx->src_x;
15444     int gd_y   = gfx->src_y;
15445     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15446     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15447     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15448     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15449     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15450     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15451     int id = i;
15452
15453     if (gfx->bitmap == NULL)
15454     {
15455       game_gadget[id] = NULL;
15456
15457       continue;
15458     }
15459
15460     if (id == GAME_CTRL_ID_STOP ||
15461         id == GAME_CTRL_ID_PANEL_STOP ||
15462         id == GAME_CTRL_ID_PLAY ||
15463         id == GAME_CTRL_ID_PANEL_PLAY ||
15464         id == GAME_CTRL_ID_SAVE ||
15465         id == GAME_CTRL_ID_LOAD)
15466     {
15467       button_type = GD_TYPE_NORMAL_BUTTON;
15468       checked = FALSE;
15469       event_mask = GD_EVENT_RELEASED;
15470     }
15471     else if (id == GAME_CTRL_ID_UNDO ||
15472              id == GAME_CTRL_ID_REDO)
15473     {
15474       button_type = GD_TYPE_NORMAL_BUTTON;
15475       checked = FALSE;
15476       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15477     }
15478     else
15479     {
15480       button_type = GD_TYPE_CHECK_BUTTON;
15481       checked = (gamebutton_info[i].setup_value != NULL ?
15482                  *gamebutton_info[i].setup_value : FALSE);
15483       event_mask = GD_EVENT_PRESSED;
15484     }
15485
15486     gi = CreateGadget(GDI_CUSTOM_ID, id,
15487                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15488                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15489                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15490                       GDI_WIDTH, gfx->width,
15491                       GDI_HEIGHT, gfx->height,
15492                       GDI_TYPE, button_type,
15493                       GDI_STATE, GD_BUTTON_UNPRESSED,
15494                       GDI_CHECKED, checked,
15495                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15496                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15497                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15498                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15499                       GDI_DIRECT_DRAW, FALSE,
15500                       GDI_EVENT_MASK, event_mask,
15501                       GDI_CALLBACK_ACTION, HandleGameButtons,
15502                       GDI_END);
15503
15504     if (gi == NULL)
15505       Error(ERR_EXIT, "cannot create gadget");
15506
15507     game_gadget[id] = gi;
15508   }
15509 }
15510
15511 void FreeGameButtons()
15512 {
15513   int i;
15514
15515   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15516     FreeGadget(game_gadget[i]);
15517 }
15518
15519 static void UnmapGameButtonsAtSamePosition(int id)
15520 {
15521   int i;
15522
15523   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15524     if (i != id &&
15525         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15526         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15527       UnmapGadget(game_gadget[i]);
15528 }
15529
15530 static void UnmapGameButtonsAtSamePosition_All()
15531 {
15532   if (setup.show_snapshot_buttons)
15533   {
15534     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15535     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15536     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15537   }
15538   else
15539   {
15540     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15541     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15542     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15543
15544     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15545     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15546     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15547   }
15548 }
15549
15550 static void MapGameButtonsAtSamePosition(int id)
15551 {
15552   int i;
15553
15554   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15555     if (i != id &&
15556         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15557         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15558       MapGadget(game_gadget[i]);
15559
15560   UnmapGameButtonsAtSamePosition_All();
15561 }
15562
15563 void MapUndoRedoButtons()
15564 {
15565   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15566   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15567
15568   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15569   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15570
15571   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15572 }
15573
15574 void UnmapUndoRedoButtons()
15575 {
15576   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15577   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15578
15579   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15580   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15581
15582   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15583 }
15584
15585 void MapGameButtonsExt(boolean on_tape)
15586 {
15587   int i;
15588
15589   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15590     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15591         i != GAME_CTRL_ID_UNDO &&
15592         i != GAME_CTRL_ID_REDO)
15593       MapGadget(game_gadget[i]);
15594
15595   UnmapGameButtonsAtSamePosition_All();
15596
15597   RedrawGameButtons();
15598 }
15599
15600 void UnmapGameButtonsExt(boolean on_tape)
15601 {
15602   int i;
15603
15604   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15605     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15606       UnmapGadget(game_gadget[i]);
15607 }
15608
15609 void RedrawGameButtonsExt(boolean on_tape)
15610 {
15611   int i;
15612
15613   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15614     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15615       RedrawGadget(game_gadget[i]);
15616
15617   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15618   redraw_mask &= ~REDRAW_ALL;
15619 }
15620
15621 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15622 {
15623   if (gi == NULL)
15624     return;
15625
15626   gi->checked = state;
15627 }
15628
15629 void RedrawSoundButtonGadget(int id)
15630 {
15631   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15632              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15633              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15634              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15635              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15636              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15637              id);
15638
15639   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15640   RedrawGadget(game_gadget[id2]);
15641 }
15642
15643 void MapGameButtons()
15644 {
15645   MapGameButtonsExt(FALSE);
15646 }
15647
15648 void UnmapGameButtons()
15649 {
15650   UnmapGameButtonsExt(FALSE);
15651 }
15652
15653 void RedrawGameButtons()
15654 {
15655   RedrawGameButtonsExt(FALSE);
15656 }
15657
15658 void MapGameButtonsOnTape()
15659 {
15660   MapGameButtonsExt(TRUE);
15661 }
15662
15663 void UnmapGameButtonsOnTape()
15664 {
15665   UnmapGameButtonsExt(TRUE);
15666 }
15667
15668 void RedrawGameButtonsOnTape()
15669 {
15670   RedrawGameButtonsExt(TRUE);
15671 }
15672
15673 void GameUndoRedoExt()
15674 {
15675   ClearPlayerAction();
15676
15677   tape.pausing = TRUE;
15678
15679   RedrawPlayfield();
15680   UpdateAndDisplayGameControlValues();
15681
15682   DrawCompleteVideoDisplay();
15683   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15684   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15685   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15686
15687   BackToFront();
15688 }
15689
15690 void GameUndo(int steps)
15691 {
15692   if (!CheckEngineSnapshotList())
15693     return;
15694
15695   LoadEngineSnapshot_Undo(steps);
15696
15697   GameUndoRedoExt();
15698 }
15699
15700 void GameRedo(int steps)
15701 {
15702   if (!CheckEngineSnapshotList())
15703     return;
15704
15705   LoadEngineSnapshot_Redo(steps);
15706
15707   GameUndoRedoExt();
15708 }
15709
15710 static void HandleGameButtonsExt(int id, int button)
15711 {
15712   static boolean game_undo_executed = FALSE;
15713   int steps = BUTTON_STEPSIZE(button);
15714   boolean handle_game_buttons =
15715     (game_status == GAME_MODE_PLAYING ||
15716      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15717
15718   if (!handle_game_buttons)
15719     return;
15720
15721   switch (id)
15722   {
15723     case GAME_CTRL_ID_STOP:
15724     case GAME_CTRL_ID_PANEL_STOP:
15725       if (game_status == GAME_MODE_MAIN)
15726         break;
15727
15728       if (tape.playing)
15729         TapeStop();
15730       else
15731         RequestQuitGame(TRUE);
15732
15733       break;
15734
15735     case GAME_CTRL_ID_PAUSE:
15736     case GAME_CTRL_ID_PAUSE2:
15737     case GAME_CTRL_ID_PANEL_PAUSE:
15738       if (options.network && game_status == GAME_MODE_PLAYING)
15739       {
15740 #if defined(NETWORK_AVALIABLE)
15741         if (tape.pausing)
15742           SendToServer_ContinuePlaying();
15743         else
15744           SendToServer_PausePlaying();
15745 #endif
15746       }
15747       else
15748         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15749
15750       game_undo_executed = FALSE;
15751
15752       break;
15753
15754     case GAME_CTRL_ID_PLAY:
15755     case GAME_CTRL_ID_PANEL_PLAY:
15756       if (game_status == GAME_MODE_MAIN)
15757       {
15758         StartGameActions(options.network, setup.autorecord, level.random_seed);
15759       }
15760       else if (tape.pausing)
15761       {
15762 #if defined(NETWORK_AVALIABLE)
15763         if (options.network)
15764           SendToServer_ContinuePlaying();
15765         else
15766 #endif
15767           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15768       }
15769       break;
15770
15771     case GAME_CTRL_ID_UNDO:
15772       // Important: When using "save snapshot when collecting an item" mode,
15773       // load last (current) snapshot for first "undo" after pressing "pause"
15774       // (else the last-but-one snapshot would be loaded, because the snapshot
15775       // pointer already points to the last snapshot when pressing "pause",
15776       // which is fine for "every step/move" mode, but not for "every collect")
15777       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15778           !game_undo_executed)
15779         steps--;
15780
15781       game_undo_executed = TRUE;
15782
15783       GameUndo(steps);
15784       break;
15785
15786     case GAME_CTRL_ID_REDO:
15787       GameRedo(steps);
15788       break;
15789
15790     case GAME_CTRL_ID_SAVE:
15791       TapeQuickSave();
15792       break;
15793
15794     case GAME_CTRL_ID_LOAD:
15795       TapeQuickLoad();
15796       break;
15797
15798     case SOUND_CTRL_ID_MUSIC:
15799     case SOUND_CTRL_ID_PANEL_MUSIC:
15800       if (setup.sound_music)
15801       { 
15802         setup.sound_music = FALSE;
15803
15804         FadeMusic();
15805       }
15806       else if (audio.music_available)
15807       { 
15808         setup.sound = setup.sound_music = TRUE;
15809
15810         SetAudioMode(setup.sound);
15811
15812         if (game_status == GAME_MODE_PLAYING)
15813           PlayLevelMusic();
15814       }
15815
15816       RedrawSoundButtonGadget(id);
15817
15818       break;
15819
15820     case SOUND_CTRL_ID_LOOPS:
15821     case SOUND_CTRL_ID_PANEL_LOOPS:
15822       if (setup.sound_loops)
15823         setup.sound_loops = FALSE;
15824       else if (audio.loops_available)
15825       {
15826         setup.sound = setup.sound_loops = TRUE;
15827
15828         SetAudioMode(setup.sound);
15829       }
15830
15831       RedrawSoundButtonGadget(id);
15832
15833       break;
15834
15835     case SOUND_CTRL_ID_SIMPLE:
15836     case SOUND_CTRL_ID_PANEL_SIMPLE:
15837       if (setup.sound_simple)
15838         setup.sound_simple = FALSE;
15839       else if (audio.sound_available)
15840       {
15841         setup.sound = setup.sound_simple = TRUE;
15842
15843         SetAudioMode(setup.sound);
15844       }
15845
15846       RedrawSoundButtonGadget(id);
15847
15848       break;
15849
15850     default:
15851       break;
15852   }
15853 }
15854
15855 static void HandleGameButtons(struct GadgetInfo *gi)
15856 {
15857   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15858 }
15859
15860 void HandleSoundButtonKeys(Key key)
15861 {
15862   if (key == setup.shortcut.sound_simple)
15863     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15864   else if (key == setup.shortcut.sound_loops)
15865     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15866   else if (key == setup.shortcut.sound_music)
15867     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15868 }