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