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