6d07ec5489b05a0a8cec073449b8bd2af0e6eed3
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!options.network || player->connected)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (options.network && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348     player->mouse_action.button_hint = 0;
3349
3350     player->effective_mouse_action.lx = 0;
3351     player->effective_mouse_action.ly = 0;
3352     player->effective_mouse_action.button = 0;
3353     player->effective_mouse_action.button_hint = 0;
3354
3355     player->score = 0;
3356     player->score_final = 0;
3357
3358     player->health = MAX_HEALTH;
3359     player->health_final = MAX_HEALTH;
3360
3361     player->gems_still_needed = level.gems_needed;
3362     player->sokobanfields_still_needed = 0;
3363     player->lights_still_needed = 0;
3364     player->friends_still_needed = 0;
3365
3366     for (j = 0; j < MAX_NUM_KEYS; j++)
3367       player->key[j] = FALSE;
3368
3369     player->num_white_keys = 0;
3370
3371     player->dynabomb_count = 0;
3372     player->dynabomb_size = 1;
3373     player->dynabombs_left = 0;
3374     player->dynabomb_xl = FALSE;
3375
3376     player->MovDir = initial_move_dir;
3377     player->MovPos = 0;
3378     player->GfxPos = 0;
3379     player->GfxDir = initial_move_dir;
3380     player->GfxAction = ACTION_DEFAULT;
3381     player->Frame = 0;
3382     player->StepFrame = 0;
3383
3384     player->initial_element = player->element_nr;
3385     player->artwork_element =
3386       (level.use_artwork_element[i] ? level.artwork_element[i] :
3387        player->element_nr);
3388     player->use_murphy = FALSE;
3389
3390     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3391     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3392
3393     player->gravity = level.initial_player_gravity[i];
3394
3395     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3396
3397     player->actual_frame_counter = 0;
3398
3399     player->step_counter = 0;
3400
3401     player->last_move_dir = initial_move_dir;
3402
3403     player->is_active = FALSE;
3404
3405     player->is_waiting = FALSE;
3406     player->is_moving = FALSE;
3407     player->is_auto_moving = FALSE;
3408     player->is_digging = FALSE;
3409     player->is_snapping = FALSE;
3410     player->is_collecting = FALSE;
3411     player->is_pushing = FALSE;
3412     player->is_switching = FALSE;
3413     player->is_dropping = FALSE;
3414     player->is_dropping_pressed = FALSE;
3415
3416     player->is_bored = FALSE;
3417     player->is_sleeping = FALSE;
3418
3419     player->was_waiting = TRUE;
3420     player->was_moving = FALSE;
3421     player->was_snapping = FALSE;
3422     player->was_dropping = FALSE;
3423
3424     player->force_dropping = FALSE;
3425
3426     player->frame_counter_bored = -1;
3427     player->frame_counter_sleeping = -1;
3428
3429     player->anim_delay_counter = 0;
3430     player->post_delay_counter = 0;
3431
3432     player->dir_waiting = initial_move_dir;
3433     player->action_waiting = ACTION_DEFAULT;
3434     player->last_action_waiting = ACTION_DEFAULT;
3435     player->special_action_bored = ACTION_DEFAULT;
3436     player->special_action_sleeping = ACTION_DEFAULT;
3437
3438     player->switch_x = -1;
3439     player->switch_y = -1;
3440
3441     player->drop_x = -1;
3442     player->drop_y = -1;
3443
3444     player->show_envelope = 0;
3445
3446     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3447
3448     player->push_delay       = -1;      /* initialized when pushing starts */
3449     player->push_delay_value = game.initial_push_delay_value;
3450
3451     player->drop_delay = 0;
3452     player->drop_pressed_delay = 0;
3453
3454     player->last_jx = -1;
3455     player->last_jy = -1;
3456     player->jx = -1;
3457     player->jy = -1;
3458
3459     player->shield_normal_time_left = 0;
3460     player->shield_deadly_time_left = 0;
3461
3462     player->inventory_infinite_element = EL_UNDEFINED;
3463     player->inventory_size = 0;
3464
3465     if (level.use_initial_inventory[i])
3466     {
3467       for (j = 0; j < level.initial_inventory_size[i]; j++)
3468       {
3469         int element = level.initial_inventory_content[i][j];
3470         int collect_count = element_info[element].collect_count_initial;
3471         int k;
3472
3473         if (!IS_CUSTOM_ELEMENT(element))
3474           collect_count = 1;
3475
3476         if (collect_count == 0)
3477           player->inventory_infinite_element = element;
3478         else
3479           for (k = 0; k < collect_count; k++)
3480             if (player->inventory_size < MAX_INVENTORY_SIZE)
3481               player->inventory_element[player->inventory_size++] = element;
3482       }
3483     }
3484
3485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486     SnapField(player, 0, 0);
3487
3488     player->LevelSolved = FALSE;
3489     player->GameOver = FALSE;
3490
3491     player->LevelSolved_GameWon = FALSE;
3492     player->LevelSolved_GameEnd = FALSE;
3493     player->LevelSolved_PanelOff = FALSE;
3494     player->LevelSolved_SaveTape = FALSE;
3495     player->LevelSolved_SaveScore = FALSE;
3496
3497     player->LevelSolved_CountingTime = 0;
3498     player->LevelSolved_CountingScore = 0;
3499     player->LevelSolved_CountingHealth = 0;
3500
3501     map_player_action[i] = i;
3502   }
3503
3504   network_player_action_received = FALSE;
3505
3506 #if defined(NETWORK_AVALIABLE)
3507   /* initial null action */
3508   if (network_playing)
3509     SendToServer_MovePlayer(MV_NONE);
3510 #endif
3511
3512   ZX = ZY = -1;
3513   ExitX = ExitY = -1;
3514
3515   FrameCounter = 0;
3516   TimeFrames = 0;
3517   TimePlayed = 0;
3518   TimeLeft = level.time;
3519   TapeTime = 0;
3520
3521   ScreenMovDir = MV_NONE;
3522   ScreenMovPos = 0;
3523   ScreenGfxPos = 0;
3524
3525   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3526
3527   AllPlayersGone = FALSE;
3528
3529   game.no_time_limit = (level.time == 0);
3530
3531   game.yamyam_content_nr = 0;
3532   game.robot_wheel_active = FALSE;
3533   game.magic_wall_active = FALSE;
3534   game.magic_wall_time_left = 0;
3535   game.light_time_left = 0;
3536   game.timegate_time_left = 0;
3537   game.switchgate_pos = 0;
3538   game.wind_direction = level.wind_direction_initial;
3539
3540   game.lenses_time_left = 0;
3541   game.magnify_time_left = 0;
3542
3543   game.ball_state = level.ball_state_initial;
3544   game.ball_content_nr = 0;
3545
3546   game.envelope_active = FALSE;
3547
3548   /* set focus to local player for network games, else to all players */
3549   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3550   game.centered_player_nr_next = game.centered_player_nr;
3551   game.set_centered_player = FALSE;
3552
3553   if (network_playing && tape.recording)
3554   {
3555     /* store client dependent player focus when recording network games */
3556     tape.centered_player_nr_next = game.centered_player_nr_next;
3557     tape.set_centered_player = TRUE;
3558   }
3559
3560   for (i = 0; i < NUM_BELTS; i++)
3561   {
3562     game.belt_dir[i] = MV_NONE;
3563     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3564   }
3565
3566   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3567     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3568
3569 #if DEBUG_INIT_PLAYER
3570   if (options.debug)
3571   {
3572     printf("Player status at level initialization:\n");
3573   }
3574 #endif
3575
3576   SCAN_PLAYFIELD(x, y)
3577   {
3578     Feld[x][y] = level.field[x][y];
3579     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3580     ChangeDelay[x][y] = 0;
3581     ChangePage[x][y] = -1;
3582     CustomValue[x][y] = 0;              /* initialized in InitField() */
3583     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3584     AmoebaNr[x][y] = 0;
3585     WasJustMoving[x][y] = 0;
3586     WasJustFalling[x][y] = 0;
3587     CheckCollision[x][y] = 0;
3588     CheckImpact[x][y] = 0;
3589     Stop[x][y] = FALSE;
3590     Pushed[x][y] = FALSE;
3591
3592     ChangeCount[x][y] = 0;
3593     ChangeEvent[x][y] = -1;
3594
3595     ExplodePhase[x][y] = 0;
3596     ExplodeDelay[x][y] = 0;
3597     ExplodeField[x][y] = EX_TYPE_NONE;
3598
3599     RunnerVisit[x][y] = 0;
3600     PlayerVisit[x][y] = 0;
3601
3602     GfxFrame[x][y] = 0;
3603     GfxRandom[x][y] = INIT_GFX_RANDOM();
3604     GfxElement[x][y] = EL_UNDEFINED;
3605     GfxAction[x][y] = ACTION_DEFAULT;
3606     GfxDir[x][y] = MV_NONE;
3607     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3608   }
3609
3610   SCAN_PLAYFIELD(x, y)
3611   {
3612     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3613       emulate_bd = FALSE;
3614     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3615       emulate_sb = FALSE;
3616     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3617       emulate_sp = FALSE;
3618
3619     InitField(x, y, TRUE);
3620
3621     ResetGfxAnimation(x, y);
3622   }
3623
3624   InitBeltMovement();
3625
3626   for (i = 0; i < MAX_PLAYERS; i++)
3627   {
3628     struct PlayerInfo *player = &stored_player[i];
3629
3630     /* set number of special actions for bored and sleeping animation */
3631     player->num_special_action_bored =
3632       get_num_special_action(player->artwork_element,
3633                              ACTION_BORING_1, ACTION_BORING_LAST);
3634     player->num_special_action_sleeping =
3635       get_num_special_action(player->artwork_element,
3636                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3637   }
3638
3639   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3640                     emulate_sb ? EMU_SOKOBAN :
3641                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3642
3643   /* initialize type of slippery elements */
3644   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3645   {
3646     if (!IS_CUSTOM_ELEMENT(i))
3647     {
3648       /* default: elements slip down either to the left or right randomly */
3649       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3650
3651       /* SP style elements prefer to slip down on the left side */
3652       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3653         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3654
3655       /* BD style elements prefer to slip down on the left side */
3656       if (game.emulation == EMU_BOULDERDASH)
3657         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658     }
3659   }
3660
3661   /* initialize explosion and ignition delay */
3662   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3663   {
3664     if (!IS_CUSTOM_ELEMENT(i))
3665     {
3666       int num_phase = 8;
3667       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3668                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3669                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670       int last_phase = (num_phase + 1) * delay;
3671       int half_phase = (num_phase / 2) * delay;
3672
3673       element_info[i].explosion_delay = last_phase - 1;
3674       element_info[i].ignition_delay = half_phase;
3675
3676       if (i == EL_BLACK_ORB)
3677         element_info[i].ignition_delay = 1;
3678     }
3679   }
3680
3681   /* correct non-moving belts to start moving left */
3682   for (i = 0; i < NUM_BELTS; i++)
3683     if (game.belt_dir[i] == MV_NONE)
3684       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3685
3686 #if USE_NEW_PLAYER_ASSIGNMENTS
3687   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3688   /* choose default local player */
3689   local_player = &stored_player[0];
3690
3691   for (i = 0; i < MAX_PLAYERS; i++)
3692     stored_player[i].connected = FALSE;
3693
3694   local_player->connected = TRUE;
3695   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3696
3697   if (tape.playing)
3698   {
3699     for (i = 0; i < MAX_PLAYERS; i++)
3700       stored_player[i].connected = tape.player_participates[i];
3701   }
3702   else if (game.team_mode && !options.network)
3703   {
3704     /* try to guess locally connected team mode players (needed for correct
3705        assignment of player figures from level to locally playing players) */
3706
3707     for (i = 0; i < MAX_PLAYERS; i++)
3708       if (setup.input[i].use_joystick ||
3709           setup.input[i].key.left != KSYM_UNDEFINED)
3710         stored_player[i].connected = TRUE;
3711   }
3712
3713 #if DEBUG_INIT_PLAYER
3714   if (options.debug)
3715   {
3716     printf("Player status after level initialization:\n");
3717
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719     {
3720       struct PlayerInfo *player = &stored_player[i];
3721
3722       printf("- player %d: present == %d, connected == %d, active == %d",
3723              i + 1,
3724              player->present,
3725              player->connected,
3726              player->active);
3727
3728       if (local_player == player)
3729         printf(" (local player)");
3730
3731       printf("\n");
3732     }
3733   }
3734 #endif
3735
3736 #if DEBUG_INIT_PLAYER
3737   if (options.debug)
3738     printf("Reassigning players ...\n");
3739 #endif
3740
3741   /* check if any connected player was not found in playfield */
3742   for (i = 0; i < MAX_PLAYERS; i++)
3743   {
3744     struct PlayerInfo *player = &stored_player[i];
3745
3746     if (player->connected && !player->present)
3747     {
3748       struct PlayerInfo *field_player = NULL;
3749
3750 #if DEBUG_INIT_PLAYER
3751       if (options.debug)
3752         printf("- looking for field player for player %d ...\n", i + 1);
3753 #endif
3754
3755       /* assign first free player found that is present in the playfield */
3756
3757       /* first try: look for unmapped playfield player that is not connected */
3758       for (j = 0; j < MAX_PLAYERS; j++)
3759         if (field_player == NULL &&
3760             stored_player[j].present &&
3761             !stored_player[j].mapped &&
3762             !stored_player[j].connected)
3763           field_player = &stored_player[j];
3764
3765       /* second try: look for *any* unmapped playfield player */
3766       for (j = 0; j < MAX_PLAYERS; j++)
3767         if (field_player == NULL &&
3768             stored_player[j].present &&
3769             !stored_player[j].mapped)
3770           field_player = &stored_player[j];
3771
3772       if (field_player != NULL)
3773       {
3774         int jx = field_player->jx, jy = field_player->jy;
3775
3776 #if DEBUG_INIT_PLAYER
3777         if (options.debug)
3778           printf("- found player %d\n", field_player->index_nr + 1);
3779 #endif
3780
3781         player->present = FALSE;
3782         player->active = FALSE;
3783
3784         field_player->present = TRUE;
3785         field_player->active = TRUE;
3786
3787         /*
3788         player->initial_element = field_player->initial_element;
3789         player->artwork_element = field_player->artwork_element;
3790
3791         player->block_last_field       = field_player->block_last_field;
3792         player->block_delay_adjustment = field_player->block_delay_adjustment;
3793         */
3794
3795         StorePlayer[jx][jy] = field_player->element_nr;
3796
3797         field_player->jx = field_player->last_jx = jx;
3798         field_player->jy = field_player->last_jy = jy;
3799
3800         if (local_player == player)
3801           local_player = field_player;
3802
3803         map_player_action[field_player->index_nr] = i;
3804
3805         field_player->mapped = TRUE;
3806
3807 #if DEBUG_INIT_PLAYER
3808         if (options.debug)
3809           printf("- map_player_action[%d] == %d\n",
3810                  field_player->index_nr + 1, i + 1);
3811 #endif
3812       }
3813     }
3814
3815     if (player->connected && player->present)
3816       player->mapped = TRUE;
3817   }
3818
3819 #if DEBUG_INIT_PLAYER
3820   if (options.debug)
3821   {
3822     printf("Player status after player assignment (first stage):\n");
3823
3824     for (i = 0; i < MAX_PLAYERS; i++)
3825     {
3826       struct PlayerInfo *player = &stored_player[i];
3827
3828       printf("- player %d: present == %d, connected == %d, active == %d",
3829              i + 1,
3830              player->present,
3831              player->connected,
3832              player->active);
3833
3834       if (local_player == player)
3835         printf(" (local player)");
3836
3837       printf("\n");
3838     }
3839   }
3840 #endif
3841
3842 #else
3843
3844   /* check if any connected player was not found in playfield */
3845   for (i = 0; i < MAX_PLAYERS; i++)
3846   {
3847     struct PlayerInfo *player = &stored_player[i];
3848
3849     if (player->connected && !player->present)
3850     {
3851       for (j = 0; j < MAX_PLAYERS; j++)
3852       {
3853         struct PlayerInfo *field_player = &stored_player[j];
3854         int jx = field_player->jx, jy = field_player->jy;
3855
3856         /* assign first free player found that is present in the playfield */
3857         if (field_player->present && !field_player->connected)
3858         {
3859           player->present = TRUE;
3860           player->active = TRUE;
3861
3862           field_player->present = FALSE;
3863           field_player->active = FALSE;
3864
3865           player->initial_element = field_player->initial_element;
3866           player->artwork_element = field_player->artwork_element;
3867
3868           player->block_last_field       = field_player->block_last_field;
3869           player->block_delay_adjustment = field_player->block_delay_adjustment;
3870
3871           StorePlayer[jx][jy] = player->element_nr;
3872
3873           player->jx = player->last_jx = jx;
3874           player->jy = player->last_jy = jy;
3875
3876           break;
3877         }
3878       }
3879     }
3880   }
3881 #endif
3882
3883 #if 0
3884   printf("::: local_player->present == %d\n", local_player->present);
3885 #endif
3886
3887   if (tape.playing)
3888   {
3889     /* when playing a tape, eliminate all players who do not participate */
3890
3891 #if USE_NEW_PLAYER_ASSIGNMENTS
3892
3893     if (!game.team_mode)
3894     {
3895       for (i = 0; i < MAX_PLAYERS; i++)
3896       {
3897         if (stored_player[i].active &&
3898             !tape.player_participates[map_player_action[i]])
3899         {
3900           struct PlayerInfo *player = &stored_player[i];
3901           int jx = player->jx, jy = player->jy;
3902
3903 #if DEBUG_INIT_PLAYER
3904           if (options.debug)
3905             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3906 #endif
3907
3908           player->active = FALSE;
3909           StorePlayer[jx][jy] = 0;
3910           Feld[jx][jy] = EL_EMPTY;
3911         }
3912       }
3913     }
3914
3915 #else
3916
3917     for (i = 0; i < MAX_PLAYERS; i++)
3918     {
3919       if (stored_player[i].active &&
3920           !tape.player_participates[i])
3921       {
3922         struct PlayerInfo *player = &stored_player[i];
3923         int jx = player->jx, jy = player->jy;
3924
3925         player->active = FALSE;
3926         StorePlayer[jx][jy] = 0;
3927         Feld[jx][jy] = EL_EMPTY;
3928       }
3929     }
3930 #endif
3931   }
3932   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3933   {
3934     /* when in single player mode, eliminate all but the first active player */
3935
3936     for (i = 0; i < MAX_PLAYERS; i++)
3937     {
3938       if (stored_player[i].active)
3939       {
3940         for (j = i + 1; j < MAX_PLAYERS; j++)
3941         {
3942           if (stored_player[j].active)
3943           {
3944             struct PlayerInfo *player = &stored_player[j];
3945             int jx = player->jx, jy = player->jy;
3946
3947             player->active = FALSE;
3948             player->present = FALSE;
3949
3950             StorePlayer[jx][jy] = 0;
3951             Feld[jx][jy] = EL_EMPTY;
3952           }
3953         }
3954       }
3955     }
3956   }
3957
3958   /* when recording the game, store which players take part in the game */
3959   if (tape.recording)
3960   {
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963       if (stored_player[i].connected)
3964         tape.player_participates[i] = TRUE;
3965 #else
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (stored_player[i].active)
3968         tape.player_participates[i] = TRUE;
3969 #endif
3970   }
3971
3972 #if DEBUG_INIT_PLAYER
3973   if (options.debug)
3974   {
3975     printf("Player status after player assignment (final stage):\n");
3976
3977     for (i = 0; i < MAX_PLAYERS; i++)
3978     {
3979       struct PlayerInfo *player = &stored_player[i];
3980
3981       printf("- player %d: present == %d, connected == %d, active == %d",
3982              i + 1,
3983              player->present,
3984              player->connected,
3985              player->active);
3986
3987       if (local_player == player)
3988         printf(" (local player)");
3989
3990       printf("\n");
3991     }
3992   }
3993 #endif
3994
3995   if (BorderElement == EL_EMPTY)
3996   {
3997     SBX_Left = 0;
3998     SBX_Right = lev_fieldx - SCR_FIELDX;
3999     SBY_Upper = 0;
4000     SBY_Lower = lev_fieldy - SCR_FIELDY;
4001   }
4002   else
4003   {
4004     SBX_Left = -1;
4005     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4006     SBY_Upper = -1;
4007     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4008   }
4009
4010   if (full_lev_fieldx <= SCR_FIELDX)
4011     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4012   if (full_lev_fieldy <= SCR_FIELDY)
4013     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4014
4015   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4016     SBX_Left--;
4017   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4018     SBY_Upper--;
4019
4020   /* if local player not found, look for custom element that might create
4021      the player (make some assumptions about the right custom element) */
4022   if (!local_player->present)
4023   {
4024     int start_x = 0, start_y = 0;
4025     int found_rating = 0;
4026     int found_element = EL_UNDEFINED;
4027     int player_nr = local_player->index_nr;
4028
4029     SCAN_PLAYFIELD(x, y)
4030     {
4031       int element = Feld[x][y];
4032       int content;
4033       int xx, yy;
4034       boolean is_player;
4035
4036       if (level.use_start_element[player_nr] &&
4037           level.start_element[player_nr] == element &&
4038           found_rating < 4)
4039       {
4040         start_x = x;
4041         start_y = y;
4042
4043         found_rating = 4;
4044         found_element = element;
4045       }
4046
4047       if (!IS_CUSTOM_ELEMENT(element))
4048         continue;
4049
4050       if (CAN_CHANGE(element))
4051       {
4052         for (i = 0; i < element_info[element].num_change_pages; i++)
4053         {
4054           /* check for player created from custom element as single target */
4055           content = element_info[element].change_page[i].target_element;
4056           is_player = ELEM_IS_PLAYER(content);
4057
4058           if (is_player && (found_rating < 3 ||
4059                             (found_rating == 3 && element < found_element)))
4060           {
4061             start_x = x;
4062             start_y = y;
4063
4064             found_rating = 3;
4065             found_element = element;
4066           }
4067         }
4068       }
4069
4070       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4071       {
4072         /* check for player created from custom element as explosion content */
4073         content = element_info[element].content.e[xx][yy];
4074         is_player = ELEM_IS_PLAYER(content);
4075
4076         if (is_player && (found_rating < 2 ||
4077                           (found_rating == 2 && element < found_element)))
4078         {
4079           start_x = x + xx - 1;
4080           start_y = y + yy - 1;
4081
4082           found_rating = 2;
4083           found_element = element;
4084         }
4085
4086         if (!CAN_CHANGE(element))
4087           continue;
4088
4089         for (i = 0; i < element_info[element].num_change_pages; i++)
4090         {
4091           /* check for player created from custom element as extended target */
4092           content =
4093             element_info[element].change_page[i].target_content.e[xx][yy];
4094
4095           is_player = ELEM_IS_PLAYER(content);
4096
4097           if (is_player && (found_rating < 1 ||
4098                             (found_rating == 1 && element < found_element)))
4099           {
4100             start_x = x + xx - 1;
4101             start_y = y + yy - 1;
4102
4103             found_rating = 1;
4104             found_element = element;
4105           }
4106         }
4107       }
4108     }
4109
4110     scroll_x = SCROLL_POSITION_X(start_x);
4111     scroll_y = SCROLL_POSITION_Y(start_y);
4112   }
4113   else
4114   {
4115     scroll_x = SCROLL_POSITION_X(local_player->jx);
4116     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4117   }
4118
4119   /* !!! FIX THIS (START) !!! */
4120   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4121   {
4122     InitGameEngine_EM();
4123   }
4124   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4125   {
4126     InitGameEngine_SP();
4127   }
4128   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4129   {
4130     InitGameEngine_MM();
4131   }
4132   else
4133   {
4134     DrawLevel(REDRAW_FIELD);
4135     DrawAllPlayers();
4136
4137     /* after drawing the level, correct some elements */
4138     if (game.timegate_time_left == 0)
4139       CloseAllOpenTimegates();
4140   }
4141
4142   /* blit playfield from scroll buffer to normal back buffer for fading in */
4143   BlitScreenToBitmap(backbuffer);
4144   /* !!! FIX THIS (END) !!! */
4145
4146   DrawMaskedBorder(fade_mask);
4147
4148   FadeIn(fade_mask);
4149
4150 #if 1
4151   // full screen redraw is required at this point in the following cases:
4152   // - special editor door undrawn when game was started from level editor
4153   // - drawing area (playfield) was changed and has to be removed completely
4154   redraw_mask = REDRAW_ALL;
4155   BackToFront();
4156 #endif
4157
4158   if (!game.restart_level)
4159   {
4160     /* copy default game door content to main double buffer */
4161
4162     /* !!! CHECK AGAIN !!! */
4163     SetPanelBackground();
4164     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4165     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4166   }
4167
4168   SetPanelBackground();
4169   SetDrawBackgroundMask(REDRAW_DOOR_1);
4170
4171   UpdateAndDisplayGameControlValues();
4172
4173   if (!game.restart_level)
4174   {
4175     UnmapGameButtons();
4176     UnmapTapeButtons();
4177
4178     FreeGameButtons();
4179     CreateGameButtons();
4180
4181     MapGameButtons();
4182     MapTapeButtons();
4183
4184     /* copy actual game door content to door double buffer for OpenDoor() */
4185     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4186
4187     OpenDoor(DOOR_OPEN_ALL);
4188
4189     KeyboardAutoRepeatOffUnlessAutoplay();
4190
4191 #if DEBUG_INIT_PLAYER
4192     if (options.debug)
4193     {
4194       printf("Player status (final):\n");
4195
4196       for (i = 0; i < MAX_PLAYERS; i++)
4197       {
4198         struct PlayerInfo *player = &stored_player[i];
4199
4200         printf("- player %d: present == %d, connected == %d, active == %d",
4201                i + 1,
4202                player->present,
4203                player->connected,
4204                player->active);
4205
4206         if (local_player == player)
4207           printf(" (local player)");
4208
4209         printf("\n");
4210       }
4211     }
4212 #endif
4213   }
4214
4215   UnmapAllGadgets();
4216
4217   MapGameButtons();
4218   MapTapeButtons();
4219
4220   if (!game.restart_level && !tape.playing)
4221   {
4222     LevelStats_incPlayed(level_nr);
4223
4224     SaveLevelSetup_SeriesInfo();
4225   }
4226
4227   game.restart_level = FALSE;
4228   game.restart_game_message = NULL;
4229
4230   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4231     InitGameActions_MM();
4232
4233   SaveEngineSnapshotToListInitial();
4234
4235   if (!game.restart_level)
4236   {
4237     PlaySound(SND_GAME_STARTING);
4238
4239     if (setup.sound_music)
4240       PlayLevelMusic();
4241   }
4242 }
4243
4244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4245                         int actual_player_x, int actual_player_y)
4246 {
4247   /* this is used for non-R'n'D game engines to update certain engine values */
4248
4249   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4250   {
4251     actual_player_x = correctLevelPosX_EM(actual_player_x);
4252     actual_player_y = correctLevelPosY_EM(actual_player_y);
4253   }
4254
4255   /* needed to determine if sounds are played within the visible screen area */
4256   scroll_x = actual_scroll_x;
4257   scroll_y = actual_scroll_y;
4258
4259   /* needed to get player position for "follow finger" playing input method */
4260   local_player->jx = actual_player_x;
4261   local_player->jy = actual_player_y;
4262 }
4263
4264 void InitMovDir(int x, int y)
4265 {
4266   int i, element = Feld[x][y];
4267   static int xy[4][2] =
4268   {
4269     {  0, +1 },
4270     { +1,  0 },
4271     {  0, -1 },
4272     { -1,  0 }
4273   };
4274   static int direction[3][4] =
4275   {
4276     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4277     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4278     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4279   };
4280
4281   switch (element)
4282   {
4283     case EL_BUG_RIGHT:
4284     case EL_BUG_UP:
4285     case EL_BUG_LEFT:
4286     case EL_BUG_DOWN:
4287       Feld[x][y] = EL_BUG;
4288       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4289       break;
4290
4291     case EL_SPACESHIP_RIGHT:
4292     case EL_SPACESHIP_UP:
4293     case EL_SPACESHIP_LEFT:
4294     case EL_SPACESHIP_DOWN:
4295       Feld[x][y] = EL_SPACESHIP;
4296       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4297       break;
4298
4299     case EL_BD_BUTTERFLY_RIGHT:
4300     case EL_BD_BUTTERFLY_UP:
4301     case EL_BD_BUTTERFLY_LEFT:
4302     case EL_BD_BUTTERFLY_DOWN:
4303       Feld[x][y] = EL_BD_BUTTERFLY;
4304       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4305       break;
4306
4307     case EL_BD_FIREFLY_RIGHT:
4308     case EL_BD_FIREFLY_UP:
4309     case EL_BD_FIREFLY_LEFT:
4310     case EL_BD_FIREFLY_DOWN:
4311       Feld[x][y] = EL_BD_FIREFLY;
4312       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4313       break;
4314
4315     case EL_PACMAN_RIGHT:
4316     case EL_PACMAN_UP:
4317     case EL_PACMAN_LEFT:
4318     case EL_PACMAN_DOWN:
4319       Feld[x][y] = EL_PACMAN;
4320       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4321       break;
4322
4323     case EL_YAMYAM_LEFT:
4324     case EL_YAMYAM_RIGHT:
4325     case EL_YAMYAM_UP:
4326     case EL_YAMYAM_DOWN:
4327       Feld[x][y] = EL_YAMYAM;
4328       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4329       break;
4330
4331     case EL_SP_SNIKSNAK:
4332       MovDir[x][y] = MV_UP;
4333       break;
4334
4335     case EL_SP_ELECTRON:
4336       MovDir[x][y] = MV_LEFT;
4337       break;
4338
4339     case EL_MOLE_LEFT:
4340     case EL_MOLE_RIGHT:
4341     case EL_MOLE_UP:
4342     case EL_MOLE_DOWN:
4343       Feld[x][y] = EL_MOLE;
4344       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4345       break;
4346
4347     default:
4348       if (IS_CUSTOM_ELEMENT(element))
4349       {
4350         struct ElementInfo *ei = &element_info[element];
4351         int move_direction_initial = ei->move_direction_initial;
4352         int move_pattern = ei->move_pattern;
4353
4354         if (move_direction_initial == MV_START_PREVIOUS)
4355         {
4356           if (MovDir[x][y] != MV_NONE)
4357             return;
4358
4359           move_direction_initial = MV_START_AUTOMATIC;
4360         }
4361
4362         if (move_direction_initial == MV_START_RANDOM)
4363           MovDir[x][y] = 1 << RND(4);
4364         else if (move_direction_initial & MV_ANY_DIRECTION)
4365           MovDir[x][y] = move_direction_initial;
4366         else if (move_pattern == MV_ALL_DIRECTIONS ||
4367                  move_pattern == MV_TURNING_LEFT ||
4368                  move_pattern == MV_TURNING_RIGHT ||
4369                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4370                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4371                  move_pattern == MV_TURNING_RANDOM)
4372           MovDir[x][y] = 1 << RND(4);
4373         else if (move_pattern == MV_HORIZONTAL)
4374           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4375         else if (move_pattern == MV_VERTICAL)
4376           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4377         else if (move_pattern & MV_ANY_DIRECTION)
4378           MovDir[x][y] = element_info[element].move_pattern;
4379         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4380                  move_pattern == MV_ALONG_RIGHT_SIDE)
4381         {
4382           /* use random direction as default start direction */
4383           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4384             MovDir[x][y] = 1 << RND(4);
4385
4386           for (i = 0; i < NUM_DIRECTIONS; i++)
4387           {
4388             int x1 = x + xy[i][0];
4389             int y1 = y + xy[i][1];
4390
4391             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4392             {
4393               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4394                 MovDir[x][y] = direction[0][i];
4395               else
4396                 MovDir[x][y] = direction[1][i];
4397
4398               break;
4399             }
4400           }
4401         }                
4402       }
4403       else
4404       {
4405         MovDir[x][y] = 1 << RND(4);
4406
4407         if (element != EL_BUG &&
4408             element != EL_SPACESHIP &&
4409             element != EL_BD_BUTTERFLY &&
4410             element != EL_BD_FIREFLY)
4411           break;
4412
4413         for (i = 0; i < NUM_DIRECTIONS; i++)
4414         {
4415           int x1 = x + xy[i][0];
4416           int y1 = y + xy[i][1];
4417
4418           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4419           {
4420             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4421             {
4422               MovDir[x][y] = direction[0][i];
4423               break;
4424             }
4425             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4426                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4427             {
4428               MovDir[x][y] = direction[1][i];
4429               break;
4430             }
4431           }
4432         }
4433       }
4434       break;
4435   }
4436
4437   GfxDir[x][y] = MovDir[x][y];
4438 }
4439
4440 void InitAmoebaNr(int x, int y)
4441 {
4442   int i;
4443   int group_nr = AmoebeNachbarNr(x, y);
4444
4445   if (group_nr == 0)
4446   {
4447     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4448     {
4449       if (AmoebaCnt[i] == 0)
4450       {
4451         group_nr = i;
4452         break;
4453       }
4454     }
4455   }
4456
4457   AmoebaNr[x][y] = group_nr;
4458   AmoebaCnt[group_nr]++;
4459   AmoebaCnt2[group_nr]++;
4460 }
4461
4462 static void PlayerWins(struct PlayerInfo *player)
4463 {
4464   player->LevelSolved = TRUE;
4465   player->GameOver = TRUE;
4466
4467   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4468                          level.native_em_level->lev->score :
4469                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4470                          game_mm.score :
4471                          player->score);
4472   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4473                           MM_HEALTH(game_mm.laser_overload_value) :
4474                           player->health);
4475
4476   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4477                                       TimeLeft);
4478   player->LevelSolved_CountingScore = player->score_final;
4479   player->LevelSolved_CountingHealth = player->health_final;
4480 }
4481
4482 void GameWon()
4483 {
4484   static int time_count_steps;
4485   static int time, time_final;
4486   static int score, score_final;
4487   static int health, health_final;
4488   static int game_over_delay_1 = 0;
4489   static int game_over_delay_2 = 0;
4490   static int game_over_delay_3 = 0;
4491   int game_over_delay_value_1 = 50;
4492   int game_over_delay_value_2 = 25;
4493   int game_over_delay_value_3 = 50;
4494
4495   if (!local_player->LevelSolved_GameWon)
4496   {
4497     int i;
4498
4499     /* do not start end game actions before the player stops moving (to exit) */
4500     if (local_player->MovPos)
4501       return;
4502
4503     local_player->LevelSolved_GameWon = TRUE;
4504     local_player->LevelSolved_SaveTape = tape.recording;
4505     local_player->LevelSolved_SaveScore = !tape.playing;
4506
4507     if (!tape.playing)
4508     {
4509       LevelStats_incSolved(level_nr);
4510
4511       SaveLevelSetup_SeriesInfo();
4512     }
4513
4514     if (tape.auto_play)         /* tape might already be stopped here */
4515       tape.auto_play_level_solved = TRUE;
4516
4517     TapeStop();
4518
4519     game_over_delay_1 = 0;
4520     game_over_delay_2 = 0;
4521     game_over_delay_3 = game_over_delay_value_3;
4522
4523     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4524     score = score_final = local_player->score_final;
4525     health = health_final = local_player->health_final;
4526
4527     if (level.score[SC_TIME_BONUS] > 0)
4528     {
4529       if (TimeLeft > 0)
4530       {
4531         time_final = 0;
4532         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4533       }
4534       else if (game.no_time_limit && TimePlayed < 999)
4535       {
4536         time_final = 999;
4537         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4538       }
4539
4540       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4541
4542       game_over_delay_1 = game_over_delay_value_1;
4543
4544       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4545       {
4546         health_final = 0;
4547         score_final += health * level.score[SC_TIME_BONUS];
4548
4549         game_over_delay_2 = game_over_delay_value_2;
4550       }
4551
4552       local_player->score_final = score_final;
4553       local_player->health_final = health_final;
4554     }
4555
4556     if (level_editor_test_game)
4557     {
4558       time = time_final;
4559       score = score_final;
4560
4561       local_player->LevelSolved_CountingTime = time;
4562       local_player->LevelSolved_CountingScore = score;
4563
4564       game_panel_controls[GAME_PANEL_TIME].value = time;
4565       game_panel_controls[GAME_PANEL_SCORE].value = score;
4566
4567       DisplayGameControlValues();
4568     }
4569
4570     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4571     {
4572       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4573       {
4574         /* close exit door after last player */
4575         if ((AllPlayersGone &&
4576              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4577               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4578               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4579             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4580             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4581         {
4582           int element = Feld[ExitX][ExitY];
4583
4584           Feld[ExitX][ExitY] =
4585             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4586              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4587              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4588              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4589              EL_EM_STEEL_EXIT_CLOSING);
4590
4591           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4592         }
4593
4594         /* player disappears */
4595         DrawLevelField(ExitX, ExitY);
4596       }
4597
4598       for (i = 0; i < MAX_PLAYERS; i++)
4599       {
4600         struct PlayerInfo *player = &stored_player[i];
4601
4602         if (player->present)
4603         {
4604           RemovePlayer(player);
4605
4606           /* player disappears */
4607           DrawLevelField(player->jx, player->jy);
4608         }
4609       }
4610     }
4611
4612     PlaySound(SND_GAME_WINNING);
4613   }
4614
4615   if (game_over_delay_1 > 0)
4616   {
4617     game_over_delay_1--;
4618
4619     return;
4620   }
4621
4622   if (time != time_final)
4623   {
4624     int time_to_go = ABS(time_final - time);
4625     int time_count_dir = (time < time_final ? +1 : -1);
4626
4627     if (time_to_go < time_count_steps)
4628       time_count_steps = 1;
4629
4630     time  += time_count_steps * time_count_dir;
4631     score += time_count_steps * level.score[SC_TIME_BONUS];
4632
4633     local_player->LevelSolved_CountingTime = time;
4634     local_player->LevelSolved_CountingScore = score;
4635
4636     game_panel_controls[GAME_PANEL_TIME].value = time;
4637     game_panel_controls[GAME_PANEL_SCORE].value = score;
4638
4639     DisplayGameControlValues();
4640
4641     if (time == time_final)
4642       StopSound(SND_GAME_LEVELTIME_BONUS);
4643     else if (setup.sound_loops)
4644       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4645     else
4646       PlaySound(SND_GAME_LEVELTIME_BONUS);
4647
4648     return;
4649   }
4650
4651   if (game_over_delay_2 > 0)
4652   {
4653     game_over_delay_2--;
4654
4655     return;
4656   }
4657
4658   if (health != health_final)
4659   {
4660     int health_count_dir = (health < health_final ? +1 : -1);
4661
4662     health += health_count_dir;
4663     score  += level.score[SC_TIME_BONUS];
4664
4665     local_player->LevelSolved_CountingHealth = health;
4666     local_player->LevelSolved_CountingScore = score;
4667
4668     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4669     game_panel_controls[GAME_PANEL_SCORE].value = score;
4670
4671     DisplayGameControlValues();
4672
4673     if (health == health_final)
4674       StopSound(SND_GAME_LEVELTIME_BONUS);
4675     else if (setup.sound_loops)
4676       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4677     else
4678       PlaySound(SND_GAME_LEVELTIME_BONUS);
4679
4680     return;
4681   }
4682
4683   local_player->LevelSolved_PanelOff = TRUE;
4684
4685   if (game_over_delay_3 > 0)
4686   {
4687     game_over_delay_3--;
4688
4689     return;
4690   }
4691
4692   GameEnd();
4693 }
4694
4695 void GameEnd()
4696 {
4697   int hi_pos;
4698   boolean raise_level = FALSE;
4699
4700   local_player->LevelSolved_GameEnd = TRUE;
4701
4702   if (local_player->LevelSolved_SaveTape)
4703   {
4704     /* make sure that request dialog to save tape does not open door again */
4705     if (!global.use_envelope_request)
4706       CloseDoor(DOOR_CLOSE_1);
4707
4708     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4709   }
4710
4711   /* if no tape is to be saved, close both doors simultaneously */
4712   CloseDoor(DOOR_CLOSE_ALL);
4713
4714   if (level_editor_test_game)
4715   {
4716     SetGameStatus(GAME_MODE_MAIN);
4717
4718     DrawMainMenu();
4719
4720     return;
4721   }
4722
4723   if (!local_player->LevelSolved_SaveScore)
4724   {
4725     SetGameStatus(GAME_MODE_MAIN);
4726
4727     DrawMainMenu();
4728
4729     return;
4730   }
4731
4732   if (level_nr == leveldir_current->handicap_level)
4733   {
4734     leveldir_current->handicap_level++;
4735
4736     SaveLevelSetup_SeriesInfo();
4737   }
4738
4739   if (setup.increment_levels &&
4740       level_nr < leveldir_current->last_level)
4741     raise_level = TRUE;                 /* advance to next level */
4742
4743   if ((hi_pos = NewHiScore()) >= 0) 
4744   {
4745     SetGameStatus(GAME_MODE_SCORES);
4746
4747     DrawHallOfFame(hi_pos);
4748
4749     if (raise_level)
4750     {
4751       level_nr++;
4752       TapeErase();
4753     }
4754   }
4755   else
4756   {
4757     SetGameStatus(GAME_MODE_MAIN);
4758
4759     if (raise_level)
4760     {
4761       level_nr++;
4762       TapeErase();
4763     }
4764
4765     DrawMainMenu();
4766   }
4767 }
4768
4769 int NewHiScore()
4770 {
4771   int k, l;
4772   int position = -1;
4773   boolean one_score_entry_per_name = !program.many_scores_per_name;
4774
4775   LoadScore(level_nr);
4776
4777   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4778       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4779     return -1;
4780
4781   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4782   {
4783     if (local_player->score_final > highscore[k].Score)
4784     {
4785       /* player has made it to the hall of fame */
4786
4787       if (k < MAX_SCORE_ENTRIES - 1)
4788       {
4789         int m = MAX_SCORE_ENTRIES - 1;
4790
4791         if (one_score_entry_per_name)
4792         {
4793           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4794             if (strEqual(setup.player_name, highscore[l].Name))
4795               m = l;
4796
4797           if (m == k)   /* player's new highscore overwrites his old one */
4798             goto put_into_list;
4799         }
4800
4801         for (l = m; l > k; l--)
4802         {
4803           strcpy(highscore[l].Name, highscore[l - 1].Name);
4804           highscore[l].Score = highscore[l - 1].Score;
4805         }
4806       }
4807
4808       put_into_list:
4809
4810       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4811       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4812       highscore[k].Score = local_player->score_final; 
4813       position = k;
4814
4815       break;
4816     }
4817     else if (one_score_entry_per_name &&
4818              !strncmp(setup.player_name, highscore[k].Name,
4819                       MAX_PLAYER_NAME_LEN))
4820       break;    /* player already there with a higher score */
4821   }
4822
4823   if (position >= 0) 
4824     SaveScore(level_nr);
4825
4826   return position;
4827 }
4828
4829 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4830 {
4831   int element = Feld[x][y];
4832   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4833   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4834   int horiz_move = (dx != 0);
4835   int sign = (horiz_move ? dx : dy);
4836   int step = sign * element_info[element].move_stepsize;
4837
4838   /* special values for move stepsize for spring and things on conveyor belt */
4839   if (horiz_move)
4840   {
4841     if (CAN_FALL(element) &&
4842         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4843       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4844     else if (element == EL_SPRING)
4845       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4846   }
4847
4848   return step;
4849 }
4850
4851 inline static int getElementMoveStepsize(int x, int y)
4852 {
4853   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4854 }
4855
4856 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4857 {
4858   if (player->GfxAction != action || player->GfxDir != dir)
4859   {
4860     player->GfxAction = action;
4861     player->GfxDir = dir;
4862     player->Frame = 0;
4863     player->StepFrame = 0;
4864   }
4865 }
4866
4867 static void ResetGfxFrame(int x, int y)
4868 {
4869   // profiling showed that "autotest" spends 10~20% of its time in this function
4870   if (DrawingDeactivatedField())
4871     return;
4872
4873   int element = Feld[x][y];
4874   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4875
4876   if (graphic_info[graphic].anim_global_sync)
4877     GfxFrame[x][y] = FrameCounter;
4878   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4879     GfxFrame[x][y] = CustomValue[x][y];
4880   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4881     GfxFrame[x][y] = element_info[element].collect_score;
4882   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4883     GfxFrame[x][y] = ChangeDelay[x][y];
4884 }
4885
4886 static void ResetGfxAnimation(int x, int y)
4887 {
4888   GfxAction[x][y] = ACTION_DEFAULT;
4889   GfxDir[x][y] = MovDir[x][y];
4890   GfxFrame[x][y] = 0;
4891
4892   ResetGfxFrame(x, y);
4893 }
4894
4895 static void ResetRandomAnimationValue(int x, int y)
4896 {
4897   GfxRandom[x][y] = INIT_GFX_RANDOM();
4898 }
4899
4900 void InitMovingField(int x, int y, int direction)
4901 {
4902   int element = Feld[x][y];
4903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4905   int newx = x + dx;
4906   int newy = y + dy;
4907   boolean is_moving_before, is_moving_after;
4908
4909   /* check if element was/is moving or being moved before/after mode change */
4910   is_moving_before = (WasJustMoving[x][y] != 0);
4911   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4912
4913   /* reset animation only for moving elements which change direction of moving
4914      or which just started or stopped moving
4915      (else CEs with property "can move" / "not moving" are reset each frame) */
4916   if (is_moving_before != is_moving_after ||
4917       direction != MovDir[x][y])
4918     ResetGfxAnimation(x, y);
4919
4920   MovDir[x][y] = direction;
4921   GfxDir[x][y] = direction;
4922
4923   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4924                      direction == MV_DOWN && CAN_FALL(element) ?
4925                      ACTION_FALLING : ACTION_MOVING);
4926
4927   /* this is needed for CEs with property "can move" / "not moving" */
4928
4929   if (is_moving_after)
4930   {
4931     if (Feld[newx][newy] == EL_EMPTY)
4932       Feld[newx][newy] = EL_BLOCKED;
4933
4934     MovDir[newx][newy] = MovDir[x][y];
4935
4936     CustomValue[newx][newy] = CustomValue[x][y];
4937
4938     GfxFrame[newx][newy] = GfxFrame[x][y];
4939     GfxRandom[newx][newy] = GfxRandom[x][y];
4940     GfxAction[newx][newy] = GfxAction[x][y];
4941     GfxDir[newx][newy] = GfxDir[x][y];
4942   }
4943 }
4944
4945 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4946 {
4947   int direction = MovDir[x][y];
4948   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4949   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4950
4951   *goes_to_x = newx;
4952   *goes_to_y = newy;
4953 }
4954
4955 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4956 {
4957   int oldx = x, oldy = y;
4958   int direction = MovDir[x][y];
4959
4960   if (direction == MV_LEFT)
4961     oldx++;
4962   else if (direction == MV_RIGHT)
4963     oldx--;
4964   else if (direction == MV_UP)
4965     oldy++;
4966   else if (direction == MV_DOWN)
4967     oldy--;
4968
4969   *comes_from_x = oldx;
4970   *comes_from_y = oldy;
4971 }
4972
4973 int MovingOrBlocked2Element(int x, int y)
4974 {
4975   int element = Feld[x][y];
4976
4977   if (element == EL_BLOCKED)
4978   {
4979     int oldx, oldy;
4980
4981     Blocked2Moving(x, y, &oldx, &oldy);
4982     return Feld[oldx][oldy];
4983   }
4984   else
4985     return element;
4986 }
4987
4988 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4989 {
4990   /* like MovingOrBlocked2Element(), but if element is moving
4991      and (x,y) is the field the moving element is just leaving,
4992      return EL_BLOCKED instead of the element value */
4993   int element = Feld[x][y];
4994
4995   if (IS_MOVING(x, y))
4996   {
4997     if (element == EL_BLOCKED)
4998     {
4999       int oldx, oldy;
5000
5001       Blocked2Moving(x, y, &oldx, &oldy);
5002       return Feld[oldx][oldy];
5003     }
5004     else
5005       return EL_BLOCKED;
5006   }
5007   else
5008     return element;
5009 }
5010
5011 static void RemoveField(int x, int y)
5012 {
5013   Feld[x][y] = EL_EMPTY;
5014
5015   MovPos[x][y] = 0;
5016   MovDir[x][y] = 0;
5017   MovDelay[x][y] = 0;
5018
5019   CustomValue[x][y] = 0;
5020
5021   AmoebaNr[x][y] = 0;
5022   ChangeDelay[x][y] = 0;
5023   ChangePage[x][y] = -1;
5024   Pushed[x][y] = FALSE;
5025
5026   GfxElement[x][y] = EL_UNDEFINED;
5027   GfxAction[x][y] = ACTION_DEFAULT;
5028   GfxDir[x][y] = MV_NONE;
5029 }
5030
5031 void RemoveMovingField(int x, int y)
5032 {
5033   int oldx = x, oldy = y, newx = x, newy = y;
5034   int element = Feld[x][y];
5035   int next_element = EL_UNDEFINED;
5036
5037   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5038     return;
5039
5040   if (IS_MOVING(x, y))
5041   {
5042     Moving2Blocked(x, y, &newx, &newy);
5043
5044     if (Feld[newx][newy] != EL_BLOCKED)
5045     {
5046       /* element is moving, but target field is not free (blocked), but
5047          already occupied by something different (example: acid pool);
5048          in this case, only remove the moving field, but not the target */
5049
5050       RemoveField(oldx, oldy);
5051
5052       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5053
5054       TEST_DrawLevelField(oldx, oldy);
5055
5056       return;
5057     }
5058   }
5059   else if (element == EL_BLOCKED)
5060   {
5061     Blocked2Moving(x, y, &oldx, &oldy);
5062     if (!IS_MOVING(oldx, oldy))
5063       return;
5064   }
5065
5066   if (element == EL_BLOCKED &&
5067       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5068        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5069        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5070        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5071        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5072        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5073     next_element = get_next_element(Feld[oldx][oldy]);
5074
5075   RemoveField(oldx, oldy);
5076   RemoveField(newx, newy);
5077
5078   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5079
5080   if (next_element != EL_UNDEFINED)
5081     Feld[oldx][oldy] = next_element;
5082
5083   TEST_DrawLevelField(oldx, oldy);
5084   TEST_DrawLevelField(newx, newy);
5085 }
5086
5087 void DrawDynamite(int x, int y)
5088 {
5089   int sx = SCREENX(x), sy = SCREENY(y);
5090   int graphic = el2img(Feld[x][y]);
5091   int frame;
5092
5093   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5094     return;
5095
5096   if (IS_WALKABLE_INSIDE(Back[x][y]))
5097     return;
5098
5099   if (Back[x][y])
5100     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5101   else if (Store[x][y])
5102     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5103
5104   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5105
5106   if (Back[x][y] || Store[x][y])
5107     DrawGraphicThruMask(sx, sy, graphic, frame);
5108   else
5109     DrawGraphic(sx, sy, graphic, frame);
5110 }
5111
5112 void CheckDynamite(int x, int y)
5113 {
5114   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5115   {
5116     MovDelay[x][y]--;
5117
5118     if (MovDelay[x][y] != 0)
5119     {
5120       DrawDynamite(x, y);
5121       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5122
5123       return;
5124     }
5125   }
5126
5127   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5128
5129   Bang(x, y);
5130 }
5131
5132 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5133 {
5134   boolean num_checked_players = 0;
5135   int i;
5136
5137   for (i = 0; i < MAX_PLAYERS; i++)
5138   {
5139     if (stored_player[i].active)
5140     {
5141       int sx = stored_player[i].jx;
5142       int sy = stored_player[i].jy;
5143
5144       if (num_checked_players == 0)
5145       {
5146         *sx1 = *sx2 = sx;
5147         *sy1 = *sy2 = sy;
5148       }
5149       else
5150       {
5151         *sx1 = MIN(*sx1, sx);
5152         *sy1 = MIN(*sy1, sy);
5153         *sx2 = MAX(*sx2, sx);
5154         *sy2 = MAX(*sy2, sy);
5155       }
5156
5157       num_checked_players++;
5158     }
5159   }
5160 }
5161
5162 static boolean checkIfAllPlayersFitToScreen_RND()
5163 {
5164   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5165
5166   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5167
5168   return (sx2 - sx1 < SCR_FIELDX &&
5169           sy2 - sy1 < SCR_FIELDY);
5170 }
5171
5172 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5173 {
5174   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5175
5176   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5177
5178   *sx = (sx1 + sx2) / 2;
5179   *sy = (sy1 + sy2) / 2;
5180 }
5181
5182 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5183                         boolean center_screen, boolean quick_relocation)
5184 {
5185   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5186   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5187   boolean no_delay = (tape.warp_forward);
5188   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5189   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5190   int new_scroll_x, new_scroll_y;
5191
5192   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5193   {
5194     /* case 1: quick relocation inside visible screen (without scrolling) */
5195
5196     RedrawPlayfield();
5197
5198     return;
5199   }
5200
5201   if (!level.shifted_relocation || center_screen)
5202   {
5203     /* relocation _with_ centering of screen */
5204
5205     new_scroll_x = SCROLL_POSITION_X(x);
5206     new_scroll_y = SCROLL_POSITION_Y(y);
5207   }
5208   else
5209   {
5210     /* relocation _without_ centering of screen */
5211
5212     int center_scroll_x = SCROLL_POSITION_X(old_x);
5213     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5214     int offset_x = x + (scroll_x - center_scroll_x);
5215     int offset_y = y + (scroll_y - center_scroll_y);
5216
5217     /* for new screen position, apply previous offset to center position */
5218     new_scroll_x = SCROLL_POSITION_X(offset_x);
5219     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5220   }
5221
5222   if (quick_relocation)
5223   {
5224     /* case 2: quick relocation (redraw without visible scrolling) */
5225
5226     scroll_x = new_scroll_x;
5227     scroll_y = new_scroll_y;
5228
5229     RedrawPlayfield();
5230
5231     return;
5232   }
5233
5234   /* case 3: visible relocation (with scrolling to new position) */
5235
5236   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5237
5238   SetVideoFrameDelay(wait_delay_value);
5239
5240   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5241   {
5242     int dx = 0, dy = 0;
5243     int fx = FX, fy = FY;
5244
5245     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5246     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5247
5248     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5249       break;
5250
5251     scroll_x -= dx;
5252     scroll_y -= dy;
5253
5254     fx += dx * TILEX / 2;
5255     fy += dy * TILEY / 2;
5256
5257     ScrollLevel(dx, dy);
5258     DrawAllPlayers();
5259
5260     /* scroll in two steps of half tile size to make things smoother */
5261     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5262
5263     /* scroll second step to align at full tile size */
5264     BlitScreenToBitmap(window);
5265   }
5266
5267   DrawAllPlayers();
5268   BackToFront();
5269
5270   SetVideoFrameDelay(frame_delay_value_old);
5271 }
5272
5273 void RelocatePlayer(int jx, int jy, int el_player_raw)
5274 {
5275   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5276   int player_nr = GET_PLAYER_NR(el_player);
5277   struct PlayerInfo *player = &stored_player[player_nr];
5278   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5279   boolean no_delay = (tape.warp_forward);
5280   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5281   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5282   int old_jx = player->jx;
5283   int old_jy = player->jy;
5284   int old_element = Feld[old_jx][old_jy];
5285   int element = Feld[jx][jy];
5286   boolean player_relocated = (old_jx != jx || old_jy != jy);
5287
5288   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5289   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5290   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5291   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5292   int leave_side_horiz = move_dir_horiz;
5293   int leave_side_vert  = move_dir_vert;
5294   int enter_side = enter_side_horiz | enter_side_vert;
5295   int leave_side = leave_side_horiz | leave_side_vert;
5296
5297   if (player->GameOver)         /* do not reanimate dead player */
5298     return;
5299
5300   if (!player_relocated)        /* no need to relocate the player */
5301     return;
5302
5303   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5304   {
5305     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5306     DrawLevelField(jx, jy);
5307   }
5308
5309   if (player->present)
5310   {
5311     while (player->MovPos)
5312     {
5313       ScrollPlayer(player, SCROLL_GO_ON);
5314       ScrollScreen(NULL, SCROLL_GO_ON);
5315
5316       AdvanceFrameAndPlayerCounters(player->index_nr);
5317
5318       DrawPlayer(player);
5319
5320       BackToFront_WithFrameDelay(wait_delay_value);
5321     }
5322
5323     DrawPlayer(player);         /* needed here only to cleanup last field */
5324     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5325
5326     player->is_moving = FALSE;
5327   }
5328
5329   if (IS_CUSTOM_ELEMENT(old_element))
5330     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5331                                CE_LEFT_BY_PLAYER,
5332                                player->index_bit, leave_side);
5333
5334   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5335                                       CE_PLAYER_LEAVES_X,
5336                                       player->index_bit, leave_side);
5337
5338   Feld[jx][jy] = el_player;
5339   InitPlayerField(jx, jy, el_player, TRUE);
5340
5341   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5342      possible that the relocation target field did not contain a player element,
5343      but a walkable element, to which the new player was relocated -- in this
5344      case, restore that (already initialized!) element on the player field */
5345   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5346   {
5347     Feld[jx][jy] = element;     /* restore previously existing element */
5348   }
5349
5350   /* only visually relocate centered player */
5351   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5352                      FALSE, level.instant_relocation);
5353
5354   TestIfPlayerTouchesBadThing(jx, jy);
5355   TestIfPlayerTouchesCustomElement(jx, jy);
5356
5357   if (IS_CUSTOM_ELEMENT(element))
5358     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5359                                player->index_bit, enter_side);
5360
5361   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5362                                       player->index_bit, enter_side);
5363
5364   if (player->is_switching)
5365   {
5366     /* ensure that relocation while still switching an element does not cause
5367        a new element to be treated as also switched directly after relocation
5368        (this is important for teleporter switches that teleport the player to
5369        a place where another teleporter switch is in the same direction, which
5370        would then incorrectly be treated as immediately switched before the
5371        direction key that caused the switch was released) */
5372
5373     player->switch_x += jx - old_jx;
5374     player->switch_y += jy - old_jy;
5375   }
5376 }
5377
5378 void Explode(int ex, int ey, int phase, int mode)
5379 {
5380   int x, y;
5381   int last_phase;
5382   int border_element;
5383
5384   /* !!! eliminate this variable !!! */
5385   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5386
5387   if (game.explosions_delayed)
5388   {
5389     ExplodeField[ex][ey] = mode;
5390     return;
5391   }
5392
5393   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5394   {
5395     int center_element = Feld[ex][ey];
5396     int artwork_element, explosion_element;     /* set these values later */
5397
5398     /* remove things displayed in background while burning dynamite */
5399     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5400       Back[ex][ey] = 0;
5401
5402     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5403     {
5404       /* put moving element to center field (and let it explode there) */
5405       center_element = MovingOrBlocked2Element(ex, ey);
5406       RemoveMovingField(ex, ey);
5407       Feld[ex][ey] = center_element;
5408     }
5409
5410     /* now "center_element" is finally determined -- set related values now */
5411     artwork_element = center_element;           /* for custom player artwork */
5412     explosion_element = center_element;         /* for custom player artwork */
5413
5414     if (IS_PLAYER(ex, ey))
5415     {
5416       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5417
5418       artwork_element = stored_player[player_nr].artwork_element;
5419
5420       if (level.use_explosion_element[player_nr])
5421       {
5422         explosion_element = level.explosion_element[player_nr];
5423         artwork_element = explosion_element;
5424       }
5425     }
5426
5427     if (mode == EX_TYPE_NORMAL ||
5428         mode == EX_TYPE_CENTER ||
5429         mode == EX_TYPE_CROSS)
5430       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5431
5432     last_phase = element_info[explosion_element].explosion_delay + 1;
5433
5434     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5435     {
5436       int xx = x - ex + 1;
5437       int yy = y - ey + 1;
5438       int element;
5439
5440       if (!IN_LEV_FIELD(x, y) ||
5441           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5442           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5443         continue;
5444
5445       element = Feld[x][y];
5446
5447       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5448       {
5449         element = MovingOrBlocked2Element(x, y);
5450
5451         if (!IS_EXPLOSION_PROOF(element))
5452           RemoveMovingField(x, y);
5453       }
5454
5455       /* indestructible elements can only explode in center (but not flames) */
5456       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5457                                            mode == EX_TYPE_BORDER)) ||
5458           element == EL_FLAMES)
5459         continue;
5460
5461       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5462          behaviour, for example when touching a yamyam that explodes to rocks
5463          with active deadly shield, a rock is created under the player !!! */
5464       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5465 #if 0
5466       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5467           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5468            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5469 #else
5470       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5471 #endif
5472       {
5473         if (IS_ACTIVE_BOMB(element))
5474         {
5475           /* re-activate things under the bomb like gate or penguin */
5476           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5477           Back[x][y] = 0;
5478         }
5479
5480         continue;
5481       }
5482
5483       /* save walkable background elements while explosion on same tile */
5484       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5485           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5486         Back[x][y] = element;
5487
5488       /* ignite explodable elements reached by other explosion */
5489       if (element == EL_EXPLOSION)
5490         element = Store2[x][y];
5491
5492       if (AmoebaNr[x][y] &&
5493           (element == EL_AMOEBA_FULL ||
5494            element == EL_BD_AMOEBA ||
5495            element == EL_AMOEBA_GROWING))
5496       {
5497         AmoebaCnt[AmoebaNr[x][y]]--;
5498         AmoebaCnt2[AmoebaNr[x][y]]--;
5499       }
5500
5501       RemoveField(x, y);
5502
5503       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5504       {
5505         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5506
5507         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5508
5509         if (PLAYERINFO(ex, ey)->use_murphy)
5510           Store[x][y] = EL_EMPTY;
5511       }
5512
5513       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5514          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5515       else if (ELEM_IS_PLAYER(center_element))
5516         Store[x][y] = EL_EMPTY;
5517       else if (center_element == EL_YAMYAM)
5518         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5519       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5520         Store[x][y] = element_info[center_element].content.e[xx][yy];
5521 #if 1
5522       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5523          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5524          otherwise) -- FIX THIS !!! */
5525       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5526         Store[x][y] = element_info[element].content.e[1][1];
5527 #else
5528       else if (!CAN_EXPLODE(element))
5529         Store[x][y] = element_info[element].content.e[1][1];
5530 #endif
5531       else
5532         Store[x][y] = EL_EMPTY;
5533
5534       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5535           center_element == EL_AMOEBA_TO_DIAMOND)
5536         Store2[x][y] = element;
5537
5538       Feld[x][y] = EL_EXPLOSION;
5539       GfxElement[x][y] = artwork_element;
5540
5541       ExplodePhase[x][y] = 1;
5542       ExplodeDelay[x][y] = last_phase;
5543
5544       Stop[x][y] = TRUE;
5545     }
5546
5547     if (center_element == EL_YAMYAM)
5548       game.yamyam_content_nr =
5549         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5550
5551     return;
5552   }
5553
5554   if (Stop[ex][ey])
5555     return;
5556
5557   x = ex;
5558   y = ey;
5559
5560   if (phase == 1)
5561     GfxFrame[x][y] = 0;         /* restart explosion animation */
5562
5563   last_phase = ExplodeDelay[x][y];
5564
5565   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5566
5567   /* this can happen if the player leaves an explosion just in time */
5568   if (GfxElement[x][y] == EL_UNDEFINED)
5569     GfxElement[x][y] = EL_EMPTY;
5570
5571   border_element = Store2[x][y];
5572   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5573     border_element = StorePlayer[x][y];
5574
5575   if (phase == element_info[border_element].ignition_delay ||
5576       phase == last_phase)
5577   {
5578     boolean border_explosion = FALSE;
5579
5580     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5581         !PLAYER_EXPLOSION_PROTECTED(x, y))
5582     {
5583       KillPlayerUnlessExplosionProtected(x, y);
5584       border_explosion = TRUE;
5585     }
5586     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5587     {
5588       Feld[x][y] = Store2[x][y];
5589       Store2[x][y] = 0;
5590       Bang(x, y);
5591       border_explosion = TRUE;
5592     }
5593     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5594     {
5595       AmoebeUmwandeln(x, y);
5596       Store2[x][y] = 0;
5597       border_explosion = TRUE;
5598     }
5599
5600     /* if an element just explodes due to another explosion (chain-reaction),
5601        do not immediately end the new explosion when it was the last frame of
5602        the explosion (as it would be done in the following "if"-statement!) */
5603     if (border_explosion && phase == last_phase)
5604       return;
5605   }
5606
5607   if (phase == last_phase)
5608   {
5609     int element;
5610
5611     element = Feld[x][y] = Store[x][y];
5612     Store[x][y] = Store2[x][y] = 0;
5613     GfxElement[x][y] = EL_UNDEFINED;
5614
5615     /* player can escape from explosions and might therefore be still alive */
5616     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5617         element <= EL_PLAYER_IS_EXPLODING_4)
5618     {
5619       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5620       int explosion_element = EL_PLAYER_1 + player_nr;
5621       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5622       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5623
5624       if (level.use_explosion_element[player_nr])
5625         explosion_element = level.explosion_element[player_nr];
5626
5627       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5628                     element_info[explosion_element].content.e[xx][yy]);
5629     }
5630
5631     /* restore probably existing indestructible background element */
5632     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5633       element = Feld[x][y] = Back[x][y];
5634     Back[x][y] = 0;
5635
5636     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5637     GfxDir[x][y] = MV_NONE;
5638     ChangeDelay[x][y] = 0;
5639     ChangePage[x][y] = -1;
5640
5641     CustomValue[x][y] = 0;
5642
5643     InitField_WithBug2(x, y, FALSE);
5644
5645     TEST_DrawLevelField(x, y);
5646
5647     TestIfElementTouchesCustomElement(x, y);
5648
5649     if (GFX_CRUMBLED(element))
5650       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5651
5652     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5653       StorePlayer[x][y] = 0;
5654
5655     if (ELEM_IS_PLAYER(element))
5656       RelocatePlayer(x, y, element);
5657   }
5658   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5659   {
5660     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5661     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5662
5663     if (phase == delay)
5664       TEST_DrawLevelFieldCrumbled(x, y);
5665
5666     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5667     {
5668       DrawLevelElement(x, y, Back[x][y]);
5669       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5670     }
5671     else if (IS_WALKABLE_UNDER(Back[x][y]))
5672     {
5673       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5674       DrawLevelElementThruMask(x, y, Back[x][y]);
5675     }
5676     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5677       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5678   }
5679 }
5680
5681 void DynaExplode(int ex, int ey)
5682 {
5683   int i, j;
5684   int dynabomb_element = Feld[ex][ey];
5685   int dynabomb_size = 1;
5686   boolean dynabomb_xl = FALSE;
5687   struct PlayerInfo *player;
5688   static int xy[4][2] =
5689   {
5690     { 0, -1 },
5691     { -1, 0 },
5692     { +1, 0 },
5693     { 0, +1 }
5694   };
5695
5696   if (IS_ACTIVE_BOMB(dynabomb_element))
5697   {
5698     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5699     dynabomb_size = player->dynabomb_size;
5700     dynabomb_xl = player->dynabomb_xl;
5701     player->dynabombs_left++;
5702   }
5703
5704   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5705
5706   for (i = 0; i < NUM_DIRECTIONS; i++)
5707   {
5708     for (j = 1; j <= dynabomb_size; j++)
5709     {
5710       int x = ex + j * xy[i][0];
5711       int y = ey + j * xy[i][1];
5712       int element;
5713
5714       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5715         break;
5716
5717       element = Feld[x][y];
5718
5719       /* do not restart explosions of fields with active bombs */
5720       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5721         continue;
5722
5723       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5724
5725       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5726           !IS_DIGGABLE(element) && !dynabomb_xl)
5727         break;
5728     }
5729   }
5730 }
5731
5732 void Bang(int x, int y)
5733 {
5734   int element = MovingOrBlocked2Element(x, y);
5735   int explosion_type = EX_TYPE_NORMAL;
5736
5737   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5738   {
5739     struct PlayerInfo *player = PLAYERINFO(x, y);
5740
5741     element = Feld[x][y] = player->initial_element;
5742
5743     if (level.use_explosion_element[player->index_nr])
5744     {
5745       int explosion_element = level.explosion_element[player->index_nr];
5746
5747       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5748         explosion_type = EX_TYPE_CROSS;
5749       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5750         explosion_type = EX_TYPE_CENTER;
5751     }
5752   }
5753
5754   switch (element)
5755   {
5756     case EL_BUG:
5757     case EL_SPACESHIP:
5758     case EL_BD_BUTTERFLY:
5759     case EL_BD_FIREFLY:
5760     case EL_YAMYAM:
5761     case EL_DARK_YAMYAM:
5762     case EL_ROBOT:
5763     case EL_PACMAN:
5764     case EL_MOLE:
5765       RaiseScoreElement(element);
5766       break;
5767
5768     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5769     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5770     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5771     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5772     case EL_DYNABOMB_INCREASE_NUMBER:
5773     case EL_DYNABOMB_INCREASE_SIZE:
5774     case EL_DYNABOMB_INCREASE_POWER:
5775       explosion_type = EX_TYPE_DYNA;
5776       break;
5777
5778     case EL_DC_LANDMINE:
5779       explosion_type = EX_TYPE_CENTER;
5780       break;
5781
5782     case EL_PENGUIN:
5783     case EL_LAMP:
5784     case EL_LAMP_ACTIVE:
5785     case EL_AMOEBA_TO_DIAMOND:
5786       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5787         explosion_type = EX_TYPE_CENTER;
5788       break;
5789
5790     default:
5791       if (element_info[element].explosion_type == EXPLODES_CROSS)
5792         explosion_type = EX_TYPE_CROSS;
5793       else if (element_info[element].explosion_type == EXPLODES_1X1)
5794         explosion_type = EX_TYPE_CENTER;
5795       break;
5796   }
5797
5798   if (explosion_type == EX_TYPE_DYNA)
5799     DynaExplode(x, y);
5800   else
5801     Explode(x, y, EX_PHASE_START, explosion_type);
5802
5803   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5804 }
5805
5806 void SplashAcid(int x, int y)
5807 {
5808   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5809       (!IN_LEV_FIELD(x - 1, y - 2) ||
5810        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5811     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5812
5813   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5814       (!IN_LEV_FIELD(x + 1, y - 2) ||
5815        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5816     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5817
5818   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5819 }
5820
5821 static void InitBeltMovement()
5822 {
5823   static int belt_base_element[4] =
5824   {
5825     EL_CONVEYOR_BELT_1_LEFT,
5826     EL_CONVEYOR_BELT_2_LEFT,
5827     EL_CONVEYOR_BELT_3_LEFT,
5828     EL_CONVEYOR_BELT_4_LEFT
5829   };
5830   static int belt_base_active_element[4] =
5831   {
5832     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5833     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5834     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5835     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5836   };
5837
5838   int x, y, i, j;
5839
5840   /* set frame order for belt animation graphic according to belt direction */
5841   for (i = 0; i < NUM_BELTS; i++)
5842   {
5843     int belt_nr = i;
5844
5845     for (j = 0; j < NUM_BELT_PARTS; j++)
5846     {
5847       int element = belt_base_active_element[belt_nr] + j;
5848       int graphic_1 = el2img(element);
5849       int graphic_2 = el2panelimg(element);
5850
5851       if (game.belt_dir[i] == MV_LEFT)
5852       {
5853         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5854         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5855       }
5856       else
5857       {
5858         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5859         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5860       }
5861     }
5862   }
5863
5864   SCAN_PLAYFIELD(x, y)
5865   {
5866     int element = Feld[x][y];
5867
5868     for (i = 0; i < NUM_BELTS; i++)
5869     {
5870       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5871       {
5872         int e_belt_nr = getBeltNrFromBeltElement(element);
5873         int belt_nr = i;
5874
5875         if (e_belt_nr == belt_nr)
5876         {
5877           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5878
5879           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5880         }
5881       }
5882     }
5883   }
5884 }
5885
5886 static void ToggleBeltSwitch(int x, int y)
5887 {
5888   static int belt_base_element[4] =
5889   {
5890     EL_CONVEYOR_BELT_1_LEFT,
5891     EL_CONVEYOR_BELT_2_LEFT,
5892     EL_CONVEYOR_BELT_3_LEFT,
5893     EL_CONVEYOR_BELT_4_LEFT
5894   };
5895   static int belt_base_active_element[4] =
5896   {
5897     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5898     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5899     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5900     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5901   };
5902   static int belt_base_switch_element[4] =
5903   {
5904     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5905     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5906     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5907     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5908   };
5909   static int belt_move_dir[4] =
5910   {
5911     MV_LEFT,
5912     MV_NONE,
5913     MV_RIGHT,
5914     MV_NONE,
5915   };
5916
5917   int element = Feld[x][y];
5918   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5919   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5920   int belt_dir = belt_move_dir[belt_dir_nr];
5921   int xx, yy, i;
5922
5923   if (!IS_BELT_SWITCH(element))
5924     return;
5925
5926   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5927   game.belt_dir[belt_nr] = belt_dir;
5928
5929   if (belt_dir_nr == 3)
5930     belt_dir_nr = 1;
5931
5932   /* set frame order for belt animation graphic according to belt direction */
5933   for (i = 0; i < NUM_BELT_PARTS; i++)
5934   {
5935     int element = belt_base_active_element[belt_nr] + i;
5936     int graphic_1 = el2img(element);
5937     int graphic_2 = el2panelimg(element);
5938
5939     if (belt_dir == MV_LEFT)
5940     {
5941       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5942       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5943     }
5944     else
5945     {
5946       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5947       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5948     }
5949   }
5950
5951   SCAN_PLAYFIELD(xx, yy)
5952   {
5953     int element = Feld[xx][yy];
5954
5955     if (IS_BELT_SWITCH(element))
5956     {
5957       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5958
5959       if (e_belt_nr == belt_nr)
5960       {
5961         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5962         TEST_DrawLevelField(xx, yy);
5963       }
5964     }
5965     else if (IS_BELT(element) && belt_dir != MV_NONE)
5966     {
5967       int e_belt_nr = getBeltNrFromBeltElement(element);
5968
5969       if (e_belt_nr == belt_nr)
5970       {
5971         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5972
5973         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5974         TEST_DrawLevelField(xx, yy);
5975       }
5976     }
5977     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5978     {
5979       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5980
5981       if (e_belt_nr == belt_nr)
5982       {
5983         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5984
5985         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5986         TEST_DrawLevelField(xx, yy);
5987       }
5988     }
5989   }
5990 }
5991
5992 static void ToggleSwitchgateSwitch(int x, int y)
5993 {
5994   int xx, yy;
5995
5996   game.switchgate_pos = !game.switchgate_pos;
5997
5998   SCAN_PLAYFIELD(xx, yy)
5999   {
6000     int element = Feld[xx][yy];
6001
6002     if (element == EL_SWITCHGATE_SWITCH_UP)
6003     {
6004       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6005       TEST_DrawLevelField(xx, yy);
6006     }
6007     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6008     {
6009       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6010       TEST_DrawLevelField(xx, yy);
6011     }
6012     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6013     {
6014       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6015       TEST_DrawLevelField(xx, yy);
6016     }
6017     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6018     {
6019       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6020       TEST_DrawLevelField(xx, yy);
6021     }
6022     else if (element == EL_SWITCHGATE_OPEN ||
6023              element == EL_SWITCHGATE_OPENING)
6024     {
6025       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6026
6027       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6028     }
6029     else if (element == EL_SWITCHGATE_CLOSED ||
6030              element == EL_SWITCHGATE_CLOSING)
6031     {
6032       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6033
6034       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6035     }
6036   }
6037 }
6038
6039 static int getInvisibleActiveFromInvisibleElement(int element)
6040 {
6041   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6042           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6043           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6044           element);
6045 }
6046
6047 static int getInvisibleFromInvisibleActiveElement(int element)
6048 {
6049   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6050           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6051           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6052           element);
6053 }
6054
6055 static void RedrawAllLightSwitchesAndInvisibleElements()
6056 {
6057   int x, y;
6058
6059   SCAN_PLAYFIELD(x, y)
6060   {
6061     int element = Feld[x][y];
6062
6063     if (element == EL_LIGHT_SWITCH &&
6064         game.light_time_left > 0)
6065     {
6066       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6067       TEST_DrawLevelField(x, y);
6068     }
6069     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6070              game.light_time_left == 0)
6071     {
6072       Feld[x][y] = EL_LIGHT_SWITCH;
6073       TEST_DrawLevelField(x, y);
6074     }
6075     else if (element == EL_EMC_DRIPPER &&
6076              game.light_time_left > 0)
6077     {
6078       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6079       TEST_DrawLevelField(x, y);
6080     }
6081     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6082              game.light_time_left == 0)
6083     {
6084       Feld[x][y] = EL_EMC_DRIPPER;
6085       TEST_DrawLevelField(x, y);
6086     }
6087     else if (element == EL_INVISIBLE_STEELWALL ||
6088              element == EL_INVISIBLE_WALL ||
6089              element == EL_INVISIBLE_SAND)
6090     {
6091       if (game.light_time_left > 0)
6092         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6093
6094       TEST_DrawLevelField(x, y);
6095
6096       /* uncrumble neighbour fields, if needed */
6097       if (element == EL_INVISIBLE_SAND)
6098         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6099     }
6100     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6101              element == EL_INVISIBLE_WALL_ACTIVE ||
6102              element == EL_INVISIBLE_SAND_ACTIVE)
6103     {
6104       if (game.light_time_left == 0)
6105         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6106
6107       TEST_DrawLevelField(x, y);
6108
6109       /* re-crumble neighbour fields, if needed */
6110       if (element == EL_INVISIBLE_SAND)
6111         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6112     }
6113   }
6114 }
6115
6116 static void RedrawAllInvisibleElementsForLenses()
6117 {
6118   int x, y;
6119
6120   SCAN_PLAYFIELD(x, y)
6121   {
6122     int element = Feld[x][y];
6123
6124     if (element == EL_EMC_DRIPPER &&
6125         game.lenses_time_left > 0)
6126     {
6127       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6128       TEST_DrawLevelField(x, y);
6129     }
6130     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6131              game.lenses_time_left == 0)
6132     {
6133       Feld[x][y] = EL_EMC_DRIPPER;
6134       TEST_DrawLevelField(x, y);
6135     }
6136     else if (element == EL_INVISIBLE_STEELWALL ||
6137              element == EL_INVISIBLE_WALL ||
6138              element == EL_INVISIBLE_SAND)
6139     {
6140       if (game.lenses_time_left > 0)
6141         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6142
6143       TEST_DrawLevelField(x, y);
6144
6145       /* uncrumble neighbour fields, if needed */
6146       if (element == EL_INVISIBLE_SAND)
6147         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6148     }
6149     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6150              element == EL_INVISIBLE_WALL_ACTIVE ||
6151              element == EL_INVISIBLE_SAND_ACTIVE)
6152     {
6153       if (game.lenses_time_left == 0)
6154         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6155
6156       TEST_DrawLevelField(x, y);
6157
6158       /* re-crumble neighbour fields, if needed */
6159       if (element == EL_INVISIBLE_SAND)
6160         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6161     }
6162   }
6163 }
6164
6165 static void RedrawAllInvisibleElementsForMagnifier()
6166 {
6167   int x, y;
6168
6169   SCAN_PLAYFIELD(x, y)
6170   {
6171     int element = Feld[x][y];
6172
6173     if (element == EL_EMC_FAKE_GRASS &&
6174         game.magnify_time_left > 0)
6175     {
6176       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6177       TEST_DrawLevelField(x, y);
6178     }
6179     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6180              game.magnify_time_left == 0)
6181     {
6182       Feld[x][y] = EL_EMC_FAKE_GRASS;
6183       TEST_DrawLevelField(x, y);
6184     }
6185     else if (IS_GATE_GRAY(element) &&
6186              game.magnify_time_left > 0)
6187     {
6188       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6189                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6190                     IS_EM_GATE_GRAY(element) ?
6191                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6192                     IS_EMC_GATE_GRAY(element) ?
6193                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6194                     IS_DC_GATE_GRAY(element) ?
6195                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6196                     element);
6197       TEST_DrawLevelField(x, y);
6198     }
6199     else if (IS_GATE_GRAY_ACTIVE(element) &&
6200              game.magnify_time_left == 0)
6201     {
6202       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6203                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6204                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6205                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6206                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6207                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6208                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6209                     EL_DC_GATE_WHITE_GRAY :
6210                     element);
6211       TEST_DrawLevelField(x, y);
6212     }
6213   }
6214 }
6215
6216 static void ToggleLightSwitch(int x, int y)
6217 {
6218   int element = Feld[x][y];
6219
6220   game.light_time_left =
6221     (element == EL_LIGHT_SWITCH ?
6222      level.time_light * FRAMES_PER_SECOND : 0);
6223
6224   RedrawAllLightSwitchesAndInvisibleElements();
6225 }
6226
6227 static void ActivateTimegateSwitch(int x, int y)
6228 {
6229   int xx, yy;
6230
6231   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6232
6233   SCAN_PLAYFIELD(xx, yy)
6234   {
6235     int element = Feld[xx][yy];
6236
6237     if (element == EL_TIMEGATE_CLOSED ||
6238         element == EL_TIMEGATE_CLOSING)
6239     {
6240       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6241       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6242     }
6243
6244     /*
6245     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6246     {
6247       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6248       TEST_DrawLevelField(xx, yy);
6249     }
6250     */
6251
6252   }
6253
6254   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6255                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6256 }
6257
6258 void Impact(int x, int y)
6259 {
6260   boolean last_line = (y == lev_fieldy - 1);
6261   boolean object_hit = FALSE;
6262   boolean impact = (last_line || object_hit);
6263   int element = Feld[x][y];
6264   int smashed = EL_STEELWALL;
6265
6266   if (!last_line)       /* check if element below was hit */
6267   {
6268     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6269       return;
6270
6271     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6272                                          MovDir[x][y + 1] != MV_DOWN ||
6273                                          MovPos[x][y + 1] <= TILEY / 2));
6274
6275     /* do not smash moving elements that left the smashed field in time */
6276     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6277         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6278       object_hit = FALSE;
6279
6280 #if USE_QUICKSAND_IMPACT_BUGFIX
6281     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6282     {
6283       RemoveMovingField(x, y + 1);
6284       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6285       Feld[x][y + 2] = EL_ROCK;
6286       TEST_DrawLevelField(x, y + 2);
6287
6288       object_hit = TRUE;
6289     }
6290
6291     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6292     {
6293       RemoveMovingField(x, y + 1);
6294       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6295       Feld[x][y + 2] = EL_ROCK;
6296       TEST_DrawLevelField(x, y + 2);
6297
6298       object_hit = TRUE;
6299     }
6300 #endif
6301
6302     if (object_hit)
6303       smashed = MovingOrBlocked2Element(x, y + 1);
6304
6305     impact = (last_line || object_hit);
6306   }
6307
6308   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6309   {
6310     SplashAcid(x, y + 1);
6311     return;
6312   }
6313
6314   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6315   /* only reset graphic animation if graphic really changes after impact */
6316   if (impact &&
6317       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6318   {
6319     ResetGfxAnimation(x, y);
6320     TEST_DrawLevelField(x, y);
6321   }
6322
6323   if (impact && CAN_EXPLODE_IMPACT(element))
6324   {
6325     Bang(x, y);
6326     return;
6327   }
6328   else if (impact && element == EL_PEARL &&
6329            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6330   {
6331     ResetGfxAnimation(x, y);
6332
6333     Feld[x][y] = EL_PEARL_BREAKING;
6334     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6335     return;
6336   }
6337   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6338   {
6339     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6340
6341     return;
6342   }
6343
6344   if (impact && element == EL_AMOEBA_DROP)
6345   {
6346     if (object_hit && IS_PLAYER(x, y + 1))
6347       KillPlayerUnlessEnemyProtected(x, y + 1);
6348     else if (object_hit && smashed == EL_PENGUIN)
6349       Bang(x, y + 1);
6350     else
6351     {
6352       Feld[x][y] = EL_AMOEBA_GROWING;
6353       Store[x][y] = EL_AMOEBA_WET;
6354
6355       ResetRandomAnimationValue(x, y);
6356     }
6357     return;
6358   }
6359
6360   if (object_hit)               /* check which object was hit */
6361   {
6362     if ((CAN_PASS_MAGIC_WALL(element) && 
6363          (smashed == EL_MAGIC_WALL ||
6364           smashed == EL_BD_MAGIC_WALL)) ||
6365         (CAN_PASS_DC_MAGIC_WALL(element) &&
6366          smashed == EL_DC_MAGIC_WALL))
6367     {
6368       int xx, yy;
6369       int activated_magic_wall =
6370         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6371          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6372          EL_DC_MAGIC_WALL_ACTIVE);
6373
6374       /* activate magic wall / mill */
6375       SCAN_PLAYFIELD(xx, yy)
6376       {
6377         if (Feld[xx][yy] == smashed)
6378           Feld[xx][yy] = activated_magic_wall;
6379       }
6380
6381       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6382       game.magic_wall_active = TRUE;
6383
6384       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6385                             SND_MAGIC_WALL_ACTIVATING :
6386                             smashed == EL_BD_MAGIC_WALL ?
6387                             SND_BD_MAGIC_WALL_ACTIVATING :
6388                             SND_DC_MAGIC_WALL_ACTIVATING));
6389     }
6390
6391     if (IS_PLAYER(x, y + 1))
6392     {
6393       if (CAN_SMASH_PLAYER(element))
6394       {
6395         KillPlayerUnlessEnemyProtected(x, y + 1);
6396         return;
6397       }
6398     }
6399     else if (smashed == EL_PENGUIN)
6400     {
6401       if (CAN_SMASH_PLAYER(element))
6402       {
6403         Bang(x, y + 1);
6404         return;
6405       }
6406     }
6407     else if (element == EL_BD_DIAMOND)
6408     {
6409       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6410       {
6411         Bang(x, y + 1);
6412         return;
6413       }
6414     }
6415     else if (((element == EL_SP_INFOTRON ||
6416                element == EL_SP_ZONK) &&
6417               (smashed == EL_SP_SNIKSNAK ||
6418                smashed == EL_SP_ELECTRON ||
6419                smashed == EL_SP_DISK_ORANGE)) ||
6420              (element == EL_SP_INFOTRON &&
6421               smashed == EL_SP_DISK_YELLOW))
6422     {
6423       Bang(x, y + 1);
6424       return;
6425     }
6426     else if (CAN_SMASH_EVERYTHING(element))
6427     {
6428       if (IS_CLASSIC_ENEMY(smashed) ||
6429           CAN_EXPLODE_SMASHED(smashed))
6430       {
6431         Bang(x, y + 1);
6432         return;
6433       }
6434       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6435       {
6436         if (smashed == EL_LAMP ||
6437             smashed == EL_LAMP_ACTIVE)
6438         {
6439           Bang(x, y + 1);
6440           return;
6441         }
6442         else if (smashed == EL_NUT)
6443         {
6444           Feld[x][y + 1] = EL_NUT_BREAKING;
6445           PlayLevelSound(x, y, SND_NUT_BREAKING);
6446           RaiseScoreElement(EL_NUT);
6447           return;
6448         }
6449         else if (smashed == EL_PEARL)
6450         {
6451           ResetGfxAnimation(x, y);
6452
6453           Feld[x][y + 1] = EL_PEARL_BREAKING;
6454           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6455           return;
6456         }
6457         else if (smashed == EL_DIAMOND)
6458         {
6459           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6460           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6461           return;
6462         }
6463         else if (IS_BELT_SWITCH(smashed))
6464         {
6465           ToggleBeltSwitch(x, y + 1);
6466         }
6467         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6468                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6469                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6470                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6471         {
6472           ToggleSwitchgateSwitch(x, y + 1);
6473         }
6474         else if (smashed == EL_LIGHT_SWITCH ||
6475                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6476         {
6477           ToggleLightSwitch(x, y + 1);
6478         }
6479         else
6480         {
6481           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6482
6483           CheckElementChangeBySide(x, y + 1, smashed, element,
6484                                    CE_SWITCHED, CH_SIDE_TOP);
6485           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6486                                             CH_SIDE_TOP);
6487         }
6488       }
6489       else
6490       {
6491         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6492       }
6493     }
6494   }
6495
6496   /* play sound of magic wall / mill */
6497   if (!last_line &&
6498       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6499        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6500        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6501   {
6502     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6503       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6504     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6505       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6506     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6507       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6508
6509     return;
6510   }
6511
6512   /* play sound of object that hits the ground */
6513   if (last_line || object_hit)
6514     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6515 }
6516
6517 inline static void TurnRoundExt(int x, int y)
6518 {
6519   static struct
6520   {
6521     int dx, dy;
6522   } move_xy[] =
6523   {
6524     {  0,  0 },
6525     { -1,  0 },
6526     { +1,  0 },
6527     {  0,  0 },
6528     {  0, -1 },
6529     {  0,  0 }, { 0, 0 }, { 0, 0 },
6530     {  0, +1 }
6531   };
6532   static struct
6533   {
6534     int left, right, back;
6535   } turn[] =
6536   {
6537     { 0,        0,              0        },
6538     { MV_DOWN,  MV_UP,          MV_RIGHT },
6539     { MV_UP,    MV_DOWN,        MV_LEFT  },
6540     { 0,        0,              0        },
6541     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6542     { 0,        0,              0        },
6543     { 0,        0,              0        },
6544     { 0,        0,              0        },
6545     { MV_RIGHT, MV_LEFT,        MV_UP    }
6546   };
6547
6548   int element = Feld[x][y];
6549   int move_pattern = element_info[element].move_pattern;
6550
6551   int old_move_dir = MovDir[x][y];
6552   int left_dir  = turn[old_move_dir].left;
6553   int right_dir = turn[old_move_dir].right;
6554   int back_dir  = turn[old_move_dir].back;
6555
6556   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6557   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6558   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6559   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6560
6561   int left_x  = x + left_dx,  left_y  = y + left_dy;
6562   int right_x = x + right_dx, right_y = y + right_dy;
6563   int move_x  = x + move_dx,  move_y  = y + move_dy;
6564
6565   int xx, yy;
6566
6567   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6568   {
6569     TestIfBadThingTouchesOtherBadThing(x, y);
6570
6571     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6572       MovDir[x][y] = right_dir;
6573     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6574       MovDir[x][y] = left_dir;
6575
6576     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6577       MovDelay[x][y] = 9;
6578     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6579       MovDelay[x][y] = 1;
6580   }
6581   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6582   {
6583     TestIfBadThingTouchesOtherBadThing(x, y);
6584
6585     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6586       MovDir[x][y] = left_dir;
6587     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6588       MovDir[x][y] = right_dir;
6589
6590     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6591       MovDelay[x][y] = 9;
6592     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6593       MovDelay[x][y] = 1;
6594   }
6595   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6596   {
6597     TestIfBadThingTouchesOtherBadThing(x, y);
6598
6599     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6600       MovDir[x][y] = left_dir;
6601     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6602       MovDir[x][y] = right_dir;
6603
6604     if (MovDir[x][y] != old_move_dir)
6605       MovDelay[x][y] = 9;
6606   }
6607   else if (element == EL_YAMYAM)
6608   {
6609     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6610     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6611
6612     if (can_turn_left && can_turn_right)
6613       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6614     else if (can_turn_left)
6615       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6616     else if (can_turn_right)
6617       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6618     else
6619       MovDir[x][y] = back_dir;
6620
6621     MovDelay[x][y] = 16 + 16 * RND(3);
6622   }
6623   else if (element == EL_DARK_YAMYAM)
6624   {
6625     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6626                                                          left_x, left_y);
6627     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6628                                                          right_x, right_y);
6629
6630     if (can_turn_left && can_turn_right)
6631       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6632     else if (can_turn_left)
6633       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6634     else if (can_turn_right)
6635       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6636     else
6637       MovDir[x][y] = back_dir;
6638
6639     MovDelay[x][y] = 16 + 16 * RND(3);
6640   }
6641   else if (element == EL_PACMAN)
6642   {
6643     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6644     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6645
6646     if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6648     else if (can_turn_left)
6649       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6650     else if (can_turn_right)
6651       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     MovDelay[x][y] = 6 + RND(40);
6656   }
6657   else if (element == EL_PIG)
6658   {
6659     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6660     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6661     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6662     boolean should_turn_left, should_turn_right, should_move_on;
6663     int rnd_value = 24;
6664     int rnd = RND(rnd_value);
6665
6666     should_turn_left = (can_turn_left &&
6667                         (!can_move_on ||
6668                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6669                                                    y + back_dy + left_dy)));
6670     should_turn_right = (can_turn_right &&
6671                          (!can_move_on ||
6672                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6673                                                     y + back_dy + right_dy)));
6674     should_move_on = (can_move_on &&
6675                       (!can_turn_left ||
6676                        !can_turn_right ||
6677                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6678                                                  y + move_dy + left_dy) ||
6679                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6680                                                  y + move_dy + right_dy)));
6681
6682     if (should_turn_left || should_turn_right || should_move_on)
6683     {
6684       if (should_turn_left && should_turn_right && should_move_on)
6685         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6686                         rnd < 2 * rnd_value / 3 ? right_dir :
6687                         old_move_dir);
6688       else if (should_turn_left && should_turn_right)
6689         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6690       else if (should_turn_left && should_move_on)
6691         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6692       else if (should_turn_right && should_move_on)
6693         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6694       else if (should_turn_left)
6695         MovDir[x][y] = left_dir;
6696       else if (should_turn_right)
6697         MovDir[x][y] = right_dir;
6698       else if (should_move_on)
6699         MovDir[x][y] = old_move_dir;
6700     }
6701     else if (can_move_on && rnd > rnd_value / 8)
6702       MovDir[x][y] = old_move_dir;
6703     else if (can_turn_left && can_turn_right)
6704       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6705     else if (can_turn_left && rnd > rnd_value / 8)
6706       MovDir[x][y] = left_dir;
6707     else if (can_turn_right && rnd > rnd_value/8)
6708       MovDir[x][y] = right_dir;
6709     else
6710       MovDir[x][y] = back_dir;
6711
6712     xx = x + move_xy[MovDir[x][y]].dx;
6713     yy = y + move_xy[MovDir[x][y]].dy;
6714
6715     if (!IN_LEV_FIELD(xx, yy) ||
6716         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6717       MovDir[x][y] = old_move_dir;
6718
6719     MovDelay[x][y] = 0;
6720   }
6721   else if (element == EL_DRAGON)
6722   {
6723     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6724     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6725     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6726     int rnd_value = 24;
6727     int rnd = RND(rnd_value);
6728
6729     if (can_move_on && rnd > rnd_value / 8)
6730       MovDir[x][y] = old_move_dir;
6731     else if (can_turn_left && can_turn_right)
6732       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6733     else if (can_turn_left && rnd > rnd_value / 8)
6734       MovDir[x][y] = left_dir;
6735     else if (can_turn_right && rnd > rnd_value / 8)
6736       MovDir[x][y] = right_dir;
6737     else
6738       MovDir[x][y] = back_dir;
6739
6740     xx = x + move_xy[MovDir[x][y]].dx;
6741     yy = y + move_xy[MovDir[x][y]].dy;
6742
6743     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6744       MovDir[x][y] = old_move_dir;
6745
6746     MovDelay[x][y] = 0;
6747   }
6748   else if (element == EL_MOLE)
6749   {
6750     boolean can_move_on =
6751       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6752                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6753                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6754     if (!can_move_on)
6755     {
6756       boolean can_turn_left =
6757         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6758                               IS_AMOEBOID(Feld[left_x][left_y])));
6759
6760       boolean can_turn_right =
6761         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6762                               IS_AMOEBOID(Feld[right_x][right_y])));
6763
6764       if (can_turn_left && can_turn_right)
6765         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6766       else if (can_turn_left)
6767         MovDir[x][y] = left_dir;
6768       else
6769         MovDir[x][y] = right_dir;
6770     }
6771
6772     if (MovDir[x][y] != old_move_dir)
6773       MovDelay[x][y] = 9;
6774   }
6775   else if (element == EL_BALLOON)
6776   {
6777     MovDir[x][y] = game.wind_direction;
6778     MovDelay[x][y] = 0;
6779   }
6780   else if (element == EL_SPRING)
6781   {
6782     if (MovDir[x][y] & MV_HORIZONTAL)
6783     {
6784       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6785           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6786       {
6787         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6788         ResetGfxAnimation(move_x, move_y);
6789         TEST_DrawLevelField(move_x, move_y);
6790
6791         MovDir[x][y] = back_dir;
6792       }
6793       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6794                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6795         MovDir[x][y] = MV_NONE;
6796     }
6797
6798     MovDelay[x][y] = 0;
6799   }
6800   else if (element == EL_ROBOT ||
6801            element == EL_SATELLITE ||
6802            element == EL_PENGUIN ||
6803            element == EL_EMC_ANDROID)
6804   {
6805     int attr_x = -1, attr_y = -1;
6806
6807     if (AllPlayersGone)
6808     {
6809       attr_x = ExitX;
6810       attr_y = ExitY;
6811     }
6812     else
6813     {
6814       int i;
6815
6816       for (i = 0; i < MAX_PLAYERS; i++)
6817       {
6818         struct PlayerInfo *player = &stored_player[i];
6819         int jx = player->jx, jy = player->jy;
6820
6821         if (!player->active)
6822           continue;
6823
6824         if (attr_x == -1 ||
6825             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6826         {
6827           attr_x = jx;
6828           attr_y = jy;
6829         }
6830       }
6831     }
6832
6833     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6834         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6835          game.engine_version < VERSION_IDENT(3,1,0,0)))
6836     {
6837       attr_x = ZX;
6838       attr_y = ZY;
6839     }
6840
6841     if (element == EL_PENGUIN)
6842     {
6843       int i;
6844       static int xy[4][2] =
6845       {
6846         { 0, -1 },
6847         { -1, 0 },
6848         { +1, 0 },
6849         { 0, +1 }
6850       };
6851
6852       for (i = 0; i < NUM_DIRECTIONS; i++)
6853       {
6854         int ex = x + xy[i][0];
6855         int ey = y + xy[i][1];
6856
6857         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6858                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6859                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6860                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6861         {
6862           attr_x = ex;
6863           attr_y = ey;
6864           break;
6865         }
6866       }
6867     }
6868
6869     MovDir[x][y] = MV_NONE;
6870     if (attr_x < x)
6871       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6872     else if (attr_x > x)
6873       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6874     if (attr_y < y)
6875       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6876     else if (attr_y > y)
6877       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6878
6879     if (element == EL_ROBOT)
6880     {
6881       int newx, newy;
6882
6883       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6884         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6885       Moving2Blocked(x, y, &newx, &newy);
6886
6887       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6888         MovDelay[x][y] = 8 + 8 * !RND(3);
6889       else
6890         MovDelay[x][y] = 16;
6891     }
6892     else if (element == EL_PENGUIN)
6893     {
6894       int newx, newy;
6895
6896       MovDelay[x][y] = 1;
6897
6898       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6899       {
6900         boolean first_horiz = RND(2);
6901         int new_move_dir = MovDir[x][y];
6902
6903         MovDir[x][y] =
6904           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6905         Moving2Blocked(x, y, &newx, &newy);
6906
6907         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6908           return;
6909
6910         MovDir[x][y] =
6911           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6912         Moving2Blocked(x, y, &newx, &newy);
6913
6914         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6915           return;
6916
6917         MovDir[x][y] = old_move_dir;
6918         return;
6919       }
6920     }
6921     else if (element == EL_SATELLITE)
6922     {
6923       int newx, newy;
6924
6925       MovDelay[x][y] = 1;
6926
6927       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6928       {
6929         boolean first_horiz = RND(2);
6930         int new_move_dir = MovDir[x][y];
6931
6932         MovDir[x][y] =
6933           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6934         Moving2Blocked(x, y, &newx, &newy);
6935
6936         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6937           return;
6938
6939         MovDir[x][y] =
6940           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6941         Moving2Blocked(x, y, &newx, &newy);
6942
6943         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6944           return;
6945
6946         MovDir[x][y] = old_move_dir;
6947         return;
6948       }
6949     }
6950     else if (element == EL_EMC_ANDROID)
6951     {
6952       static int check_pos[16] =
6953       {
6954         -1,             /*  0 => (invalid)          */
6955         7,              /*  1 => MV_LEFT            */
6956         3,              /*  2 => MV_RIGHT           */
6957         -1,             /*  3 => (invalid)          */
6958         1,              /*  4 =>            MV_UP   */
6959         0,              /*  5 => MV_LEFT  | MV_UP   */
6960         2,              /*  6 => MV_RIGHT | MV_UP   */
6961         -1,             /*  7 => (invalid)          */
6962         5,              /*  8 =>            MV_DOWN */
6963         6,              /*  9 => MV_LEFT  | MV_DOWN */
6964         4,              /* 10 => MV_RIGHT | MV_DOWN */
6965         -1,             /* 11 => (invalid)          */
6966         -1,             /* 12 => (invalid)          */
6967         -1,             /* 13 => (invalid)          */
6968         -1,             /* 14 => (invalid)          */
6969         -1,             /* 15 => (invalid)          */
6970       };
6971       static struct
6972       {
6973         int dx, dy;
6974         int dir;
6975       } check_xy[8] =
6976       {
6977         { -1, -1,       MV_LEFT  | MV_UP   },
6978         {  0, -1,                  MV_UP   },
6979         { +1, -1,       MV_RIGHT | MV_UP   },
6980         { +1,  0,       MV_RIGHT           },
6981         { +1, +1,       MV_RIGHT | MV_DOWN },
6982         {  0, +1,                  MV_DOWN },
6983         { -1, +1,       MV_LEFT  | MV_DOWN },
6984         { -1,  0,       MV_LEFT            },
6985       };
6986       int start_pos, check_order;
6987       boolean can_clone = FALSE;
6988       int i;
6989
6990       /* check if there is any free field around current position */
6991       for (i = 0; i < 8; i++)
6992       {
6993         int newx = x + check_xy[i].dx;
6994         int newy = y + check_xy[i].dy;
6995
6996         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6997         {
6998           can_clone = TRUE;
6999
7000           break;
7001         }
7002       }
7003
7004       if (can_clone)            /* randomly find an element to clone */
7005       {
7006         can_clone = FALSE;
7007
7008         start_pos = check_pos[RND(8)];
7009         check_order = (RND(2) ? -1 : +1);
7010
7011         for (i = 0; i < 8; i++)
7012         {
7013           int pos_raw = start_pos + i * check_order;
7014           int pos = (pos_raw + 8) % 8;
7015           int newx = x + check_xy[pos].dx;
7016           int newy = y + check_xy[pos].dy;
7017
7018           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7019           {
7020             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7021             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7022
7023             Store[x][y] = Feld[newx][newy];
7024
7025             can_clone = TRUE;
7026
7027             break;
7028           }
7029         }
7030       }
7031
7032       if (can_clone)            /* randomly find a direction to move */
7033       {
7034         can_clone = FALSE;
7035
7036         start_pos = check_pos[RND(8)];
7037         check_order = (RND(2) ? -1 : +1);
7038
7039         for (i = 0; i < 8; i++)
7040         {
7041           int pos_raw = start_pos + i * check_order;
7042           int pos = (pos_raw + 8) % 8;
7043           int newx = x + check_xy[pos].dx;
7044           int newy = y + check_xy[pos].dy;
7045           int new_move_dir = check_xy[pos].dir;
7046
7047           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7048           {
7049             MovDir[x][y] = new_move_dir;
7050             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7051
7052             can_clone = TRUE;
7053
7054             break;
7055           }
7056         }
7057       }
7058
7059       if (can_clone)            /* cloning and moving successful */
7060         return;
7061
7062       /* cannot clone -- try to move towards player */
7063
7064       start_pos = check_pos[MovDir[x][y] & 0x0f];
7065       check_order = (RND(2) ? -1 : +1);
7066
7067       for (i = 0; i < 3; i++)
7068       {
7069         /* first check start_pos, then previous/next or (next/previous) pos */
7070         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7071         int pos = (pos_raw + 8) % 8;
7072         int newx = x + check_xy[pos].dx;
7073         int newy = y + check_xy[pos].dy;
7074         int new_move_dir = check_xy[pos].dir;
7075
7076         if (IS_PLAYER(newx, newy))
7077           break;
7078
7079         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7080         {
7081           MovDir[x][y] = new_move_dir;
7082           MovDelay[x][y] = level.android_move_time * 8 + 1;
7083
7084           break;
7085         }
7086       }
7087     }
7088   }
7089   else if (move_pattern == MV_TURNING_LEFT ||
7090            move_pattern == MV_TURNING_RIGHT ||
7091            move_pattern == MV_TURNING_LEFT_RIGHT ||
7092            move_pattern == MV_TURNING_RIGHT_LEFT ||
7093            move_pattern == MV_TURNING_RANDOM ||
7094            move_pattern == MV_ALL_DIRECTIONS)
7095   {
7096     boolean can_turn_left =
7097       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7098     boolean can_turn_right =
7099       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7100
7101     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7102       return;
7103
7104     if (move_pattern == MV_TURNING_LEFT)
7105       MovDir[x][y] = left_dir;
7106     else if (move_pattern == MV_TURNING_RIGHT)
7107       MovDir[x][y] = right_dir;
7108     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7109       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7110     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7111       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7112     else if (move_pattern == MV_TURNING_RANDOM)
7113       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7114                       can_turn_right && !can_turn_left ? right_dir :
7115                       RND(2) ? left_dir : right_dir);
7116     else if (can_turn_left && can_turn_right)
7117       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7118     else if (can_turn_left)
7119       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7120     else if (can_turn_right)
7121       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7122     else
7123       MovDir[x][y] = back_dir;
7124
7125     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7126   }
7127   else if (move_pattern == MV_HORIZONTAL ||
7128            move_pattern == MV_VERTICAL)
7129   {
7130     if (move_pattern & old_move_dir)
7131       MovDir[x][y] = back_dir;
7132     else if (move_pattern == MV_HORIZONTAL)
7133       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7134     else if (move_pattern == MV_VERTICAL)
7135       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7136
7137     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138   }
7139   else if (move_pattern & MV_ANY_DIRECTION)
7140   {
7141     MovDir[x][y] = move_pattern;
7142     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143   }
7144   else if (move_pattern & MV_WIND_DIRECTION)
7145   {
7146     MovDir[x][y] = game.wind_direction;
7147     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7148   }
7149   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7150   {
7151     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7152       MovDir[x][y] = left_dir;
7153     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7154       MovDir[x][y] = right_dir;
7155
7156     if (MovDir[x][y] != old_move_dir)
7157       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7158   }
7159   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7160   {
7161     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7162       MovDir[x][y] = right_dir;
7163     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7164       MovDir[x][y] = left_dir;
7165
7166     if (MovDir[x][y] != old_move_dir)
7167       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7168   }
7169   else if (move_pattern == MV_TOWARDS_PLAYER ||
7170            move_pattern == MV_AWAY_FROM_PLAYER)
7171   {
7172     int attr_x = -1, attr_y = -1;
7173     int newx, newy;
7174     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7175
7176     if (AllPlayersGone)
7177     {
7178       attr_x = ExitX;
7179       attr_y = ExitY;
7180     }
7181     else
7182     {
7183       int i;
7184
7185       for (i = 0; i < MAX_PLAYERS; i++)
7186       {
7187         struct PlayerInfo *player = &stored_player[i];
7188         int jx = player->jx, jy = player->jy;
7189
7190         if (!player->active)
7191           continue;
7192
7193         if (attr_x == -1 ||
7194             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7195         {
7196           attr_x = jx;
7197           attr_y = jy;
7198         }
7199       }
7200     }
7201
7202     MovDir[x][y] = MV_NONE;
7203     if (attr_x < x)
7204       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7205     else if (attr_x > x)
7206       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7207     if (attr_y < y)
7208       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7209     else if (attr_y > y)
7210       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7211
7212     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7213
7214     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7215     {
7216       boolean first_horiz = RND(2);
7217       int new_move_dir = MovDir[x][y];
7218
7219       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7220       {
7221         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7222         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7223
7224         return;
7225       }
7226
7227       MovDir[x][y] =
7228         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7229       Moving2Blocked(x, y, &newx, &newy);
7230
7231       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7232         return;
7233
7234       MovDir[x][y] =
7235         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7236       Moving2Blocked(x, y, &newx, &newy);
7237
7238       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7239         return;
7240
7241       MovDir[x][y] = old_move_dir;
7242     }
7243   }
7244   else if (move_pattern == MV_WHEN_PUSHED ||
7245            move_pattern == MV_WHEN_DROPPED)
7246   {
7247     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7248       MovDir[x][y] = MV_NONE;
7249
7250     MovDelay[x][y] = 0;
7251   }
7252   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7253   {
7254     static int test_xy[7][2] =
7255     {
7256       { 0, -1 },
7257       { -1, 0 },
7258       { +1, 0 },
7259       { 0, +1 },
7260       { 0, -1 },
7261       { -1, 0 },
7262       { +1, 0 },
7263     };
7264     static int test_dir[7] =
7265     {
7266       MV_UP,
7267       MV_LEFT,
7268       MV_RIGHT,
7269       MV_DOWN,
7270       MV_UP,
7271       MV_LEFT,
7272       MV_RIGHT,
7273     };
7274     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7275     int move_preference = -1000000;     /* start with very low preference */
7276     int new_move_dir = MV_NONE;
7277     int start_test = RND(4);
7278     int i;
7279
7280     for (i = 0; i < NUM_DIRECTIONS; i++)
7281     {
7282       int move_dir = test_dir[start_test + i];
7283       int move_dir_preference;
7284
7285       xx = x + test_xy[start_test + i][0];
7286       yy = y + test_xy[start_test + i][1];
7287
7288       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7289           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7290       {
7291         new_move_dir = move_dir;
7292
7293         break;
7294       }
7295
7296       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7297         continue;
7298
7299       move_dir_preference = -1 * RunnerVisit[xx][yy];
7300       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7301         move_dir_preference = PlayerVisit[xx][yy];
7302
7303       if (move_dir_preference > move_preference)
7304       {
7305         /* prefer field that has not been visited for the longest time */
7306         move_preference = move_dir_preference;
7307         new_move_dir = move_dir;
7308       }
7309       else if (move_dir_preference == move_preference &&
7310                move_dir == old_move_dir)
7311       {
7312         /* prefer last direction when all directions are preferred equally */
7313         move_preference = move_dir_preference;
7314         new_move_dir = move_dir;
7315       }
7316     }
7317
7318     MovDir[x][y] = new_move_dir;
7319     if (old_move_dir != new_move_dir)
7320       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7321   }
7322 }
7323
7324 static void TurnRound(int x, int y)
7325 {
7326   int direction = MovDir[x][y];
7327
7328   TurnRoundExt(x, y);
7329
7330   GfxDir[x][y] = MovDir[x][y];
7331
7332   if (direction != MovDir[x][y])
7333     GfxFrame[x][y] = 0;
7334
7335   if (MovDelay[x][y])
7336     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7337
7338   ResetGfxFrame(x, y);
7339 }
7340
7341 static boolean JustBeingPushed(int x, int y)
7342 {
7343   int i;
7344
7345   for (i = 0; i < MAX_PLAYERS; i++)
7346   {
7347     struct PlayerInfo *player = &stored_player[i];
7348
7349     if (player->active && player->is_pushing && player->MovPos)
7350     {
7351       int next_jx = player->jx + (player->jx - player->last_jx);
7352       int next_jy = player->jy + (player->jy - player->last_jy);
7353
7354       if (x == next_jx && y == next_jy)
7355         return TRUE;
7356     }
7357   }
7358
7359   return FALSE;
7360 }
7361
7362 void StartMoving(int x, int y)
7363 {
7364   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7365   int element = Feld[x][y];
7366
7367   if (Stop[x][y])
7368     return;
7369
7370   if (MovDelay[x][y] == 0)
7371     GfxAction[x][y] = ACTION_DEFAULT;
7372
7373   if (CAN_FALL(element) && y < lev_fieldy - 1)
7374   {
7375     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7376         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7377       if (JustBeingPushed(x, y))
7378         return;
7379
7380     if (element == EL_QUICKSAND_FULL)
7381     {
7382       if (IS_FREE(x, y + 1))
7383       {
7384         InitMovingField(x, y, MV_DOWN);
7385         started_moving = TRUE;
7386
7387         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7388 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7389         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7390           Store[x][y] = EL_ROCK;
7391 #else
7392         Store[x][y] = EL_ROCK;
7393 #endif
7394
7395         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7396       }
7397       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7398       {
7399         if (!MovDelay[x][y])
7400         {
7401           MovDelay[x][y] = TILEY + 1;
7402
7403           ResetGfxAnimation(x, y);
7404           ResetGfxAnimation(x, y + 1);
7405         }
7406
7407         if (MovDelay[x][y])
7408         {
7409           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7410           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7411
7412           MovDelay[x][y]--;
7413           if (MovDelay[x][y])
7414             return;
7415         }
7416
7417         Feld[x][y] = EL_QUICKSAND_EMPTY;
7418         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7419         Store[x][y + 1] = Store[x][y];
7420         Store[x][y] = 0;
7421
7422         PlayLevelSoundAction(x, y, ACTION_FILLING);
7423       }
7424       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7425       {
7426         if (!MovDelay[x][y])
7427         {
7428           MovDelay[x][y] = TILEY + 1;
7429
7430           ResetGfxAnimation(x, y);
7431           ResetGfxAnimation(x, y + 1);
7432         }
7433
7434         if (MovDelay[x][y])
7435         {
7436           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7437           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7438
7439           MovDelay[x][y]--;
7440           if (MovDelay[x][y])
7441             return;
7442         }
7443
7444         Feld[x][y] = EL_QUICKSAND_EMPTY;
7445         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7446         Store[x][y + 1] = Store[x][y];
7447         Store[x][y] = 0;
7448
7449         PlayLevelSoundAction(x, y, ACTION_FILLING);
7450       }
7451     }
7452     else if (element == EL_QUICKSAND_FAST_FULL)
7453     {
7454       if (IS_FREE(x, y + 1))
7455       {
7456         InitMovingField(x, y, MV_DOWN);
7457         started_moving = TRUE;
7458
7459         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7460 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7461         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7462           Store[x][y] = EL_ROCK;
7463 #else
7464         Store[x][y] = EL_ROCK;
7465 #endif
7466
7467         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7468       }
7469       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7470       {
7471         if (!MovDelay[x][y])
7472         {
7473           MovDelay[x][y] = TILEY + 1;
7474
7475           ResetGfxAnimation(x, y);
7476           ResetGfxAnimation(x, y + 1);
7477         }
7478
7479         if (MovDelay[x][y])
7480         {
7481           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7482           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7483
7484           MovDelay[x][y]--;
7485           if (MovDelay[x][y])
7486             return;
7487         }
7488
7489         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7490         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7491         Store[x][y + 1] = Store[x][y];
7492         Store[x][y] = 0;
7493
7494         PlayLevelSoundAction(x, y, ACTION_FILLING);
7495       }
7496       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7497       {
7498         if (!MovDelay[x][y])
7499         {
7500           MovDelay[x][y] = TILEY + 1;
7501
7502           ResetGfxAnimation(x, y);
7503           ResetGfxAnimation(x, y + 1);
7504         }
7505
7506         if (MovDelay[x][y])
7507         {
7508           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7509           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7510
7511           MovDelay[x][y]--;
7512           if (MovDelay[x][y])
7513             return;
7514         }
7515
7516         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7517         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7518         Store[x][y + 1] = Store[x][y];
7519         Store[x][y] = 0;
7520
7521         PlayLevelSoundAction(x, y, ACTION_FILLING);
7522       }
7523     }
7524     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7525              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7526     {
7527       InitMovingField(x, y, MV_DOWN);
7528       started_moving = TRUE;
7529
7530       Feld[x][y] = EL_QUICKSAND_FILLING;
7531       Store[x][y] = element;
7532
7533       PlayLevelSoundAction(x, y, ACTION_FILLING);
7534     }
7535     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7536              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7537     {
7538       InitMovingField(x, y, MV_DOWN);
7539       started_moving = TRUE;
7540
7541       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7542       Store[x][y] = element;
7543
7544       PlayLevelSoundAction(x, y, ACTION_FILLING);
7545     }
7546     else if (element == EL_MAGIC_WALL_FULL)
7547     {
7548       if (IS_FREE(x, y + 1))
7549       {
7550         InitMovingField(x, y, MV_DOWN);
7551         started_moving = TRUE;
7552
7553         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7554         Store[x][y] = EL_CHANGED(Store[x][y]);
7555       }
7556       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7557       {
7558         if (!MovDelay[x][y])
7559           MovDelay[x][y] = TILEY / 4 + 1;
7560
7561         if (MovDelay[x][y])
7562         {
7563           MovDelay[x][y]--;
7564           if (MovDelay[x][y])
7565             return;
7566         }
7567
7568         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7569         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7570         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7571         Store[x][y] = 0;
7572       }
7573     }
7574     else if (element == EL_BD_MAGIC_WALL_FULL)
7575     {
7576       if (IS_FREE(x, y + 1))
7577       {
7578         InitMovingField(x, y, MV_DOWN);
7579         started_moving = TRUE;
7580
7581         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7582         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7583       }
7584       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7585       {
7586         if (!MovDelay[x][y])
7587           MovDelay[x][y] = TILEY / 4 + 1;
7588
7589         if (MovDelay[x][y])
7590         {
7591           MovDelay[x][y]--;
7592           if (MovDelay[x][y])
7593             return;
7594         }
7595
7596         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7597         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7598         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7599         Store[x][y] = 0;
7600       }
7601     }
7602     else if (element == EL_DC_MAGIC_WALL_FULL)
7603     {
7604       if (IS_FREE(x, y + 1))
7605       {
7606         InitMovingField(x, y, MV_DOWN);
7607         started_moving = TRUE;
7608
7609         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7610         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7611       }
7612       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7613       {
7614         if (!MovDelay[x][y])
7615           MovDelay[x][y] = TILEY / 4 + 1;
7616
7617         if (MovDelay[x][y])
7618         {
7619           MovDelay[x][y]--;
7620           if (MovDelay[x][y])
7621             return;
7622         }
7623
7624         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7625         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7626         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7627         Store[x][y] = 0;
7628       }
7629     }
7630     else if ((CAN_PASS_MAGIC_WALL(element) &&
7631               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7632                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7633              (CAN_PASS_DC_MAGIC_WALL(element) &&
7634               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7635
7636     {
7637       InitMovingField(x, y, MV_DOWN);
7638       started_moving = TRUE;
7639
7640       Feld[x][y] =
7641         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7642          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7643          EL_DC_MAGIC_WALL_FILLING);
7644       Store[x][y] = element;
7645     }
7646     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7647     {
7648       SplashAcid(x, y + 1);
7649
7650       InitMovingField(x, y, MV_DOWN);
7651       started_moving = TRUE;
7652
7653       Store[x][y] = EL_ACID;
7654     }
7655     else if (
7656              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7657               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7658              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7659               CAN_FALL(element) && WasJustFalling[x][y] &&
7660               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7661
7662              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7663               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7664               (Feld[x][y + 1] == EL_BLOCKED)))
7665     {
7666       /* this is needed for a special case not covered by calling "Impact()"
7667          from "ContinueMoving()": if an element moves to a tile directly below
7668          another element which was just falling on that tile (which was empty
7669          in the previous frame), the falling element above would just stop
7670          instead of smashing the element below (in previous version, the above
7671          element was just checked for "moving" instead of "falling", resulting
7672          in incorrect smashes caused by horizontal movement of the above
7673          element; also, the case of the player being the element to smash was
7674          simply not covered here... :-/ ) */
7675
7676       CheckCollision[x][y] = 0;
7677       CheckImpact[x][y] = 0;
7678
7679       Impact(x, y);
7680     }
7681     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7682     {
7683       if (MovDir[x][y] == MV_NONE)
7684       {
7685         InitMovingField(x, y, MV_DOWN);
7686         started_moving = TRUE;
7687       }
7688     }
7689     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7690     {
7691       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7692         MovDir[x][y] = MV_DOWN;
7693
7694       InitMovingField(x, y, MV_DOWN);
7695       started_moving = TRUE;
7696     }
7697     else if (element == EL_AMOEBA_DROP)
7698     {
7699       Feld[x][y] = EL_AMOEBA_GROWING;
7700       Store[x][y] = EL_AMOEBA_WET;
7701     }
7702     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7703               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7704              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7705              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7706     {
7707       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7708                                 (IS_FREE(x - 1, y + 1) ||
7709                                  Feld[x - 1][y + 1] == EL_ACID));
7710       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7711                                 (IS_FREE(x + 1, y + 1) ||
7712                                  Feld[x + 1][y + 1] == EL_ACID));
7713       boolean can_fall_any  = (can_fall_left || can_fall_right);
7714       boolean can_fall_both = (can_fall_left && can_fall_right);
7715       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7716
7717       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7718       {
7719         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7720           can_fall_right = FALSE;
7721         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7722           can_fall_left = FALSE;
7723         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7724           can_fall_right = FALSE;
7725         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7726           can_fall_left = FALSE;
7727
7728         can_fall_any  = (can_fall_left || can_fall_right);
7729         can_fall_both = FALSE;
7730       }
7731
7732       if (can_fall_both)
7733       {
7734         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7735           can_fall_right = FALSE;       /* slip down on left side */
7736         else
7737           can_fall_left = !(can_fall_right = RND(2));
7738
7739         can_fall_both = FALSE;
7740       }
7741
7742       if (can_fall_any)
7743       {
7744         /* if not determined otherwise, prefer left side for slipping down */
7745         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7746         started_moving = TRUE;
7747       }
7748     }
7749     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7750     {
7751       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7752       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7753       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7754       int belt_dir = game.belt_dir[belt_nr];
7755
7756       if ((belt_dir == MV_LEFT  && left_is_free) ||
7757           (belt_dir == MV_RIGHT && right_is_free))
7758       {
7759         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7760
7761         InitMovingField(x, y, belt_dir);
7762         started_moving = TRUE;
7763
7764         Pushed[x][y] = TRUE;
7765         Pushed[nextx][y] = TRUE;
7766
7767         GfxAction[x][y] = ACTION_DEFAULT;
7768       }
7769       else
7770       {
7771         MovDir[x][y] = 0;       /* if element was moving, stop it */
7772       }
7773     }
7774   }
7775
7776   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7777   if (CAN_MOVE(element) && !started_moving)
7778   {
7779     int move_pattern = element_info[element].move_pattern;
7780     int newx, newy;
7781
7782     Moving2Blocked(x, y, &newx, &newy);
7783
7784     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7785       return;
7786
7787     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7788         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7789     {
7790       WasJustMoving[x][y] = 0;
7791       CheckCollision[x][y] = 0;
7792
7793       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7794
7795       if (Feld[x][y] != element)        /* element has changed */
7796         return;
7797     }
7798
7799     if (!MovDelay[x][y])        /* start new movement phase */
7800     {
7801       /* all objects that can change their move direction after each step
7802          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7803
7804       if (element != EL_YAMYAM &&
7805           element != EL_DARK_YAMYAM &&
7806           element != EL_PACMAN &&
7807           !(move_pattern & MV_ANY_DIRECTION) &&
7808           move_pattern != MV_TURNING_LEFT &&
7809           move_pattern != MV_TURNING_RIGHT &&
7810           move_pattern != MV_TURNING_LEFT_RIGHT &&
7811           move_pattern != MV_TURNING_RIGHT_LEFT &&
7812           move_pattern != MV_TURNING_RANDOM)
7813       {
7814         TurnRound(x, y);
7815
7816         if (MovDelay[x][y] && (element == EL_BUG ||
7817                                element == EL_SPACESHIP ||
7818                                element == EL_SP_SNIKSNAK ||
7819                                element == EL_SP_ELECTRON ||
7820                                element == EL_MOLE))
7821           TEST_DrawLevelField(x, y);
7822       }
7823     }
7824
7825     if (MovDelay[x][y])         /* wait some time before next movement */
7826     {
7827       MovDelay[x][y]--;
7828
7829       if (element == EL_ROBOT ||
7830           element == EL_YAMYAM ||
7831           element == EL_DARK_YAMYAM)
7832       {
7833         DrawLevelElementAnimationIfNeeded(x, y, element);
7834         PlayLevelSoundAction(x, y, ACTION_WAITING);
7835       }
7836       else if (element == EL_SP_ELECTRON)
7837         DrawLevelElementAnimationIfNeeded(x, y, element);
7838       else if (element == EL_DRAGON)
7839       {
7840         int i;
7841         int dir = MovDir[x][y];
7842         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7843         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7844         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7845                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7846                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7847                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7848         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7849
7850         GfxAction[x][y] = ACTION_ATTACKING;
7851
7852         if (IS_PLAYER(x, y))
7853           DrawPlayerField(x, y);
7854         else
7855           TEST_DrawLevelField(x, y);
7856
7857         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7858
7859         for (i = 1; i <= 3; i++)
7860         {
7861           int xx = x + i * dx;
7862           int yy = y + i * dy;
7863           int sx = SCREENX(xx);
7864           int sy = SCREENY(yy);
7865           int flame_graphic = graphic + (i - 1);
7866
7867           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7868             break;
7869
7870           if (MovDelay[x][y])
7871           {
7872             int flamed = MovingOrBlocked2Element(xx, yy);
7873
7874             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7875               Bang(xx, yy);
7876             else
7877               RemoveMovingField(xx, yy);
7878
7879             ChangeDelay[xx][yy] = 0;
7880
7881             Feld[xx][yy] = EL_FLAMES;
7882
7883             if (IN_SCR_FIELD(sx, sy))
7884             {
7885               TEST_DrawLevelFieldCrumbled(xx, yy);
7886               DrawGraphic(sx, sy, flame_graphic, frame);
7887             }
7888           }
7889           else
7890           {
7891             if (Feld[xx][yy] == EL_FLAMES)
7892               Feld[xx][yy] = EL_EMPTY;
7893             TEST_DrawLevelField(xx, yy);
7894           }
7895         }
7896       }
7897
7898       if (MovDelay[x][y])       /* element still has to wait some time */
7899       {
7900         PlayLevelSoundAction(x, y, ACTION_WAITING);
7901
7902         return;
7903       }
7904     }
7905
7906     /* now make next step */
7907
7908     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7909
7910     if (DONT_COLLIDE_WITH(element) &&
7911         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7912         !PLAYER_ENEMY_PROTECTED(newx, newy))
7913     {
7914       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7915
7916       return;
7917     }
7918
7919     else if (CAN_MOVE_INTO_ACID(element) &&
7920              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7921              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7922              (MovDir[x][y] == MV_DOWN ||
7923               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7924     {
7925       SplashAcid(newx, newy);
7926       Store[x][y] = EL_ACID;
7927     }
7928     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7929     {
7930       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7931           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7932           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7933           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7934       {
7935         RemoveField(x, y);
7936         TEST_DrawLevelField(x, y);
7937
7938         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7939         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7940           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7941
7942         local_player->friends_still_needed--;
7943         if (!local_player->friends_still_needed &&
7944             !local_player->GameOver && AllPlayersGone)
7945           PlayerWins(local_player);
7946
7947         return;
7948       }
7949       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7950       {
7951         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7952           TEST_DrawLevelField(newx, newy);
7953         else
7954           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7955       }
7956       else if (!IS_FREE(newx, newy))
7957       {
7958         GfxAction[x][y] = ACTION_WAITING;
7959
7960         if (IS_PLAYER(x, y))
7961           DrawPlayerField(x, y);
7962         else
7963           TEST_DrawLevelField(x, y);
7964
7965         return;
7966       }
7967     }
7968     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7969     {
7970       if (IS_FOOD_PIG(Feld[newx][newy]))
7971       {
7972         if (IS_MOVING(newx, newy))
7973           RemoveMovingField(newx, newy);
7974         else
7975         {
7976           Feld[newx][newy] = EL_EMPTY;
7977           TEST_DrawLevelField(newx, newy);
7978         }
7979
7980         PlayLevelSound(x, y, SND_PIG_DIGGING);
7981       }
7982       else if (!IS_FREE(newx, newy))
7983       {
7984         if (IS_PLAYER(x, y))
7985           DrawPlayerField(x, y);
7986         else
7987           TEST_DrawLevelField(x, y);
7988
7989         return;
7990       }
7991     }
7992     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7993     {
7994       if (Store[x][y] != EL_EMPTY)
7995       {
7996         boolean can_clone = FALSE;
7997         int xx, yy;
7998
7999         /* check if element to clone is still there */
8000         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8001         {
8002           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8003           {
8004             can_clone = TRUE;
8005
8006             break;
8007           }
8008         }
8009
8010         /* cannot clone or target field not free anymore -- do not clone */
8011         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8012           Store[x][y] = EL_EMPTY;
8013       }
8014
8015       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8016       {
8017         if (IS_MV_DIAGONAL(MovDir[x][y]))
8018         {
8019           int diagonal_move_dir = MovDir[x][y];
8020           int stored = Store[x][y];
8021           int change_delay = 8;
8022           int graphic;
8023
8024           /* android is moving diagonally */
8025
8026           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8027
8028           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8029           GfxElement[x][y] = EL_EMC_ANDROID;
8030           GfxAction[x][y] = ACTION_SHRINKING;
8031           GfxDir[x][y] = diagonal_move_dir;
8032           ChangeDelay[x][y] = change_delay;
8033
8034           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8035                                    GfxDir[x][y]);
8036
8037           DrawLevelGraphicAnimation(x, y, graphic);
8038           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8039
8040           if (Feld[newx][newy] == EL_ACID)
8041           {
8042             SplashAcid(newx, newy);
8043
8044             return;
8045           }
8046
8047           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8048
8049           Store[newx][newy] = EL_EMC_ANDROID;
8050           GfxElement[newx][newy] = EL_EMC_ANDROID;
8051           GfxAction[newx][newy] = ACTION_GROWING;
8052           GfxDir[newx][newy] = diagonal_move_dir;
8053           ChangeDelay[newx][newy] = change_delay;
8054
8055           graphic = el_act_dir2img(GfxElement[newx][newy],
8056                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8057
8058           DrawLevelGraphicAnimation(newx, newy, graphic);
8059           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8060
8061           return;
8062         }
8063         else
8064         {
8065           Feld[newx][newy] = EL_EMPTY;
8066           TEST_DrawLevelField(newx, newy);
8067
8068           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8069         }
8070       }
8071       else if (!IS_FREE(newx, newy))
8072       {
8073         return;
8074       }
8075     }
8076     else if (IS_CUSTOM_ELEMENT(element) &&
8077              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8078     {
8079       if (!DigFieldByCE(newx, newy, element))
8080         return;
8081
8082       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8083       {
8084         RunnerVisit[x][y] = FrameCounter;
8085         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8086       }
8087     }
8088     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8089     {
8090       if (!IS_FREE(newx, newy))
8091       {
8092         if (IS_PLAYER(x, y))
8093           DrawPlayerField(x, y);
8094         else
8095           TEST_DrawLevelField(x, y);
8096
8097         return;
8098       }
8099       else
8100       {
8101         boolean wanna_flame = !RND(10);
8102         int dx = newx - x, dy = newy - y;
8103         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8104         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8105         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8106                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8107         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8108                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8109
8110         if ((wanna_flame ||
8111              IS_CLASSIC_ENEMY(element1) ||
8112              IS_CLASSIC_ENEMY(element2)) &&
8113             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8114             element1 != EL_FLAMES && element2 != EL_FLAMES)
8115         {
8116           ResetGfxAnimation(x, y);
8117           GfxAction[x][y] = ACTION_ATTACKING;
8118
8119           if (IS_PLAYER(x, y))
8120             DrawPlayerField(x, y);
8121           else
8122             TEST_DrawLevelField(x, y);
8123
8124           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8125
8126           MovDelay[x][y] = 50;
8127
8128           Feld[newx][newy] = EL_FLAMES;
8129           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8130             Feld[newx1][newy1] = EL_FLAMES;
8131           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8132             Feld[newx2][newy2] = EL_FLAMES;
8133
8134           return;
8135         }
8136       }
8137     }
8138     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8139              Feld[newx][newy] == EL_DIAMOND)
8140     {
8141       if (IS_MOVING(newx, newy))
8142         RemoveMovingField(newx, newy);
8143       else
8144       {
8145         Feld[newx][newy] = EL_EMPTY;
8146         TEST_DrawLevelField(newx, newy);
8147       }
8148
8149       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8150     }
8151     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8152              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8153     {
8154       if (AmoebaNr[newx][newy])
8155       {
8156         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8157         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8158             Feld[newx][newy] == EL_BD_AMOEBA)
8159           AmoebaCnt[AmoebaNr[newx][newy]]--;
8160       }
8161
8162       if (IS_MOVING(newx, newy))
8163       {
8164         RemoveMovingField(newx, newy);
8165       }
8166       else
8167       {
8168         Feld[newx][newy] = EL_EMPTY;
8169         TEST_DrawLevelField(newx, newy);
8170       }
8171
8172       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8173     }
8174     else if ((element == EL_PACMAN || element == EL_MOLE)
8175              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8176     {
8177       if (AmoebaNr[newx][newy])
8178       {
8179         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8180         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8181             Feld[newx][newy] == EL_BD_AMOEBA)
8182           AmoebaCnt[AmoebaNr[newx][newy]]--;
8183       }
8184
8185       if (element == EL_MOLE)
8186       {
8187         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8188         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8189
8190         ResetGfxAnimation(x, y);
8191         GfxAction[x][y] = ACTION_DIGGING;
8192         TEST_DrawLevelField(x, y);
8193
8194         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8195
8196         return;                         /* wait for shrinking amoeba */
8197       }
8198       else      /* element == EL_PACMAN */
8199       {
8200         Feld[newx][newy] = EL_EMPTY;
8201         TEST_DrawLevelField(newx, newy);
8202         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8203       }
8204     }
8205     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8206              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8207               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8208     {
8209       /* wait for shrinking amoeba to completely disappear */
8210       return;
8211     }
8212     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8213     {
8214       /* object was running against a wall */
8215
8216       TurnRound(x, y);
8217
8218       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8219         DrawLevelElementAnimation(x, y, element);
8220
8221       if (DONT_TOUCH(element))
8222         TestIfBadThingTouchesPlayer(x, y);
8223
8224       return;
8225     }
8226
8227     InitMovingField(x, y, MovDir[x][y]);
8228
8229     PlayLevelSoundAction(x, y, ACTION_MOVING);
8230   }
8231
8232   if (MovDir[x][y])
8233     ContinueMoving(x, y);
8234 }
8235
8236 void ContinueMoving(int x, int y)
8237 {
8238   int element = Feld[x][y];
8239   struct ElementInfo *ei = &element_info[element];
8240   int direction = MovDir[x][y];
8241   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8242   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8243   int newx = x + dx, newy = y + dy;
8244   int stored = Store[x][y];
8245   int stored_new = Store[newx][newy];
8246   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8247   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8248   boolean last_line = (newy == lev_fieldy - 1);
8249
8250   MovPos[x][y] += getElementMoveStepsize(x, y);
8251
8252   if (pushed_by_player) /* special case: moving object pushed by player */
8253     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8254
8255   if (ABS(MovPos[x][y]) < TILEX)
8256   {
8257     TEST_DrawLevelField(x, y);
8258
8259     return;     /* element is still moving */
8260   }
8261
8262   /* element reached destination field */
8263
8264   Feld[x][y] = EL_EMPTY;
8265   Feld[newx][newy] = element;
8266   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8267
8268   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8269   {
8270     element = Feld[newx][newy] = EL_ACID;
8271   }
8272   else if (element == EL_MOLE)
8273   {
8274     Feld[x][y] = EL_SAND;
8275
8276     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8277   }
8278   else if (element == EL_QUICKSAND_FILLING)
8279   {
8280     element = Feld[newx][newy] = get_next_element(element);
8281     Store[newx][newy] = Store[x][y];
8282   }
8283   else if (element == EL_QUICKSAND_EMPTYING)
8284   {
8285     Feld[x][y] = get_next_element(element);
8286     element = Feld[newx][newy] = Store[x][y];
8287   }
8288   else if (element == EL_QUICKSAND_FAST_FILLING)
8289   {
8290     element = Feld[newx][newy] = get_next_element(element);
8291     Store[newx][newy] = Store[x][y];
8292   }
8293   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8294   {
8295     Feld[x][y] = get_next_element(element);
8296     element = Feld[newx][newy] = Store[x][y];
8297   }
8298   else if (element == EL_MAGIC_WALL_FILLING)
8299   {
8300     element = Feld[newx][newy] = get_next_element(element);
8301     if (!game.magic_wall_active)
8302       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8303     Store[newx][newy] = Store[x][y];
8304   }
8305   else if (element == EL_MAGIC_WALL_EMPTYING)
8306   {
8307     Feld[x][y] = get_next_element(element);
8308     if (!game.magic_wall_active)
8309       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8310     element = Feld[newx][newy] = Store[x][y];
8311
8312     InitField(newx, newy, FALSE);
8313   }
8314   else if (element == EL_BD_MAGIC_WALL_FILLING)
8315   {
8316     element = Feld[newx][newy] = get_next_element(element);
8317     if (!game.magic_wall_active)
8318       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8319     Store[newx][newy] = Store[x][y];
8320   }
8321   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8322   {
8323     Feld[x][y] = get_next_element(element);
8324     if (!game.magic_wall_active)
8325       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8326     element = Feld[newx][newy] = Store[x][y];
8327
8328     InitField(newx, newy, FALSE);
8329   }
8330   else if (element == EL_DC_MAGIC_WALL_FILLING)
8331   {
8332     element = Feld[newx][newy] = get_next_element(element);
8333     if (!game.magic_wall_active)
8334       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8335     Store[newx][newy] = Store[x][y];
8336   }
8337   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8338   {
8339     Feld[x][y] = get_next_element(element);
8340     if (!game.magic_wall_active)
8341       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8342     element = Feld[newx][newy] = Store[x][y];
8343
8344     InitField(newx, newy, FALSE);
8345   }
8346   else if (element == EL_AMOEBA_DROPPING)
8347   {
8348     Feld[x][y] = get_next_element(element);
8349     element = Feld[newx][newy] = Store[x][y];
8350   }
8351   else if (element == EL_SOKOBAN_OBJECT)
8352   {
8353     if (Back[x][y])
8354       Feld[x][y] = Back[x][y];
8355
8356     if (Back[newx][newy])
8357       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8358
8359     Back[x][y] = Back[newx][newy] = 0;
8360   }
8361
8362   Store[x][y] = EL_EMPTY;
8363   MovPos[x][y] = 0;
8364   MovDir[x][y] = 0;
8365   MovDelay[x][y] = 0;
8366
8367   MovDelay[newx][newy] = 0;
8368
8369   if (CAN_CHANGE_OR_HAS_ACTION(element))
8370   {
8371     /* copy element change control values to new field */
8372     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8373     ChangePage[newx][newy]  = ChangePage[x][y];
8374     ChangeCount[newx][newy] = ChangeCount[x][y];
8375     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8376   }
8377
8378   CustomValue[newx][newy] = CustomValue[x][y];
8379
8380   ChangeDelay[x][y] = 0;
8381   ChangePage[x][y] = -1;
8382   ChangeCount[x][y] = 0;
8383   ChangeEvent[x][y] = -1;
8384
8385   CustomValue[x][y] = 0;
8386
8387   /* copy animation control values to new field */
8388   GfxFrame[newx][newy]  = GfxFrame[x][y];
8389   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8390   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8391   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8392
8393   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8394
8395   /* some elements can leave other elements behind after moving */
8396   if (ei->move_leave_element != EL_EMPTY &&
8397       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8398       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8399   {
8400     int move_leave_element = ei->move_leave_element;
8401
8402     /* this makes it possible to leave the removed element again */
8403     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8404       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8405
8406     Feld[x][y] = move_leave_element;
8407
8408     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8409       MovDir[x][y] = direction;
8410
8411     InitField(x, y, FALSE);
8412
8413     if (GFX_CRUMBLED(Feld[x][y]))
8414       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8415
8416     if (ELEM_IS_PLAYER(move_leave_element))
8417       RelocatePlayer(x, y, move_leave_element);
8418   }
8419
8420   /* do this after checking for left-behind element */
8421   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8422
8423   if (!CAN_MOVE(element) ||
8424       (CAN_FALL(element) && direction == MV_DOWN &&
8425        (element == EL_SPRING ||
8426         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8427         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8428     GfxDir[x][y] = MovDir[newx][newy] = 0;
8429
8430   TEST_DrawLevelField(x, y);
8431   TEST_DrawLevelField(newx, newy);
8432
8433   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8434
8435   /* prevent pushed element from moving on in pushed direction */
8436   if (pushed_by_player && CAN_MOVE(element) &&
8437       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8438       !(element_info[element].move_pattern & direction))
8439     TurnRound(newx, newy);
8440
8441   /* prevent elements on conveyor belt from moving on in last direction */
8442   if (pushed_by_conveyor && CAN_FALL(element) &&
8443       direction & MV_HORIZONTAL)
8444     MovDir[newx][newy] = 0;
8445
8446   if (!pushed_by_player)
8447   {
8448     int nextx = newx + dx, nexty = newy + dy;
8449     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8450
8451     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8452
8453     if (CAN_FALL(element) && direction == MV_DOWN)
8454       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8455
8456     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8457       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8458
8459     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8460       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8461   }
8462
8463   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8464   {
8465     TestIfBadThingTouchesPlayer(newx, newy);
8466     TestIfBadThingTouchesFriend(newx, newy);
8467
8468     if (!IS_CUSTOM_ELEMENT(element))
8469       TestIfBadThingTouchesOtherBadThing(newx, newy);
8470   }
8471   else if (element == EL_PENGUIN)
8472     TestIfFriendTouchesBadThing(newx, newy);
8473
8474   if (DONT_GET_HIT_BY(element))
8475   {
8476     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8477   }
8478
8479   /* give the player one last chance (one more frame) to move away */
8480   if (CAN_FALL(element) && direction == MV_DOWN &&
8481       (last_line || (!IS_FREE(x, newy + 1) &&
8482                      (!IS_PLAYER(x, newy + 1) ||
8483                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8484     Impact(x, newy);
8485
8486   if (pushed_by_player && !game.use_change_when_pushing_bug)
8487   {
8488     int push_side = MV_DIR_OPPOSITE(direction);
8489     struct PlayerInfo *player = PLAYERINFO(x, y);
8490
8491     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8492                                player->index_bit, push_side);
8493     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8494                                         player->index_bit, push_side);
8495   }
8496
8497   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8498     MovDelay[newx][newy] = 1;
8499
8500   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8501
8502   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8503   TestIfElementHitsCustomElement(newx, newy, direction);
8504   TestIfPlayerTouchesCustomElement(newx, newy);
8505   TestIfElementTouchesCustomElement(newx, newy);
8506
8507   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8508       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8509     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8510                              MV_DIR_OPPOSITE(direction));
8511 }
8512
8513 int AmoebeNachbarNr(int ax, int ay)
8514 {
8515   int i;
8516   int element = Feld[ax][ay];
8517   int group_nr = 0;
8518   static int xy[4][2] =
8519   {
8520     { 0, -1 },
8521     { -1, 0 },
8522     { +1, 0 },
8523     { 0, +1 }
8524   };
8525
8526   for (i = 0; i < NUM_DIRECTIONS; i++)
8527   {
8528     int x = ax + xy[i][0];
8529     int y = ay + xy[i][1];
8530
8531     if (!IN_LEV_FIELD(x, y))
8532       continue;
8533
8534     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8535       group_nr = AmoebaNr[x][y];
8536   }
8537
8538   return group_nr;
8539 }
8540
8541 void AmoebenVereinigen(int ax, int ay)
8542 {
8543   int i, x, y, xx, yy;
8544   int new_group_nr = AmoebaNr[ax][ay];
8545   static int xy[4][2] =
8546   {
8547     { 0, -1 },
8548     { -1, 0 },
8549     { +1, 0 },
8550     { 0, +1 }
8551   };
8552
8553   if (new_group_nr == 0)
8554     return;
8555
8556   for (i = 0; i < NUM_DIRECTIONS; i++)
8557   {
8558     x = ax + xy[i][0];
8559     y = ay + xy[i][1];
8560
8561     if (!IN_LEV_FIELD(x, y))
8562       continue;
8563
8564     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8565          Feld[x][y] == EL_BD_AMOEBA ||
8566          Feld[x][y] == EL_AMOEBA_DEAD) &&
8567         AmoebaNr[x][y] != new_group_nr)
8568     {
8569       int old_group_nr = AmoebaNr[x][y];
8570
8571       if (old_group_nr == 0)
8572         return;
8573
8574       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8575       AmoebaCnt[old_group_nr] = 0;
8576       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8577       AmoebaCnt2[old_group_nr] = 0;
8578
8579       SCAN_PLAYFIELD(xx, yy)
8580       {
8581         if (AmoebaNr[xx][yy] == old_group_nr)
8582           AmoebaNr[xx][yy] = new_group_nr;
8583       }
8584     }
8585   }
8586 }
8587
8588 void AmoebeUmwandeln(int ax, int ay)
8589 {
8590   int i, x, y;
8591
8592   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8593   {
8594     int group_nr = AmoebaNr[ax][ay];
8595
8596 #ifdef DEBUG
8597     if (group_nr == 0)
8598     {
8599       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8600       printf("AmoebeUmwandeln(): This should never happen!\n");
8601       return;
8602     }
8603 #endif
8604
8605     SCAN_PLAYFIELD(x, y)
8606     {
8607       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8608       {
8609         AmoebaNr[x][y] = 0;
8610         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8611       }
8612     }
8613
8614     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8615                             SND_AMOEBA_TURNING_TO_GEM :
8616                             SND_AMOEBA_TURNING_TO_ROCK));
8617     Bang(ax, ay);
8618   }
8619   else
8620   {
8621     static int xy[4][2] =
8622     {
8623       { 0, -1 },
8624       { -1, 0 },
8625       { +1, 0 },
8626       { 0, +1 }
8627     };
8628
8629     for (i = 0; i < NUM_DIRECTIONS; i++)
8630     {
8631       x = ax + xy[i][0];
8632       y = ay + xy[i][1];
8633
8634       if (!IN_LEV_FIELD(x, y))
8635         continue;
8636
8637       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8638       {
8639         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8640                               SND_AMOEBA_TURNING_TO_GEM :
8641                               SND_AMOEBA_TURNING_TO_ROCK));
8642         Bang(x, y);
8643       }
8644     }
8645   }
8646 }
8647
8648 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8649 {
8650   int x, y;
8651   int group_nr = AmoebaNr[ax][ay];
8652   boolean done = FALSE;
8653
8654 #ifdef DEBUG
8655   if (group_nr == 0)
8656   {
8657     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8658     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8659     return;
8660   }
8661 #endif
8662
8663   SCAN_PLAYFIELD(x, y)
8664   {
8665     if (AmoebaNr[x][y] == group_nr &&
8666         (Feld[x][y] == EL_AMOEBA_DEAD ||
8667          Feld[x][y] == EL_BD_AMOEBA ||
8668          Feld[x][y] == EL_AMOEBA_GROWING))
8669     {
8670       AmoebaNr[x][y] = 0;
8671       Feld[x][y] = new_element;
8672       InitField(x, y, FALSE);
8673       TEST_DrawLevelField(x, y);
8674       done = TRUE;
8675     }
8676   }
8677
8678   if (done)
8679     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8680                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8681                             SND_BD_AMOEBA_TURNING_TO_GEM));
8682 }
8683
8684 void AmoebeWaechst(int x, int y)
8685 {
8686   static unsigned int sound_delay = 0;
8687   static unsigned int sound_delay_value = 0;
8688
8689   if (!MovDelay[x][y])          /* start new growing cycle */
8690   {
8691     MovDelay[x][y] = 7;
8692
8693     if (DelayReached(&sound_delay, sound_delay_value))
8694     {
8695       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8696       sound_delay_value = 30;
8697     }
8698   }
8699
8700   if (MovDelay[x][y])           /* wait some time before growing bigger */
8701   {
8702     MovDelay[x][y]--;
8703     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8704     {
8705       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8706                                            6 - MovDelay[x][y]);
8707
8708       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8709     }
8710
8711     if (!MovDelay[x][y])
8712     {
8713       Feld[x][y] = Store[x][y];
8714       Store[x][y] = 0;
8715       TEST_DrawLevelField(x, y);
8716     }
8717   }
8718 }
8719
8720 void AmoebaDisappearing(int x, int y)
8721 {
8722   static unsigned int sound_delay = 0;
8723   static unsigned int sound_delay_value = 0;
8724
8725   if (!MovDelay[x][y])          /* start new shrinking cycle */
8726   {
8727     MovDelay[x][y] = 7;
8728
8729     if (DelayReached(&sound_delay, sound_delay_value))
8730       sound_delay_value = 30;
8731   }
8732
8733   if (MovDelay[x][y])           /* wait some time before shrinking */
8734   {
8735     MovDelay[x][y]--;
8736     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8737     {
8738       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8739                                            6 - MovDelay[x][y]);
8740
8741       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8742     }
8743
8744     if (!MovDelay[x][y])
8745     {
8746       Feld[x][y] = EL_EMPTY;
8747       TEST_DrawLevelField(x, y);
8748
8749       /* don't let mole enter this field in this cycle;
8750          (give priority to objects falling to this field from above) */
8751       Stop[x][y] = TRUE;
8752     }
8753   }
8754 }
8755
8756 void AmoebeAbleger(int ax, int ay)
8757 {
8758   int i;
8759   int element = Feld[ax][ay];
8760   int graphic = el2img(element);
8761   int newax = ax, neway = ay;
8762   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8763   static int xy[4][2] =
8764   {
8765     { 0, -1 },
8766     { -1, 0 },
8767     { +1, 0 },
8768     { 0, +1 }
8769   };
8770
8771   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8772   {
8773     Feld[ax][ay] = EL_AMOEBA_DEAD;
8774     TEST_DrawLevelField(ax, ay);
8775     return;
8776   }
8777
8778   if (IS_ANIMATED(graphic))
8779     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8780
8781   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8782     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8783
8784   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8785   {
8786     MovDelay[ax][ay]--;
8787     if (MovDelay[ax][ay])
8788       return;
8789   }
8790
8791   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8792   {
8793     int start = RND(4);
8794     int x = ax + xy[start][0];
8795     int y = ay + xy[start][1];
8796
8797     if (!IN_LEV_FIELD(x, y))
8798       return;
8799
8800     if (IS_FREE(x, y) ||
8801         CAN_GROW_INTO(Feld[x][y]) ||
8802         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8803         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8804     {
8805       newax = x;
8806       neway = y;
8807     }
8808
8809     if (newax == ax && neway == ay)
8810       return;
8811   }
8812   else                          /* normal or "filled" (BD style) amoeba */
8813   {
8814     int start = RND(4);
8815     boolean waiting_for_player = FALSE;
8816
8817     for (i = 0; i < NUM_DIRECTIONS; i++)
8818     {
8819       int j = (start + i) % 4;
8820       int x = ax + xy[j][0];
8821       int y = ay + xy[j][1];
8822
8823       if (!IN_LEV_FIELD(x, y))
8824         continue;
8825
8826       if (IS_FREE(x, y) ||
8827           CAN_GROW_INTO(Feld[x][y]) ||
8828           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8829           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8830       {
8831         newax = x;
8832         neway = y;
8833         break;
8834       }
8835       else if (IS_PLAYER(x, y))
8836         waiting_for_player = TRUE;
8837     }
8838
8839     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8840     {
8841       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8842       {
8843         Feld[ax][ay] = EL_AMOEBA_DEAD;
8844         TEST_DrawLevelField(ax, ay);
8845         AmoebaCnt[AmoebaNr[ax][ay]]--;
8846
8847         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8848         {
8849           if (element == EL_AMOEBA_FULL)
8850             AmoebeUmwandeln(ax, ay);
8851           else if (element == EL_BD_AMOEBA)
8852             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8853         }
8854       }
8855       return;
8856     }
8857     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8858     {
8859       /* amoeba gets larger by growing in some direction */
8860
8861       int new_group_nr = AmoebaNr[ax][ay];
8862
8863 #ifdef DEBUG
8864   if (new_group_nr == 0)
8865   {
8866     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8867     printf("AmoebeAbleger(): This should never happen!\n");
8868     return;
8869   }
8870 #endif
8871
8872       AmoebaNr[newax][neway] = new_group_nr;
8873       AmoebaCnt[new_group_nr]++;
8874       AmoebaCnt2[new_group_nr]++;
8875
8876       /* if amoeba touches other amoeba(s) after growing, unify them */
8877       AmoebenVereinigen(newax, neway);
8878
8879       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8880       {
8881         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8882         return;
8883       }
8884     }
8885   }
8886
8887   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8888       (neway == lev_fieldy - 1 && newax != ax))
8889   {
8890     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8891     Store[newax][neway] = element;
8892   }
8893   else if (neway == ay || element == EL_EMC_DRIPPER)
8894   {
8895     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8896
8897     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8898   }
8899   else
8900   {
8901     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8902     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8903     Store[ax][ay] = EL_AMOEBA_DROP;
8904     ContinueMoving(ax, ay);
8905     return;
8906   }
8907
8908   TEST_DrawLevelField(newax, neway);
8909 }
8910
8911 void Life(int ax, int ay)
8912 {
8913   int x1, y1, x2, y2;
8914   int life_time = 40;
8915   int element = Feld[ax][ay];
8916   int graphic = el2img(element);
8917   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8918                          level.biomaze);
8919   boolean changed = FALSE;
8920
8921   if (IS_ANIMATED(graphic))
8922     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8923
8924   if (Stop[ax][ay])
8925     return;
8926
8927   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8928     MovDelay[ax][ay] = life_time;
8929
8930   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8931   {
8932     MovDelay[ax][ay]--;
8933     if (MovDelay[ax][ay])
8934       return;
8935   }
8936
8937   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8938   {
8939     int xx = ax+x1, yy = ay+y1;
8940     int nachbarn = 0;
8941
8942     if (!IN_LEV_FIELD(xx, yy))
8943       continue;
8944
8945     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8946     {
8947       int x = xx+x2, y = yy+y2;
8948
8949       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8950         continue;
8951
8952       if (((Feld[x][y] == element ||
8953             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8954            !Stop[x][y]) ||
8955           (IS_FREE(x, y) && Stop[x][y]))
8956         nachbarn++;
8957     }
8958
8959     if (xx == ax && yy == ay)           /* field in the middle */
8960     {
8961       if (nachbarn < life_parameter[0] ||
8962           nachbarn > life_parameter[1])
8963       {
8964         Feld[xx][yy] = EL_EMPTY;
8965         if (!Stop[xx][yy])
8966           TEST_DrawLevelField(xx, yy);
8967         Stop[xx][yy] = TRUE;
8968         changed = TRUE;
8969       }
8970     }
8971     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8972     {                                   /* free border field */
8973       if (nachbarn >= life_parameter[2] &&
8974           nachbarn <= life_parameter[3])
8975       {
8976         Feld[xx][yy] = element;
8977         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8978         if (!Stop[xx][yy])
8979           TEST_DrawLevelField(xx, yy);
8980         Stop[xx][yy] = TRUE;
8981         changed = TRUE;
8982       }
8983     }
8984   }
8985
8986   if (changed)
8987     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8988                    SND_GAME_OF_LIFE_GROWING);
8989 }
8990
8991 static void InitRobotWheel(int x, int y)
8992 {
8993   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8994 }
8995
8996 static void RunRobotWheel(int x, int y)
8997 {
8998   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8999 }
9000
9001 static void StopRobotWheel(int x, int y)
9002 {
9003   if (ZX == x && ZY == y)
9004   {
9005     ZX = ZY = -1;
9006
9007     game.robot_wheel_active = FALSE;
9008   }
9009 }
9010
9011 static void InitTimegateWheel(int x, int y)
9012 {
9013   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9014 }
9015
9016 static void RunTimegateWheel(int x, int y)
9017 {
9018   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9019 }
9020
9021 static void InitMagicBallDelay(int x, int y)
9022 {
9023   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9024 }
9025
9026 static void ActivateMagicBall(int bx, int by)
9027 {
9028   int x, y;
9029
9030   if (level.ball_random)
9031   {
9032     int pos_border = RND(8);    /* select one of the eight border elements */
9033     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9034     int xx = pos_content % 3;
9035     int yy = pos_content / 3;
9036
9037     x = bx - 1 + xx;
9038     y = by - 1 + yy;
9039
9040     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9041       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9042   }
9043   else
9044   {
9045     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9046     {
9047       int xx = x - bx + 1;
9048       int yy = y - by + 1;
9049
9050       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9051         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9052     }
9053   }
9054
9055   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9056 }
9057
9058 void CheckExit(int x, int y)
9059 {
9060   if (local_player->gems_still_needed > 0 ||
9061       local_player->sokobanfields_still_needed > 0 ||
9062       local_player->lights_still_needed > 0)
9063   {
9064     int element = Feld[x][y];
9065     int graphic = el2img(element);
9066
9067     if (IS_ANIMATED(graphic))
9068       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9069
9070     return;
9071   }
9072
9073   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9074     return;
9075
9076   Feld[x][y] = EL_EXIT_OPENING;
9077
9078   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9079 }
9080
9081 void CheckExitEM(int x, int y)
9082 {
9083   if (local_player->gems_still_needed > 0 ||
9084       local_player->sokobanfields_still_needed > 0 ||
9085       local_player->lights_still_needed > 0)
9086   {
9087     int element = Feld[x][y];
9088     int graphic = el2img(element);
9089
9090     if (IS_ANIMATED(graphic))
9091       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9092
9093     return;
9094   }
9095
9096   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9097     return;
9098
9099   Feld[x][y] = EL_EM_EXIT_OPENING;
9100
9101   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9102 }
9103
9104 void CheckExitSteel(int x, int y)
9105 {
9106   if (local_player->gems_still_needed > 0 ||
9107       local_player->sokobanfields_still_needed > 0 ||
9108       local_player->lights_still_needed > 0)
9109   {
9110     int element = Feld[x][y];
9111     int graphic = el2img(element);
9112
9113     if (IS_ANIMATED(graphic))
9114       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9115
9116     return;
9117   }
9118
9119   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9120     return;
9121
9122   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9123
9124   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9125 }
9126
9127 void CheckExitSteelEM(int x, int y)
9128 {
9129   if (local_player->gems_still_needed > 0 ||
9130       local_player->sokobanfields_still_needed > 0 ||
9131       local_player->lights_still_needed > 0)
9132   {
9133     int element = Feld[x][y];
9134     int graphic = el2img(element);
9135
9136     if (IS_ANIMATED(graphic))
9137       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9138
9139     return;
9140   }
9141
9142   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9143     return;
9144
9145   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9146
9147   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9148 }
9149
9150 void CheckExitSP(int x, int y)
9151 {
9152   if (local_player->gems_still_needed > 0)
9153   {
9154     int element = Feld[x][y];
9155     int graphic = el2img(element);
9156
9157     if (IS_ANIMATED(graphic))
9158       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9159
9160     return;
9161   }
9162
9163   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9164     return;
9165
9166   Feld[x][y] = EL_SP_EXIT_OPENING;
9167
9168   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9169 }
9170
9171 static void CloseAllOpenTimegates()
9172 {
9173   int x, y;
9174
9175   SCAN_PLAYFIELD(x, y)
9176   {
9177     int element = Feld[x][y];
9178
9179     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9180     {
9181       Feld[x][y] = EL_TIMEGATE_CLOSING;
9182
9183       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9184     }
9185   }
9186 }
9187
9188 void DrawTwinkleOnField(int x, int y)
9189 {
9190   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9191     return;
9192
9193   if (Feld[x][y] == EL_BD_DIAMOND)
9194     return;
9195
9196   if (MovDelay[x][y] == 0)      /* next animation frame */
9197     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9198
9199   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9200   {
9201     MovDelay[x][y]--;
9202
9203     DrawLevelElementAnimation(x, y, Feld[x][y]);
9204
9205     if (MovDelay[x][y] != 0)
9206     {
9207       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9208                                            10 - MovDelay[x][y]);
9209
9210       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9211     }
9212   }
9213 }
9214
9215 void MauerWaechst(int x, int y)
9216 {
9217   int delay = 6;
9218
9219   if (!MovDelay[x][y])          /* next animation frame */
9220     MovDelay[x][y] = 3 * delay;
9221
9222   if (MovDelay[x][y])           /* wait some time before next frame */
9223   {
9224     MovDelay[x][y]--;
9225
9226     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9227     {
9228       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9229       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9230
9231       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9232     }
9233
9234     if (!MovDelay[x][y])
9235     {
9236       if (MovDir[x][y] == MV_LEFT)
9237       {
9238         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9239           TEST_DrawLevelField(x - 1, y);
9240       }
9241       else if (MovDir[x][y] == MV_RIGHT)
9242       {
9243         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9244           TEST_DrawLevelField(x + 1, y);
9245       }
9246       else if (MovDir[x][y] == MV_UP)
9247       {
9248         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9249           TEST_DrawLevelField(x, y - 1);
9250       }
9251       else
9252       {
9253         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9254           TEST_DrawLevelField(x, y + 1);
9255       }
9256
9257       Feld[x][y] = Store[x][y];
9258       Store[x][y] = 0;
9259       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9260       TEST_DrawLevelField(x, y);
9261     }
9262   }
9263 }
9264
9265 void MauerAbleger(int ax, int ay)
9266 {
9267   int element = Feld[ax][ay];
9268   int graphic = el2img(element);
9269   boolean oben_frei = FALSE, unten_frei = FALSE;
9270   boolean links_frei = FALSE, rechts_frei = FALSE;
9271   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9272   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9273   boolean new_wall = FALSE;
9274
9275   if (IS_ANIMATED(graphic))
9276     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9277
9278   if (!MovDelay[ax][ay])        /* start building new wall */
9279     MovDelay[ax][ay] = 6;
9280
9281   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9282   {
9283     MovDelay[ax][ay]--;
9284     if (MovDelay[ax][ay])
9285       return;
9286   }
9287
9288   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9289     oben_frei = TRUE;
9290   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9291     unten_frei = TRUE;
9292   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9293     links_frei = TRUE;
9294   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9295     rechts_frei = TRUE;
9296
9297   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9298       element == EL_EXPANDABLE_WALL_ANY)
9299   {
9300     if (oben_frei)
9301     {
9302       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9303       Store[ax][ay-1] = element;
9304       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9305       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9306         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9307                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9308       new_wall = TRUE;
9309     }
9310     if (unten_frei)
9311     {
9312       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9313       Store[ax][ay+1] = element;
9314       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9315       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9316         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9317                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9318       new_wall = TRUE;
9319     }
9320   }
9321
9322   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9323       element == EL_EXPANDABLE_WALL_ANY ||
9324       element == EL_EXPANDABLE_WALL ||
9325       element == EL_BD_EXPANDABLE_WALL)
9326   {
9327     if (links_frei)
9328     {
9329       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9330       Store[ax-1][ay] = element;
9331       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9332       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9333         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9334                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9335       new_wall = TRUE;
9336     }
9337
9338     if (rechts_frei)
9339     {
9340       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9341       Store[ax+1][ay] = element;
9342       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9343       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9344         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9345                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9346       new_wall = TRUE;
9347     }
9348   }
9349
9350   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9351     TEST_DrawLevelField(ax, ay);
9352
9353   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9354     oben_massiv = TRUE;
9355   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9356     unten_massiv = TRUE;
9357   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9358     links_massiv = TRUE;
9359   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9360     rechts_massiv = TRUE;
9361
9362   if (((oben_massiv && unten_massiv) ||
9363        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9364        element == EL_EXPANDABLE_WALL) &&
9365       ((links_massiv && rechts_massiv) ||
9366        element == EL_EXPANDABLE_WALL_VERTICAL))
9367     Feld[ax][ay] = EL_WALL;
9368
9369   if (new_wall)
9370     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9371 }
9372
9373 void MauerAblegerStahl(int ax, int ay)
9374 {
9375   int element = Feld[ax][ay];
9376   int graphic = el2img(element);
9377   boolean oben_frei = FALSE, unten_frei = FALSE;
9378   boolean links_frei = FALSE, rechts_frei = FALSE;
9379   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9380   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9381   boolean new_wall = FALSE;
9382
9383   if (IS_ANIMATED(graphic))
9384     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9385
9386   if (!MovDelay[ax][ay])        /* start building new wall */
9387     MovDelay[ax][ay] = 6;
9388
9389   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9390   {
9391     MovDelay[ax][ay]--;
9392     if (MovDelay[ax][ay])
9393       return;
9394   }
9395
9396   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9397     oben_frei = TRUE;
9398   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9399     unten_frei = TRUE;
9400   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9401     links_frei = TRUE;
9402   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9403     rechts_frei = TRUE;
9404
9405   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9406       element == EL_EXPANDABLE_STEELWALL_ANY)
9407   {
9408     if (oben_frei)
9409     {
9410       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9411       Store[ax][ay-1] = element;
9412       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9413       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9414         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9415                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9416       new_wall = TRUE;
9417     }
9418     if (unten_frei)
9419     {
9420       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9421       Store[ax][ay+1] = element;
9422       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9423       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9424         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9425                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9426       new_wall = TRUE;
9427     }
9428   }
9429
9430   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9431       element == EL_EXPANDABLE_STEELWALL_ANY)
9432   {
9433     if (links_frei)
9434     {
9435       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9436       Store[ax-1][ay] = element;
9437       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9438       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9439         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9440                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9441       new_wall = TRUE;
9442     }
9443
9444     if (rechts_frei)
9445     {
9446       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9447       Store[ax+1][ay] = element;
9448       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9449       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9450         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9451                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9452       new_wall = TRUE;
9453     }
9454   }
9455
9456   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9457     oben_massiv = TRUE;
9458   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9459     unten_massiv = TRUE;
9460   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9461     links_massiv = TRUE;
9462   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9463     rechts_massiv = TRUE;
9464
9465   if (((oben_massiv && unten_massiv) ||
9466        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9467       ((links_massiv && rechts_massiv) ||
9468        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9469     Feld[ax][ay] = EL_STEELWALL;
9470
9471   if (new_wall)
9472     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9473 }
9474
9475 void CheckForDragon(int x, int y)
9476 {
9477   int i, j;
9478   boolean dragon_found = FALSE;
9479   static int xy[4][2] =
9480   {
9481     { 0, -1 },
9482     { -1, 0 },
9483     { +1, 0 },
9484     { 0, +1 }
9485   };
9486
9487   for (i = 0; i < NUM_DIRECTIONS; i++)
9488   {
9489     for (j = 0; j < 4; j++)
9490     {
9491       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9492
9493       if (IN_LEV_FIELD(xx, yy) &&
9494           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9495       {
9496         if (Feld[xx][yy] == EL_DRAGON)
9497           dragon_found = TRUE;
9498       }
9499       else
9500         break;
9501     }
9502   }
9503
9504   if (!dragon_found)
9505   {
9506     for (i = 0; i < NUM_DIRECTIONS; i++)
9507     {
9508       for (j = 0; j < 3; j++)
9509       {
9510         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9511   
9512         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9513         {
9514           Feld[xx][yy] = EL_EMPTY;
9515           TEST_DrawLevelField(xx, yy);
9516         }
9517         else
9518           break;
9519       }
9520     }
9521   }
9522 }
9523
9524 static void InitBuggyBase(int x, int y)
9525 {
9526   int element = Feld[x][y];
9527   int activating_delay = FRAMES_PER_SECOND / 4;
9528
9529   ChangeDelay[x][y] =
9530     (element == EL_SP_BUGGY_BASE ?
9531      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9532      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9533      activating_delay :
9534      element == EL_SP_BUGGY_BASE_ACTIVE ?
9535      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9536 }
9537
9538 static void WarnBuggyBase(int x, int y)
9539 {
9540   int i;
9541   static int xy[4][2] =
9542   {
9543     { 0, -1 },
9544     { -1, 0 },
9545     { +1, 0 },
9546     { 0, +1 }
9547   };
9548
9549   for (i = 0; i < NUM_DIRECTIONS; i++)
9550   {
9551     int xx = x + xy[i][0];
9552     int yy = y + xy[i][1];
9553
9554     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9555     {
9556       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9557
9558       break;
9559     }
9560   }
9561 }
9562
9563 static void InitTrap(int x, int y)
9564 {
9565   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9566 }
9567
9568 static void ActivateTrap(int x, int y)
9569 {
9570   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9571 }
9572
9573 static void ChangeActiveTrap(int x, int y)
9574 {
9575   int graphic = IMG_TRAP_ACTIVE;
9576
9577   /* if new animation frame was drawn, correct crumbled sand border */
9578   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9579     TEST_DrawLevelFieldCrumbled(x, y);
9580 }
9581
9582 static int getSpecialActionElement(int element, int number, int base_element)
9583 {
9584   return (element != EL_EMPTY ? element :
9585           number != -1 ? base_element + number - 1 :
9586           EL_EMPTY);
9587 }
9588
9589 static int getModifiedActionNumber(int value_old, int operator, int operand,
9590                                    int value_min, int value_max)
9591 {
9592   int value_new = (operator == CA_MODE_SET      ? operand :
9593                    operator == CA_MODE_ADD      ? value_old + operand :
9594                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9595                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9596                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9597                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9598                    value_old);
9599
9600   return (value_new < value_min ? value_min :
9601           value_new > value_max ? value_max :
9602           value_new);
9603 }
9604
9605 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9606 {
9607   struct ElementInfo *ei = &element_info[element];
9608   struct ElementChangeInfo *change = &ei->change_page[page];
9609   int target_element = change->target_element;
9610   int action_type = change->action_type;
9611   int action_mode = change->action_mode;
9612   int action_arg = change->action_arg;
9613   int action_element = change->action_element;
9614   int i;
9615
9616   if (!change->has_action)
9617     return;
9618
9619   /* ---------- determine action paramater values -------------------------- */
9620
9621   int level_time_value =
9622     (level.time > 0 ? TimeLeft :
9623      TimePlayed);
9624
9625   int action_arg_element_raw =
9626     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9627      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9628      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9629      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9630      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9631      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9632      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9633      EL_EMPTY);
9634   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9635
9636   int action_arg_direction =
9637     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9638      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9639      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9640      change->actual_trigger_side :
9641      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9642      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9643      MV_NONE);
9644
9645   int action_arg_number_min =
9646     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9647      CA_ARG_MIN);
9648
9649   int action_arg_number_max =
9650     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9651      action_type == CA_SET_LEVEL_GEMS ? 999 :
9652      action_type == CA_SET_LEVEL_TIME ? 9999 :
9653      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9654      action_type == CA_SET_CE_VALUE ? 9999 :
9655      action_type == CA_SET_CE_SCORE ? 9999 :
9656      CA_ARG_MAX);
9657
9658   int action_arg_number_reset =
9659     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9660      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9661      action_type == CA_SET_LEVEL_TIME ? level.time :
9662      action_type == CA_SET_LEVEL_SCORE ? 0 :
9663      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9664      action_type == CA_SET_CE_SCORE ? 0 :
9665      0);
9666
9667   int action_arg_number =
9668     (action_arg <= CA_ARG_MAX ? action_arg :
9669      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9670      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9671      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9672      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9673      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9674      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9675      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9676      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9677      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9678      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9679      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9680      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9681      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9682      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9683      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9684      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9685      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9686      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9687      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9688      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9689      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9690      -1);
9691
9692   int action_arg_number_old =
9693     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9694      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9695      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9696      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9697      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9698      0);
9699
9700   int action_arg_number_new =
9701     getModifiedActionNumber(action_arg_number_old,
9702                             action_mode, action_arg_number,
9703                             action_arg_number_min, action_arg_number_max);
9704
9705   int trigger_player_bits =
9706     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9707      change->actual_trigger_player_bits : change->trigger_player);
9708
9709   int action_arg_player_bits =
9710     (action_arg >= CA_ARG_PLAYER_1 &&
9711      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9712      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9713      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9714      PLAYER_BITS_ANY);
9715
9716   /* ---------- execute action  -------------------------------------------- */
9717
9718   switch (action_type)
9719   {
9720     case CA_NO_ACTION:
9721     {
9722       return;
9723     }
9724
9725     /* ---------- level actions  ------------------------------------------- */
9726
9727     case CA_RESTART_LEVEL:
9728     {
9729       game.restart_level = TRUE;
9730
9731       break;
9732     }
9733
9734     case CA_SHOW_ENVELOPE:
9735     {
9736       int element = getSpecialActionElement(action_arg_element,
9737                                             action_arg_number, EL_ENVELOPE_1);
9738
9739       if (IS_ENVELOPE(element))
9740         local_player->show_envelope = element;
9741
9742       break;
9743     }
9744
9745     case CA_SET_LEVEL_TIME:
9746     {
9747       if (level.time > 0)       /* only modify limited time value */
9748       {
9749         TimeLeft = action_arg_number_new;
9750
9751         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9752
9753         DisplayGameControlValues();
9754
9755         if (!TimeLeft && setup.time_limit)
9756           for (i = 0; i < MAX_PLAYERS; i++)
9757             KillPlayer(&stored_player[i]);
9758       }
9759
9760       break;
9761     }
9762
9763     case CA_SET_LEVEL_SCORE:
9764     {
9765       local_player->score = action_arg_number_new;
9766
9767       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9768
9769       DisplayGameControlValues();
9770
9771       break;
9772     }
9773
9774     case CA_SET_LEVEL_GEMS:
9775     {
9776       local_player->gems_still_needed = action_arg_number_new;
9777
9778       game.snapshot.collected_item = TRUE;
9779
9780       game_panel_controls[GAME_PANEL_GEMS].value =
9781         local_player->gems_still_needed;
9782
9783       DisplayGameControlValues();
9784
9785       break;
9786     }
9787
9788     case CA_SET_LEVEL_WIND:
9789     {
9790       game.wind_direction = action_arg_direction;
9791
9792       break;
9793     }
9794
9795     case CA_SET_LEVEL_RANDOM_SEED:
9796     {
9797       /* ensure that setting a new random seed while playing is predictable */
9798       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9799
9800       break;
9801     }
9802
9803     /* ---------- player actions  ------------------------------------------ */
9804
9805     case CA_MOVE_PLAYER:
9806     {
9807       /* automatically move to the next field in specified direction */
9808       for (i = 0; i < MAX_PLAYERS; i++)
9809         if (trigger_player_bits & (1 << i))
9810           stored_player[i].programmed_action = action_arg_direction;
9811
9812       break;
9813     }
9814
9815     case CA_EXIT_PLAYER:
9816     {
9817       for (i = 0; i < MAX_PLAYERS; i++)
9818         if (action_arg_player_bits & (1 << i))
9819           PlayerWins(&stored_player[i]);
9820
9821       break;
9822     }
9823
9824     case CA_KILL_PLAYER:
9825     {
9826       for (i = 0; i < MAX_PLAYERS; i++)
9827         if (action_arg_player_bits & (1 << i))
9828           KillPlayer(&stored_player[i]);
9829
9830       break;
9831     }
9832
9833     case CA_SET_PLAYER_KEYS:
9834     {
9835       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9836       int element = getSpecialActionElement(action_arg_element,
9837                                             action_arg_number, EL_KEY_1);
9838
9839       if (IS_KEY(element))
9840       {
9841         for (i = 0; i < MAX_PLAYERS; i++)
9842         {
9843           if (trigger_player_bits & (1 << i))
9844           {
9845             stored_player[i].key[KEY_NR(element)] = key_state;
9846
9847             DrawGameDoorValues();
9848           }
9849         }
9850       }
9851
9852       break;
9853     }
9854
9855     case CA_SET_PLAYER_SPEED:
9856     {
9857       for (i = 0; i < MAX_PLAYERS; i++)
9858       {
9859         if (trigger_player_bits & (1 << i))
9860         {
9861           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9862
9863           if (action_arg == CA_ARG_SPEED_FASTER &&
9864               stored_player[i].cannot_move)
9865           {
9866             action_arg_number = STEPSIZE_VERY_SLOW;
9867           }
9868           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9869                    action_arg == CA_ARG_SPEED_FASTER)
9870           {
9871             action_arg_number = 2;
9872             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9873                            CA_MODE_MULTIPLY);
9874           }
9875           else if (action_arg == CA_ARG_NUMBER_RESET)
9876           {
9877             action_arg_number = level.initial_player_stepsize[i];
9878           }
9879
9880           move_stepsize =
9881             getModifiedActionNumber(move_stepsize,
9882                                     action_mode,
9883                                     action_arg_number,
9884                                     action_arg_number_min,
9885                                     action_arg_number_max);
9886
9887           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9888         }
9889       }
9890
9891       break;
9892     }
9893
9894     case CA_SET_PLAYER_SHIELD:
9895     {
9896       for (i = 0; i < MAX_PLAYERS; i++)
9897       {
9898         if (trigger_player_bits & (1 << i))
9899         {
9900           if (action_arg == CA_ARG_SHIELD_OFF)
9901           {
9902             stored_player[i].shield_normal_time_left = 0;
9903             stored_player[i].shield_deadly_time_left = 0;
9904           }
9905           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9906           {
9907             stored_player[i].shield_normal_time_left = 999999;
9908           }
9909           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9910           {
9911             stored_player[i].shield_normal_time_left = 999999;
9912             stored_player[i].shield_deadly_time_left = 999999;
9913           }
9914         }
9915       }
9916
9917       break;
9918     }
9919
9920     case CA_SET_PLAYER_GRAVITY:
9921     {
9922       for (i = 0; i < MAX_PLAYERS; i++)
9923       {
9924         if (trigger_player_bits & (1 << i))
9925         {
9926           stored_player[i].gravity =
9927             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9928              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9929              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9930              stored_player[i].gravity);
9931         }
9932       }
9933
9934       break;
9935     }
9936
9937     case CA_SET_PLAYER_ARTWORK:
9938     {
9939       for (i = 0; i < MAX_PLAYERS; i++)
9940       {
9941         if (trigger_player_bits & (1 << i))
9942         {
9943           int artwork_element = action_arg_element;
9944
9945           if (action_arg == CA_ARG_ELEMENT_RESET)
9946             artwork_element =
9947               (level.use_artwork_element[i] ? level.artwork_element[i] :
9948                stored_player[i].element_nr);
9949
9950           if (stored_player[i].artwork_element != artwork_element)
9951             stored_player[i].Frame = 0;
9952
9953           stored_player[i].artwork_element = artwork_element;
9954
9955           SetPlayerWaiting(&stored_player[i], FALSE);
9956
9957           /* set number of special actions for bored and sleeping animation */
9958           stored_player[i].num_special_action_bored =
9959             get_num_special_action(artwork_element,
9960                                    ACTION_BORING_1, ACTION_BORING_LAST);
9961           stored_player[i].num_special_action_sleeping =
9962             get_num_special_action(artwork_element,
9963                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9964         }
9965       }
9966
9967       break;
9968     }
9969
9970     case CA_SET_PLAYER_INVENTORY:
9971     {
9972       for (i = 0; i < MAX_PLAYERS; i++)
9973       {
9974         struct PlayerInfo *player = &stored_player[i];
9975         int j, k;
9976
9977         if (trigger_player_bits & (1 << i))
9978         {
9979           int inventory_element = action_arg_element;
9980
9981           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9982               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9983               action_arg == CA_ARG_ELEMENT_ACTION)
9984           {
9985             int element = inventory_element;
9986             int collect_count = element_info[element].collect_count_initial;
9987
9988             if (!IS_CUSTOM_ELEMENT(element))
9989               collect_count = 1;
9990
9991             if (collect_count == 0)
9992               player->inventory_infinite_element = element;
9993             else
9994               for (k = 0; k < collect_count; k++)
9995                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9996                   player->inventory_element[player->inventory_size++] =
9997                     element;
9998           }
9999           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10000                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10001                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10002           {
10003             if (player->inventory_infinite_element != EL_UNDEFINED &&
10004                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10005                                      action_arg_element_raw))
10006               player->inventory_infinite_element = EL_UNDEFINED;
10007
10008             for (k = 0, j = 0; j < player->inventory_size; j++)
10009             {
10010               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10011                                         action_arg_element_raw))
10012                 player->inventory_element[k++] = player->inventory_element[j];
10013             }
10014
10015             player->inventory_size = k;
10016           }
10017           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10018           {
10019             if (player->inventory_size > 0)
10020             {
10021               for (j = 0; j < player->inventory_size - 1; j++)
10022                 player->inventory_element[j] = player->inventory_element[j + 1];
10023
10024               player->inventory_size--;
10025             }
10026           }
10027           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10028           {
10029             if (player->inventory_size > 0)
10030               player->inventory_size--;
10031           }
10032           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10033           {
10034             player->inventory_infinite_element = EL_UNDEFINED;
10035             player->inventory_size = 0;
10036           }
10037           else if (action_arg == CA_ARG_INVENTORY_RESET)
10038           {
10039             player->inventory_infinite_element = EL_UNDEFINED;
10040             player->inventory_size = 0;
10041
10042             if (level.use_initial_inventory[i])
10043             {
10044               for (j = 0; j < level.initial_inventory_size[i]; j++)
10045               {
10046                 int element = level.initial_inventory_content[i][j];
10047                 int collect_count = element_info[element].collect_count_initial;
10048
10049                 if (!IS_CUSTOM_ELEMENT(element))
10050                   collect_count = 1;
10051
10052                 if (collect_count == 0)
10053                   player->inventory_infinite_element = element;
10054                 else
10055                   for (k = 0; k < collect_count; k++)
10056                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10057                       player->inventory_element[player->inventory_size++] =
10058                         element;
10059               }
10060             }
10061           }
10062         }
10063       }
10064
10065       break;
10066     }
10067
10068     /* ---------- CE actions  ---------------------------------------------- */
10069
10070     case CA_SET_CE_VALUE:
10071     {
10072       int last_ce_value = CustomValue[x][y];
10073
10074       CustomValue[x][y] = action_arg_number_new;
10075
10076       if (CustomValue[x][y] != last_ce_value)
10077       {
10078         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10079         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10080
10081         if (CustomValue[x][y] == 0)
10082         {
10083           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10084           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10085         }
10086       }
10087
10088       break;
10089     }
10090
10091     case CA_SET_CE_SCORE:
10092     {
10093       int last_ce_score = ei->collect_score;
10094
10095       ei->collect_score = action_arg_number_new;
10096
10097       if (ei->collect_score != last_ce_score)
10098       {
10099         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10100         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10101
10102         if (ei->collect_score == 0)
10103         {
10104           int xx, yy;
10105
10106           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10107           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10108
10109           /*
10110             This is a very special case that seems to be a mixture between
10111             CheckElementChange() and CheckTriggeredElementChange(): while
10112             the first one only affects single elements that are triggered
10113             directly, the second one affects multiple elements in the playfield
10114             that are triggered indirectly by another element. This is a third
10115             case: Changing the CE score always affects multiple identical CEs,
10116             so every affected CE must be checked, not only the single CE for
10117             which the CE score was changed in the first place (as every instance
10118             of that CE shares the same CE score, and therefore also can change)!
10119           */
10120           SCAN_PLAYFIELD(xx, yy)
10121           {
10122             if (Feld[xx][yy] == element)
10123               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10124                                  CE_SCORE_GETS_ZERO);
10125           }
10126         }
10127       }
10128
10129       break;
10130     }
10131
10132     case CA_SET_CE_ARTWORK:
10133     {
10134       int artwork_element = action_arg_element;
10135       boolean reset_frame = FALSE;
10136       int xx, yy;
10137
10138       if (action_arg == CA_ARG_ELEMENT_RESET)
10139         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10140                            element);
10141
10142       if (ei->gfx_element != artwork_element)
10143         reset_frame = TRUE;
10144
10145       ei->gfx_element = artwork_element;
10146
10147       SCAN_PLAYFIELD(xx, yy)
10148       {
10149         if (Feld[xx][yy] == element)
10150         {
10151           if (reset_frame)
10152           {
10153             ResetGfxAnimation(xx, yy);
10154             ResetRandomAnimationValue(xx, yy);
10155           }
10156
10157           TEST_DrawLevelField(xx, yy);
10158         }
10159       }
10160
10161       break;
10162     }
10163
10164     /* ---------- engine actions  ------------------------------------------ */
10165
10166     case CA_SET_ENGINE_SCAN_MODE:
10167     {
10168       InitPlayfieldScanMode(action_arg);
10169
10170       break;
10171     }
10172
10173     default:
10174       break;
10175   }
10176 }
10177
10178 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10179 {
10180   int old_element = Feld[x][y];
10181   int new_element = GetElementFromGroupElement(element);
10182   int previous_move_direction = MovDir[x][y];
10183   int last_ce_value = CustomValue[x][y];
10184   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10185   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10186   boolean add_player_onto_element = (new_element_is_player &&
10187                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10188                                      IS_WALKABLE(old_element));
10189
10190   if (!add_player_onto_element)
10191   {
10192     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10193       RemoveMovingField(x, y);
10194     else
10195       RemoveField(x, y);
10196
10197     Feld[x][y] = new_element;
10198
10199     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10200       MovDir[x][y] = previous_move_direction;
10201
10202     if (element_info[new_element].use_last_ce_value)
10203       CustomValue[x][y] = last_ce_value;
10204
10205     InitField_WithBug1(x, y, FALSE);
10206
10207     new_element = Feld[x][y];   /* element may have changed */
10208
10209     ResetGfxAnimation(x, y);
10210     ResetRandomAnimationValue(x, y);
10211
10212     TEST_DrawLevelField(x, y);
10213
10214     if (GFX_CRUMBLED(new_element))
10215       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10216   }
10217
10218   /* check if element under the player changes from accessible to unaccessible
10219      (needed for special case of dropping element which then changes) */
10220   /* (must be checked after creating new element for walkable group elements) */
10221   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10222       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10223   {
10224     Bang(x, y);
10225
10226     return;
10227   }
10228
10229   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10230   if (new_element_is_player)
10231     RelocatePlayer(x, y, new_element);
10232
10233   if (is_change)
10234     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10235
10236   TestIfBadThingTouchesPlayer(x, y);
10237   TestIfPlayerTouchesCustomElement(x, y);
10238   TestIfElementTouchesCustomElement(x, y);
10239 }
10240
10241 static void CreateField(int x, int y, int element)
10242 {
10243   CreateFieldExt(x, y, element, FALSE);
10244 }
10245
10246 static void CreateElementFromChange(int x, int y, int element)
10247 {
10248   element = GET_VALID_RUNTIME_ELEMENT(element);
10249
10250   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10251   {
10252     int old_element = Feld[x][y];
10253
10254     /* prevent changed element from moving in same engine frame
10255        unless both old and new element can either fall or move */
10256     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10257         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10258       Stop[x][y] = TRUE;
10259   }
10260
10261   CreateFieldExt(x, y, element, TRUE);
10262 }
10263
10264 static boolean ChangeElement(int x, int y, int element, int page)
10265 {
10266   struct ElementInfo *ei = &element_info[element];
10267   struct ElementChangeInfo *change = &ei->change_page[page];
10268   int ce_value = CustomValue[x][y];
10269   int ce_score = ei->collect_score;
10270   int target_element;
10271   int old_element = Feld[x][y];
10272
10273   /* always use default change event to prevent running into a loop */
10274   if (ChangeEvent[x][y] == -1)
10275     ChangeEvent[x][y] = CE_DELAY;
10276
10277   if (ChangeEvent[x][y] == CE_DELAY)
10278   {
10279     /* reset actual trigger element, trigger player and action element */
10280     change->actual_trigger_element = EL_EMPTY;
10281     change->actual_trigger_player = EL_EMPTY;
10282     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10283     change->actual_trigger_side = CH_SIDE_NONE;
10284     change->actual_trigger_ce_value = 0;
10285     change->actual_trigger_ce_score = 0;
10286   }
10287
10288   /* do not change elements more than a specified maximum number of changes */
10289   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10290     return FALSE;
10291
10292   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10293
10294   if (change->explode)
10295   {
10296     Bang(x, y);
10297
10298     return TRUE;
10299   }
10300
10301   if (change->use_target_content)
10302   {
10303     boolean complete_replace = TRUE;
10304     boolean can_replace[3][3];
10305     int xx, yy;
10306
10307     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10308     {
10309       boolean is_empty;
10310       boolean is_walkable;
10311       boolean is_diggable;
10312       boolean is_collectible;
10313       boolean is_removable;
10314       boolean is_destructible;
10315       int ex = x + xx - 1;
10316       int ey = y + yy - 1;
10317       int content_element = change->target_content.e[xx][yy];
10318       int e;
10319
10320       can_replace[xx][yy] = TRUE;
10321
10322       if (ex == x && ey == y)   /* do not check changing element itself */
10323         continue;
10324
10325       if (content_element == EL_EMPTY_SPACE)
10326       {
10327         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10328
10329         continue;
10330       }
10331
10332       if (!IN_LEV_FIELD(ex, ey))
10333       {
10334         can_replace[xx][yy] = FALSE;
10335         complete_replace = FALSE;
10336
10337         continue;
10338       }
10339
10340       e = Feld[ex][ey];
10341
10342       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10343         e = MovingOrBlocked2Element(ex, ey);
10344
10345       is_empty = (IS_FREE(ex, ey) ||
10346                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10347
10348       is_walkable     = (is_empty || IS_WALKABLE(e));
10349       is_diggable     = (is_empty || IS_DIGGABLE(e));
10350       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10351       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10352       is_removable    = (is_diggable || is_collectible);
10353
10354       can_replace[xx][yy] =
10355         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10356           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10357           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10358           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10359           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10360           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10361          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10362
10363       if (!can_replace[xx][yy])
10364         complete_replace = FALSE;
10365     }
10366
10367     if (!change->only_if_complete || complete_replace)
10368     {
10369       boolean something_has_changed = FALSE;
10370
10371       if (change->only_if_complete && change->use_random_replace &&
10372           RND(100) < change->random_percentage)
10373         return FALSE;
10374
10375       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10376       {
10377         int ex = x + xx - 1;
10378         int ey = y + yy - 1;
10379         int content_element;
10380
10381         if (can_replace[xx][yy] && (!change->use_random_replace ||
10382                                     RND(100) < change->random_percentage))
10383         {
10384           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10385             RemoveMovingField(ex, ey);
10386
10387           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10388
10389           content_element = change->target_content.e[xx][yy];
10390           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10391                                               ce_value, ce_score);
10392
10393           CreateElementFromChange(ex, ey, target_element);
10394
10395           something_has_changed = TRUE;
10396
10397           /* for symmetry reasons, freeze newly created border elements */
10398           if (ex != x || ey != y)
10399             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10400         }
10401       }
10402
10403       if (something_has_changed)
10404       {
10405         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10406         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10407       }
10408     }
10409   }
10410   else
10411   {
10412     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10413                                         ce_value, ce_score);
10414
10415     if (element == EL_DIAGONAL_GROWING ||
10416         element == EL_DIAGONAL_SHRINKING)
10417     {
10418       target_element = Store[x][y];
10419
10420       Store[x][y] = EL_EMPTY;
10421     }
10422
10423     CreateElementFromChange(x, y, target_element);
10424
10425     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10426     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10427   }
10428
10429   /* this uses direct change before indirect change */
10430   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10431
10432   return TRUE;
10433 }
10434
10435 static void HandleElementChange(int x, int y, int page)
10436 {
10437   int element = MovingOrBlocked2Element(x, y);
10438   struct ElementInfo *ei = &element_info[element];
10439   struct ElementChangeInfo *change = &ei->change_page[page];
10440   boolean handle_action_before_change = FALSE;
10441
10442 #ifdef DEBUG
10443   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10444       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10445   {
10446     printf("\n\n");
10447     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10448            x, y, element, element_info[element].token_name);
10449     printf("HandleElementChange(): This should never happen!\n");
10450     printf("\n\n");
10451   }
10452 #endif
10453
10454   /* this can happen with classic bombs on walkable, changing elements */
10455   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10456   {
10457     return;
10458   }
10459
10460   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10461   {
10462     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10463
10464     if (change->can_change)
10465     {
10466       /* !!! not clear why graphic animation should be reset at all here !!! */
10467       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10468       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10469
10470       /*
10471         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10472
10473         When using an animation frame delay of 1 (this only happens with
10474         "sp_zonk.moving.left/right" in the classic graphics), the default
10475         (non-moving) animation shows wrong animation frames (while the
10476         moving animation, like "sp_zonk.moving.left/right", is correct,
10477         so this graphical bug never shows up with the classic graphics).
10478         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10479         be drawn instead of the correct frames 0,1,2,3. This is caused by
10480         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10481         an element change: First when the change delay ("ChangeDelay[][]")
10482         counter has reached zero after decrementing, then a second time in
10483         the next frame (after "GfxFrame[][]" was already incremented) when
10484         "ChangeDelay[][]" is reset to the initial delay value again.
10485
10486         This causes frame 0 to be drawn twice, while the last frame won't
10487         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10488
10489         As some animations may already be cleverly designed around this bug
10490         (at least the "Snake Bite" snake tail animation does this), it cannot
10491         simply be fixed here without breaking such existing animations.
10492         Unfortunately, it cannot easily be detected if a graphics set was
10493         designed "before" or "after" the bug was fixed. As a workaround,
10494         a new graphics set option "game.graphics_engine_version" was added
10495         to be able to specify the game's major release version for which the
10496         graphics set was designed, which can then be used to decide if the
10497         bugfix should be used (version 4 and above) or not (version 3 or
10498         below, or if no version was specified at all, as with old sets).
10499
10500         (The wrong/fixed animation frames can be tested with the test level set
10501         "test_gfxframe" and level "000", which contains a specially prepared
10502         custom element at level position (x/y) == (11/9) which uses the zonk
10503         animation mentioned above. Using "game.graphics_engine_version: 4"
10504         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10505         This can also be seen from the debug output for this test element.)
10506       */
10507
10508       /* when a custom element is about to change (for example by change delay),
10509          do not reset graphic animation when the custom element is moving */
10510       if (game.graphics_engine_version < 4 &&
10511           !IS_MOVING(x, y))
10512       {
10513         ResetGfxAnimation(x, y);
10514         ResetRandomAnimationValue(x, y);
10515       }
10516
10517       if (change->pre_change_function)
10518         change->pre_change_function(x, y);
10519     }
10520   }
10521
10522   ChangeDelay[x][y]--;
10523
10524   if (ChangeDelay[x][y] != 0)           /* continue element change */
10525   {
10526     if (change->can_change)
10527     {
10528       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10529
10530       if (IS_ANIMATED(graphic))
10531         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10532
10533       if (change->change_function)
10534         change->change_function(x, y);
10535     }
10536   }
10537   else                                  /* finish element change */
10538   {
10539     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10540     {
10541       page = ChangePage[x][y];
10542       ChangePage[x][y] = -1;
10543
10544       change = &ei->change_page[page];
10545     }
10546
10547     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10548     {
10549       ChangeDelay[x][y] = 1;            /* try change after next move step */
10550       ChangePage[x][y] = page;          /* remember page to use for change */
10551
10552       return;
10553     }
10554
10555     /* special case: set new level random seed before changing element */
10556     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10557       handle_action_before_change = TRUE;
10558
10559     if (change->has_action && handle_action_before_change)
10560       ExecuteCustomElementAction(x, y, element, page);
10561
10562     if (change->can_change)
10563     {
10564       if (ChangeElement(x, y, element, page))
10565       {
10566         if (change->post_change_function)
10567           change->post_change_function(x, y);
10568       }
10569     }
10570
10571     if (change->has_action && !handle_action_before_change)
10572       ExecuteCustomElementAction(x, y, element, page);
10573   }
10574 }
10575
10576 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10577                                               int trigger_element,
10578                                               int trigger_event,
10579                                               int trigger_player,
10580                                               int trigger_side,
10581                                               int trigger_page)
10582 {
10583   boolean change_done_any = FALSE;
10584   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10585   int i;
10586
10587   if (!(trigger_events[trigger_element][trigger_event]))
10588     return FALSE;
10589
10590   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10591
10592   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10593   {
10594     int element = EL_CUSTOM_START + i;
10595     boolean change_done = FALSE;
10596     int p;
10597
10598     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10599         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10600       continue;
10601
10602     for (p = 0; p < element_info[element].num_change_pages; p++)
10603     {
10604       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10605
10606       if (change->can_change_or_has_action &&
10607           change->has_event[trigger_event] &&
10608           change->trigger_side & trigger_side &&
10609           change->trigger_player & trigger_player &&
10610           change->trigger_page & trigger_page_bits &&
10611           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10612       {
10613         change->actual_trigger_element = trigger_element;
10614         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10615         change->actual_trigger_player_bits = trigger_player;
10616         change->actual_trigger_side = trigger_side;
10617         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10618         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10619
10620         if ((change->can_change && !change_done) || change->has_action)
10621         {
10622           int x, y;
10623
10624           SCAN_PLAYFIELD(x, y)
10625           {
10626             if (Feld[x][y] == element)
10627             {
10628               if (change->can_change && !change_done)
10629               {
10630                 /* if element already changed in this frame, not only prevent
10631                    another element change (checked in ChangeElement()), but
10632                    also prevent additional element actions for this element */
10633
10634                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10635                     !level.use_action_after_change_bug)
10636                   continue;
10637
10638                 ChangeDelay[x][y] = 1;
10639                 ChangeEvent[x][y] = trigger_event;
10640
10641                 HandleElementChange(x, y, p);
10642               }
10643               else if (change->has_action)
10644               {
10645                 /* if element already changed in this frame, not only prevent
10646                    another element change (checked in ChangeElement()), but
10647                    also prevent additional element actions for this element */
10648
10649                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10650                     !level.use_action_after_change_bug)
10651                   continue;
10652
10653                 ExecuteCustomElementAction(x, y, element, p);
10654                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10655               }
10656             }
10657           }
10658
10659           if (change->can_change)
10660           {
10661             change_done = TRUE;
10662             change_done_any = TRUE;
10663           }
10664         }
10665       }
10666     }
10667   }
10668
10669   RECURSION_LOOP_DETECTION_END();
10670
10671   return change_done_any;
10672 }
10673
10674 static boolean CheckElementChangeExt(int x, int y,
10675                                      int element,
10676                                      int trigger_element,
10677                                      int trigger_event,
10678                                      int trigger_player,
10679                                      int trigger_side)
10680 {
10681   boolean change_done = FALSE;
10682   int p;
10683
10684   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10685       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10686     return FALSE;
10687
10688   if (Feld[x][y] == EL_BLOCKED)
10689   {
10690     Blocked2Moving(x, y, &x, &y);
10691     element = Feld[x][y];
10692   }
10693
10694   /* check if element has already changed or is about to change after moving */
10695   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10696        Feld[x][y] != element) ||
10697
10698       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10699        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10700         ChangePage[x][y] != -1)))
10701     return FALSE;
10702
10703   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10704
10705   for (p = 0; p < element_info[element].num_change_pages; p++)
10706   {
10707     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10708
10709     /* check trigger element for all events where the element that is checked
10710        for changing interacts with a directly adjacent element -- this is
10711        different to element changes that affect other elements to change on the
10712        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10713     boolean check_trigger_element =
10714       (trigger_event == CE_TOUCHING_X ||
10715        trigger_event == CE_HITTING_X ||
10716        trigger_event == CE_HIT_BY_X ||
10717        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10718
10719     if (change->can_change_or_has_action &&
10720         change->has_event[trigger_event] &&
10721         change->trigger_side & trigger_side &&
10722         change->trigger_player & trigger_player &&
10723         (!check_trigger_element ||
10724          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10725     {
10726       change->actual_trigger_element = trigger_element;
10727       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10728       change->actual_trigger_player_bits = trigger_player;
10729       change->actual_trigger_side = trigger_side;
10730       change->actual_trigger_ce_value = CustomValue[x][y];
10731       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10732
10733       /* special case: trigger element not at (x,y) position for some events */
10734       if (check_trigger_element)
10735       {
10736         static struct
10737         {
10738           int dx, dy;
10739         } move_xy[] =
10740           {
10741             {  0,  0 },
10742             { -1,  0 },
10743             { +1,  0 },
10744             {  0,  0 },
10745             {  0, -1 },
10746             {  0,  0 }, { 0, 0 }, { 0, 0 },
10747             {  0, +1 }
10748           };
10749
10750         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10751         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10752
10753         change->actual_trigger_ce_value = CustomValue[xx][yy];
10754         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10755       }
10756
10757       if (change->can_change && !change_done)
10758       {
10759         ChangeDelay[x][y] = 1;
10760         ChangeEvent[x][y] = trigger_event;
10761
10762         HandleElementChange(x, y, p);
10763
10764         change_done = TRUE;
10765       }
10766       else if (change->has_action)
10767       {
10768         ExecuteCustomElementAction(x, y, element, p);
10769         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10770       }
10771     }
10772   }
10773
10774   RECURSION_LOOP_DETECTION_END();
10775
10776   return change_done;
10777 }
10778
10779 static void PlayPlayerSound(struct PlayerInfo *player)
10780 {
10781   int jx = player->jx, jy = player->jy;
10782   int sound_element = player->artwork_element;
10783   int last_action = player->last_action_waiting;
10784   int action = player->action_waiting;
10785
10786   if (player->is_waiting)
10787   {
10788     if (action != last_action)
10789       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10790     else
10791       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10792   }
10793   else
10794   {
10795     if (action != last_action)
10796       StopSound(element_info[sound_element].sound[last_action]);
10797
10798     if (last_action == ACTION_SLEEPING)
10799       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10800   }
10801 }
10802
10803 static void PlayAllPlayersSound()
10804 {
10805   int i;
10806
10807   for (i = 0; i < MAX_PLAYERS; i++)
10808     if (stored_player[i].active)
10809       PlayPlayerSound(&stored_player[i]);
10810 }
10811
10812 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10813 {
10814   boolean last_waiting = player->is_waiting;
10815   int move_dir = player->MovDir;
10816
10817   player->dir_waiting = move_dir;
10818   player->last_action_waiting = player->action_waiting;
10819
10820   if (is_waiting)
10821   {
10822     if (!last_waiting)          /* not waiting -> waiting */
10823     {
10824       player->is_waiting = TRUE;
10825
10826       player->frame_counter_bored =
10827         FrameCounter +
10828         game.player_boring_delay_fixed +
10829         GetSimpleRandom(game.player_boring_delay_random);
10830       player->frame_counter_sleeping =
10831         FrameCounter +
10832         game.player_sleeping_delay_fixed +
10833         GetSimpleRandom(game.player_sleeping_delay_random);
10834
10835       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10836     }
10837
10838     if (game.player_sleeping_delay_fixed +
10839         game.player_sleeping_delay_random > 0 &&
10840         player->anim_delay_counter == 0 &&
10841         player->post_delay_counter == 0 &&
10842         FrameCounter >= player->frame_counter_sleeping)
10843       player->is_sleeping = TRUE;
10844     else if (game.player_boring_delay_fixed +
10845              game.player_boring_delay_random > 0 &&
10846              FrameCounter >= player->frame_counter_bored)
10847       player->is_bored = TRUE;
10848
10849     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10850                               player->is_bored ? ACTION_BORING :
10851                               ACTION_WAITING);
10852
10853     if (player->is_sleeping && player->use_murphy)
10854     {
10855       /* special case for sleeping Murphy when leaning against non-free tile */
10856
10857       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10858           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10859            !IS_MOVING(player->jx - 1, player->jy)))
10860         move_dir = MV_LEFT;
10861       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10862                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10863                 !IS_MOVING(player->jx + 1, player->jy)))
10864         move_dir = MV_RIGHT;
10865       else
10866         player->is_sleeping = FALSE;
10867
10868       player->dir_waiting = move_dir;
10869     }
10870
10871     if (player->is_sleeping)
10872     {
10873       if (player->num_special_action_sleeping > 0)
10874       {
10875         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10876         {
10877           int last_special_action = player->special_action_sleeping;
10878           int num_special_action = player->num_special_action_sleeping;
10879           int special_action =
10880             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10881              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10882              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10883              last_special_action + 1 : ACTION_SLEEPING);
10884           int special_graphic =
10885             el_act_dir2img(player->artwork_element, special_action, move_dir);
10886
10887           player->anim_delay_counter =
10888             graphic_info[special_graphic].anim_delay_fixed +
10889             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10890           player->post_delay_counter =
10891             graphic_info[special_graphic].post_delay_fixed +
10892             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10893
10894           player->special_action_sleeping = special_action;
10895         }
10896
10897         if (player->anim_delay_counter > 0)
10898         {
10899           player->action_waiting = player->special_action_sleeping;
10900           player->anim_delay_counter--;
10901         }
10902         else if (player->post_delay_counter > 0)
10903         {
10904           player->post_delay_counter--;
10905         }
10906       }
10907     }
10908     else if (player->is_bored)
10909     {
10910       if (player->num_special_action_bored > 0)
10911       {
10912         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10913         {
10914           int special_action =
10915             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10916           int special_graphic =
10917             el_act_dir2img(player->artwork_element, special_action, move_dir);
10918
10919           player->anim_delay_counter =
10920             graphic_info[special_graphic].anim_delay_fixed +
10921             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10922           player->post_delay_counter =
10923             graphic_info[special_graphic].post_delay_fixed +
10924             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10925
10926           player->special_action_bored = special_action;
10927         }
10928
10929         if (player->anim_delay_counter > 0)
10930         {
10931           player->action_waiting = player->special_action_bored;
10932           player->anim_delay_counter--;
10933         }
10934         else if (player->post_delay_counter > 0)
10935         {
10936           player->post_delay_counter--;
10937         }
10938       }
10939     }
10940   }
10941   else if (last_waiting)        /* waiting -> not waiting */
10942   {
10943     player->is_waiting = FALSE;
10944     player->is_bored = FALSE;
10945     player->is_sleeping = FALSE;
10946
10947     player->frame_counter_bored = -1;
10948     player->frame_counter_sleeping = -1;
10949
10950     player->anim_delay_counter = 0;
10951     player->post_delay_counter = 0;
10952
10953     player->dir_waiting = player->MovDir;
10954     player->action_waiting = ACTION_DEFAULT;
10955
10956     player->special_action_bored = ACTION_DEFAULT;
10957     player->special_action_sleeping = ACTION_DEFAULT;
10958   }
10959 }
10960
10961 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10962 {
10963   if ((!player->is_moving  && player->was_moving) ||
10964       (player->MovPos == 0 && player->was_moving) ||
10965       (player->is_snapping && !player->was_snapping) ||
10966       (player->is_dropping && !player->was_dropping))
10967   {
10968     if (!CheckSaveEngineSnapshotToList())
10969       return;
10970
10971     player->was_moving = FALSE;
10972     player->was_snapping = TRUE;
10973     player->was_dropping = TRUE;
10974   }
10975   else
10976   {
10977     if (player->is_moving)
10978       player->was_moving = TRUE;
10979
10980     if (!player->is_snapping)
10981       player->was_snapping = FALSE;
10982
10983     if (!player->is_dropping)
10984       player->was_dropping = FALSE;
10985   }
10986 }
10987
10988 static void CheckSingleStepMode(struct PlayerInfo *player)
10989 {
10990   if (tape.single_step && tape.recording && !tape.pausing)
10991   {
10992     /* as it is called "single step mode", just return to pause mode when the
10993        player stopped moving after one tile (or never starts moving at all) */
10994     if (!player->is_moving &&
10995         !player->is_pushing &&
10996         !player->is_dropping_pressed)
10997     {
10998       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10999       SnapField(player, 0, 0);                  /* stop snapping */
11000     }
11001   }
11002
11003   CheckSaveEngineSnapshot(player);
11004 }
11005
11006 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11007 {
11008   int left      = player_action & JOY_LEFT;
11009   int right     = player_action & JOY_RIGHT;
11010   int up        = player_action & JOY_UP;
11011   int down      = player_action & JOY_DOWN;
11012   int button1   = player_action & JOY_BUTTON_1;
11013   int button2   = player_action & JOY_BUTTON_2;
11014   int dx        = (left ? -1 : right ? 1 : 0);
11015   int dy        = (up   ? -1 : down  ? 1 : 0);
11016
11017   if (!player->active || tape.pausing)
11018     return 0;
11019
11020   if (player_action)
11021   {
11022     if (button1)
11023       SnapField(player, dx, dy);
11024     else
11025     {
11026       if (button2)
11027         DropElement(player);
11028
11029       MovePlayer(player, dx, dy);
11030     }
11031
11032     CheckSingleStepMode(player);
11033
11034     SetPlayerWaiting(player, FALSE);
11035
11036     return player_action;
11037   }
11038   else
11039   {
11040     /* no actions for this player (no input at player's configured device) */
11041
11042     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11043     SnapField(player, 0, 0);
11044     CheckGravityMovementWhenNotMoving(player);
11045
11046     if (player->MovPos == 0)
11047       SetPlayerWaiting(player, TRUE);
11048
11049     if (player->MovPos == 0)    /* needed for tape.playing */
11050       player->is_moving = FALSE;
11051
11052     player->is_dropping = FALSE;
11053     player->is_dropping_pressed = FALSE;
11054     player->drop_pressed_delay = 0;
11055
11056     CheckSingleStepMode(player);
11057
11058     return 0;
11059   }
11060 }
11061
11062 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11063                                          byte *tape_action)
11064 {
11065   if (!tape.use_mouse)
11066     return;
11067
11068   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11069   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11070   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11071 }
11072
11073 static void SetTapeActionFromMouseAction(byte *tape_action,
11074                                          struct MouseActionInfo *mouse_action)
11075 {
11076   if (!tape.use_mouse)
11077     return;
11078
11079   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11080   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11081   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11082 }
11083
11084 static void CheckLevelTime()
11085 {
11086   int i;
11087
11088   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11089   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11090   {
11091     if (level.native_em_level->lev->home == 0)  /* all players at home */
11092     {
11093       PlayerWins(local_player);
11094
11095       AllPlayersGone = TRUE;
11096
11097       level.native_em_level->lev->home = -1;
11098     }
11099
11100     if (level.native_em_level->ply[0]->alive == 0 &&
11101         level.native_em_level->ply[1]->alive == 0 &&
11102         level.native_em_level->ply[2]->alive == 0 &&
11103         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11104       AllPlayersGone = TRUE;
11105   }
11106   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11107   {
11108     if (game_sp.LevelSolved &&
11109         !game_sp.GameOver)                              /* game won */
11110     {
11111       PlayerWins(local_player);
11112
11113       game_sp.GameOver = TRUE;
11114
11115       AllPlayersGone = TRUE;
11116     }
11117
11118     if (game_sp.GameOver)                               /* game lost */
11119       AllPlayersGone = TRUE;
11120   }
11121   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11122   {
11123     if (game_mm.level_solved &&
11124         !game_mm.game_over)                             /* game won */
11125     {
11126       PlayerWins(local_player);
11127
11128       game_mm.game_over = TRUE;
11129
11130       AllPlayersGone = TRUE;
11131     }
11132
11133     if (game_mm.game_over)                              /* game lost */
11134       AllPlayersGone = TRUE;
11135   }
11136
11137   if (TimeFrames >= FRAMES_PER_SECOND)
11138   {
11139     TimeFrames = 0;
11140     TapeTime++;
11141
11142     for (i = 0; i < MAX_PLAYERS; i++)
11143     {
11144       struct PlayerInfo *player = &stored_player[i];
11145
11146       if (SHIELD_ON(player))
11147       {
11148         player->shield_normal_time_left--;
11149
11150         if (player->shield_deadly_time_left > 0)
11151           player->shield_deadly_time_left--;
11152       }
11153     }
11154
11155     if (!local_player->LevelSolved && !level.use_step_counter)
11156     {
11157       TimePlayed++;
11158
11159       if (TimeLeft > 0)
11160       {
11161         TimeLeft--;
11162
11163         if (TimeLeft <= 10 && setup.time_limit)
11164           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11165
11166         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11167            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11168
11169         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11170
11171         if (!TimeLeft && setup.time_limit)
11172         {
11173           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11174             level.native_em_level->lev->killed_out_of_time = TRUE;
11175           else
11176             for (i = 0; i < MAX_PLAYERS; i++)
11177               KillPlayer(&stored_player[i]);
11178         }
11179       }
11180       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11181       {
11182         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11183       }
11184
11185       level.native_em_level->lev->time =
11186         (game.no_time_limit ? TimePlayed : TimeLeft);
11187     }
11188
11189     if (tape.recording || tape.playing)
11190       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11191   }
11192
11193   if (tape.recording || tape.playing)
11194     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11195
11196   UpdateAndDisplayGameControlValues();
11197 }
11198
11199 void AdvanceFrameAndPlayerCounters(int player_nr)
11200 {
11201   int i;
11202
11203   /* advance frame counters (global frame counter and time frame counter) */
11204   FrameCounter++;
11205   TimeFrames++;
11206
11207   /* advance player counters (counters for move delay, move animation etc.) */
11208   for (i = 0; i < MAX_PLAYERS; i++)
11209   {
11210     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11211     int move_delay_value = stored_player[i].move_delay_value;
11212     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11213
11214     if (!advance_player_counters)       /* not all players may be affected */
11215       continue;
11216
11217     if (move_frames == 0)       /* less than one move per game frame */
11218     {
11219       int stepsize = TILEX / move_delay_value;
11220       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11221       int count = (stored_player[i].is_moving ?
11222                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11223
11224       if (count % delay == 0)
11225         move_frames = 1;
11226     }
11227
11228     stored_player[i].Frame += move_frames;
11229
11230     if (stored_player[i].MovPos != 0)
11231       stored_player[i].StepFrame += move_frames;
11232
11233     if (stored_player[i].move_delay > 0)
11234       stored_player[i].move_delay--;
11235
11236     /* due to bugs in previous versions, counter must count up, not down */
11237     if (stored_player[i].push_delay != -1)
11238       stored_player[i].push_delay++;
11239
11240     if (stored_player[i].drop_delay > 0)
11241       stored_player[i].drop_delay--;
11242
11243     if (stored_player[i].is_dropping_pressed)
11244       stored_player[i].drop_pressed_delay++;
11245   }
11246 }
11247
11248 void StartGameActions(boolean init_network_game, boolean record_tape,
11249                       int random_seed)
11250 {
11251   unsigned int new_random_seed = InitRND(random_seed);
11252
11253   if (record_tape)
11254     TapeStartRecording(new_random_seed);
11255
11256 #if defined(NETWORK_AVALIABLE)
11257   if (init_network_game)
11258   {
11259     SendToServer_StartPlaying();
11260
11261     return;
11262   }
11263 #endif
11264
11265   InitGame();
11266 }
11267
11268 void GameActionsExt()
11269 {
11270 #if 0
11271   static unsigned int game_frame_delay = 0;
11272 #endif
11273   unsigned int game_frame_delay_value;
11274   byte *recorded_player_action;
11275   byte summarized_player_action = 0;
11276   byte tape_action[MAX_PLAYERS];
11277   int i;
11278
11279   /* detect endless loops, caused by custom element programming */
11280   if (recursion_loop_detected && recursion_loop_depth == 0)
11281   {
11282     char *message = getStringCat3("Internal Error! Element ",
11283                                   EL_NAME(recursion_loop_element),
11284                                   " caused endless loop! Quit the game?");
11285
11286     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11287           EL_NAME(recursion_loop_element));
11288
11289     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11290
11291     recursion_loop_detected = FALSE;    /* if game should be continued */
11292
11293     free(message);
11294
11295     return;
11296   }
11297
11298   if (game.restart_level)
11299     StartGameActions(options.network, setup.autorecord, level.random_seed);
11300
11301   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11302   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11303   {
11304     if (level.native_em_level->lev->home == 0)  /* all players at home */
11305     {
11306       PlayerWins(local_player);
11307
11308       AllPlayersGone = TRUE;
11309
11310       level.native_em_level->lev->home = -1;
11311     }
11312
11313     if (level.native_em_level->ply[0]->alive == 0 &&
11314         level.native_em_level->ply[1]->alive == 0 &&
11315         level.native_em_level->ply[2]->alive == 0 &&
11316         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11317       AllPlayersGone = TRUE;
11318   }
11319   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11320   {
11321     if (game_sp.LevelSolved &&
11322         !game_sp.GameOver)                              /* game won */
11323     {
11324       PlayerWins(local_player);
11325
11326       game_sp.GameOver = TRUE;
11327
11328       AllPlayersGone = TRUE;
11329     }
11330
11331     if (game_sp.GameOver)                               /* game lost */
11332       AllPlayersGone = TRUE;
11333   }
11334   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11335   {
11336     if (game_mm.level_solved &&
11337         !game_mm.game_over)                             /* game won */
11338     {
11339       PlayerWins(local_player);
11340
11341       game_mm.game_over = TRUE;
11342
11343       AllPlayersGone = TRUE;
11344     }
11345
11346     if (game_mm.game_over)                              /* game lost */
11347       AllPlayersGone = TRUE;
11348   }
11349
11350   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11351     GameWon();
11352
11353   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11354     TapeStop();
11355
11356   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11357     return;
11358
11359   game_frame_delay_value =
11360     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11361
11362   if (tape.playing && tape.warp_forward && !tape.pausing)
11363     game_frame_delay_value = 0;
11364
11365   SetVideoFrameDelay(game_frame_delay_value);
11366
11367 #if 0
11368 #if 0
11369   /* ---------- main game synchronization point ---------- */
11370
11371   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11372
11373   printf("::: skip == %d\n", skip);
11374
11375 #else
11376   /* ---------- main game synchronization point ---------- */
11377
11378   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11379 #endif
11380 #endif
11381
11382   if (network_playing && !network_player_action_received)
11383   {
11384     /* try to get network player actions in time */
11385
11386 #if defined(NETWORK_AVALIABLE)
11387     /* last chance to get network player actions without main loop delay */
11388     HandleNetworking();
11389 #endif
11390
11391     /* game was quit by network peer */
11392     if (game_status != GAME_MODE_PLAYING)
11393       return;
11394
11395     if (!network_player_action_received)
11396       return;           /* failed to get network player actions in time */
11397
11398     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11399   }
11400
11401   if (tape.pausing)
11402     return;
11403
11404   /* at this point we know that we really continue executing the game */
11405
11406   network_player_action_received = FALSE;
11407
11408   /* when playing tape, read previously recorded player input from tape data */
11409   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11410
11411   local_player->effective_mouse_action = local_player->mouse_action;
11412
11413   if (recorded_player_action != NULL)
11414     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11415                                  recorded_player_action);
11416
11417   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11418   if (tape.pausing)
11419     return;
11420
11421   if (tape.set_centered_player)
11422   {
11423     game.centered_player_nr_next = tape.centered_player_nr_next;
11424     game.set_centered_player = TRUE;
11425   }
11426
11427   for (i = 0; i < MAX_PLAYERS; i++)
11428   {
11429     summarized_player_action |= stored_player[i].action;
11430
11431     if (!network_playing && (game.team_mode || tape.playing))
11432       stored_player[i].effective_action = stored_player[i].action;
11433   }
11434
11435 #if defined(NETWORK_AVALIABLE)
11436   if (network_playing)
11437     SendToServer_MovePlayer(summarized_player_action);
11438 #endif
11439
11440   // summarize all actions at local players mapped input device position
11441   // (this allows using different input devices in single player mode)
11442   if (!options.network && !game.team_mode)
11443     stored_player[map_player_action[local_player->index_nr]].effective_action =
11444       summarized_player_action;
11445
11446   if (tape.recording &&
11447       setup.team_mode &&
11448       setup.input_on_focus &&
11449       game.centered_player_nr != -1)
11450   {
11451     for (i = 0; i < MAX_PLAYERS; i++)
11452       stored_player[i].effective_action =
11453         (i == game.centered_player_nr ? summarized_player_action : 0);
11454   }
11455
11456   if (recorded_player_action != NULL)
11457     for (i = 0; i < MAX_PLAYERS; i++)
11458       stored_player[i].effective_action = recorded_player_action[i];
11459
11460   for (i = 0; i < MAX_PLAYERS; i++)
11461   {
11462     tape_action[i] = stored_player[i].effective_action;
11463
11464     /* (this may happen in the RND game engine if a player was not present on
11465        the playfield on level start, but appeared later from a custom element */
11466     if (setup.team_mode &&
11467         tape.recording &&
11468         tape_action[i] &&
11469         !tape.player_participates[i])
11470       tape.player_participates[i] = TRUE;
11471   }
11472
11473   SetTapeActionFromMouseAction(tape_action,
11474                                &local_player->effective_mouse_action);
11475
11476   /* only record actions from input devices, but not programmed actions */
11477   if (tape.recording)
11478     TapeRecordAction(tape_action);
11479
11480 #if USE_NEW_PLAYER_ASSIGNMENTS
11481   // !!! also map player actions in single player mode !!!
11482   // if (game.team_mode)
11483   if (1)
11484   {
11485     byte mapped_action[MAX_PLAYERS];
11486
11487 #if DEBUG_PLAYER_ACTIONS
11488     printf(":::");
11489     for (i = 0; i < MAX_PLAYERS; i++)
11490       printf(" %d, ", stored_player[i].effective_action);
11491 #endif
11492
11493     for (i = 0; i < MAX_PLAYERS; i++)
11494       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11495
11496     for (i = 0; i < MAX_PLAYERS; i++)
11497       stored_player[i].effective_action = mapped_action[i];
11498
11499 #if DEBUG_PLAYER_ACTIONS
11500     printf(" =>");
11501     for (i = 0; i < MAX_PLAYERS; i++)
11502       printf(" %d, ", stored_player[i].effective_action);
11503     printf("\n");
11504 #endif
11505   }
11506 #if DEBUG_PLAYER_ACTIONS
11507   else
11508   {
11509     printf(":::");
11510     for (i = 0; i < MAX_PLAYERS; i++)
11511       printf(" %d, ", stored_player[i].effective_action);
11512     printf("\n");
11513   }
11514 #endif
11515 #endif
11516
11517   for (i = 0; i < MAX_PLAYERS; i++)
11518   {
11519     // allow engine snapshot in case of changed movement attempt
11520     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11521         (stored_player[i].effective_action & KEY_MOTION))
11522       game.snapshot.changed_action = TRUE;
11523
11524     // allow engine snapshot in case of snapping/dropping attempt
11525     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11526         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11527       game.snapshot.changed_action = TRUE;
11528
11529     game.snapshot.last_action[i] = stored_player[i].effective_action;
11530   }
11531
11532   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11533   {
11534     GameActions_EM_Main();
11535   }
11536   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11537   {
11538     GameActions_SP_Main();
11539   }
11540   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11541   {
11542     GameActions_MM_Main();
11543   }
11544   else
11545   {
11546     GameActions_RND_Main();
11547   }
11548
11549   BlitScreenToBitmap(backbuffer);
11550
11551   CheckLevelTime();
11552
11553   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11554
11555   if (global.show_frames_per_second)
11556   {
11557     static unsigned int fps_counter = 0;
11558     static int fps_frames = 0;
11559     unsigned int fps_delay_ms = Counter() - fps_counter;
11560
11561     fps_frames++;
11562
11563     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11564     {
11565       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11566
11567       fps_frames = 0;
11568       fps_counter = Counter();
11569
11570       /* always draw FPS to screen after FPS value was updated */
11571       redraw_mask |= REDRAW_FPS;
11572     }
11573
11574     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11575     if (GetDrawDeactivationMask() == REDRAW_NONE)
11576       redraw_mask |= REDRAW_FPS;
11577   }
11578 }
11579
11580 static void GameActions_CheckSaveEngineSnapshot()
11581 {
11582   if (!game.snapshot.save_snapshot)
11583     return;
11584
11585   // clear flag for saving snapshot _before_ saving snapshot
11586   game.snapshot.save_snapshot = FALSE;
11587
11588   SaveEngineSnapshotToList();
11589 }
11590
11591 void GameActions()
11592 {
11593   GameActionsExt();
11594
11595   GameActions_CheckSaveEngineSnapshot();
11596 }
11597
11598 void GameActions_EM_Main()
11599 {
11600   byte effective_action[MAX_PLAYERS];
11601   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11602   int i;
11603
11604   for (i = 0; i < MAX_PLAYERS; i++)
11605     effective_action[i] = stored_player[i].effective_action;
11606
11607   GameActions_EM(effective_action, warp_mode);
11608 }
11609
11610 void GameActions_SP_Main()
11611 {
11612   byte effective_action[MAX_PLAYERS];
11613   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11614   int i;
11615
11616   for (i = 0; i < MAX_PLAYERS; i++)
11617     effective_action[i] = stored_player[i].effective_action;
11618
11619   GameActions_SP(effective_action, warp_mode);
11620
11621   for (i = 0; i < MAX_PLAYERS; i++)
11622   {
11623     if (stored_player[i].force_dropping)
11624       stored_player[i].action |= KEY_BUTTON_DROP;
11625
11626     stored_player[i].force_dropping = FALSE;
11627   }
11628 }
11629
11630 void GameActions_MM_Main()
11631 {
11632   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11633
11634   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11635 }
11636
11637 void GameActions_RND_Main()
11638 {
11639   GameActions_RND();
11640 }
11641
11642 void GameActions_RND()
11643 {
11644   int magic_wall_x = 0, magic_wall_y = 0;
11645   int i, x, y, element, graphic, last_gfx_frame;
11646
11647   InitPlayfieldScanModeVars();
11648
11649   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11650   {
11651     SCAN_PLAYFIELD(x, y)
11652     {
11653       ChangeCount[x][y] = 0;
11654       ChangeEvent[x][y] = -1;
11655     }
11656   }
11657
11658   if (game.set_centered_player)
11659   {
11660     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11661
11662     /* switching to "all players" only possible if all players fit to screen */
11663     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11664     {
11665       game.centered_player_nr_next = game.centered_player_nr;
11666       game.set_centered_player = FALSE;
11667     }
11668
11669     /* do not switch focus to non-existing (or non-active) player */
11670     if (game.centered_player_nr_next >= 0 &&
11671         !stored_player[game.centered_player_nr_next].active)
11672     {
11673       game.centered_player_nr_next = game.centered_player_nr;
11674       game.set_centered_player = FALSE;
11675     }
11676   }
11677
11678   if (game.set_centered_player &&
11679       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11680   {
11681     int sx, sy;
11682
11683     if (game.centered_player_nr_next == -1)
11684     {
11685       setScreenCenteredToAllPlayers(&sx, &sy);
11686     }
11687     else
11688     {
11689       sx = stored_player[game.centered_player_nr_next].jx;
11690       sy = stored_player[game.centered_player_nr_next].jy;
11691     }
11692
11693     game.centered_player_nr = game.centered_player_nr_next;
11694     game.set_centered_player = FALSE;
11695
11696     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11697     DrawGameDoorValues();
11698   }
11699
11700   for (i = 0; i < MAX_PLAYERS; i++)
11701   {
11702     int actual_player_action = stored_player[i].effective_action;
11703
11704 #if 1
11705     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11706        - rnd_equinox_tetrachloride 048
11707        - rnd_equinox_tetrachloride_ii 096
11708        - rnd_emanuel_schmieg 002
11709        - doctor_sloan_ww 001, 020
11710     */
11711     if (stored_player[i].MovPos == 0)
11712       CheckGravityMovement(&stored_player[i]);
11713 #endif
11714
11715     /* overwrite programmed action with tape action */
11716     if (stored_player[i].programmed_action)
11717       actual_player_action = stored_player[i].programmed_action;
11718
11719     PlayerActions(&stored_player[i], actual_player_action);
11720
11721     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11722   }
11723
11724   ScrollScreen(NULL, SCROLL_GO_ON);
11725
11726   /* for backwards compatibility, the following code emulates a fixed bug that
11727      occured when pushing elements (causing elements that just made their last
11728      pushing step to already (if possible) make their first falling step in the
11729      same game frame, which is bad); this code is also needed to use the famous
11730      "spring push bug" which is used in older levels and might be wanted to be
11731      used also in newer levels, but in this case the buggy pushing code is only
11732      affecting the "spring" element and no other elements */
11733
11734   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11735   {
11736     for (i = 0; i < MAX_PLAYERS; i++)
11737     {
11738       struct PlayerInfo *player = &stored_player[i];
11739       int x = player->jx;
11740       int y = player->jy;
11741
11742       if (player->active && player->is_pushing && player->is_moving &&
11743           IS_MOVING(x, y) &&
11744           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11745            Feld[x][y] == EL_SPRING))
11746       {
11747         ContinueMoving(x, y);
11748
11749         /* continue moving after pushing (this is actually a bug) */
11750         if (!IS_MOVING(x, y))
11751           Stop[x][y] = FALSE;
11752       }
11753     }
11754   }
11755
11756   SCAN_PLAYFIELD(x, y)
11757   {
11758     ChangeCount[x][y] = 0;
11759     ChangeEvent[x][y] = -1;
11760
11761     /* this must be handled before main playfield loop */
11762     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11763     {
11764       MovDelay[x][y]--;
11765       if (MovDelay[x][y] <= 0)
11766         RemoveField(x, y);
11767     }
11768
11769     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11770     {
11771       MovDelay[x][y]--;
11772       if (MovDelay[x][y] <= 0)
11773       {
11774         RemoveField(x, y);
11775         TEST_DrawLevelField(x, y);
11776
11777         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11778       }
11779     }
11780
11781 #if DEBUG
11782     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11783     {
11784       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11785       printf("GameActions(): This should never happen!\n");
11786
11787       ChangePage[x][y] = -1;
11788     }
11789 #endif
11790
11791     Stop[x][y] = FALSE;
11792     if (WasJustMoving[x][y] > 0)
11793       WasJustMoving[x][y]--;
11794     if (WasJustFalling[x][y] > 0)
11795       WasJustFalling[x][y]--;
11796     if (CheckCollision[x][y] > 0)
11797       CheckCollision[x][y]--;
11798     if (CheckImpact[x][y] > 0)
11799       CheckImpact[x][y]--;
11800
11801     GfxFrame[x][y]++;
11802
11803     /* reset finished pushing action (not done in ContinueMoving() to allow
11804        continuous pushing animation for elements with zero push delay) */
11805     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11806     {
11807       ResetGfxAnimation(x, y);
11808       TEST_DrawLevelField(x, y);
11809     }
11810
11811 #if DEBUG
11812     if (IS_BLOCKED(x, y))
11813     {
11814       int oldx, oldy;
11815
11816       Blocked2Moving(x, y, &oldx, &oldy);
11817       if (!IS_MOVING(oldx, oldy))
11818       {
11819         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11820         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11821         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11822         printf("GameActions(): This should never happen!\n");
11823       }
11824     }
11825 #endif
11826   }
11827
11828   SCAN_PLAYFIELD(x, y)
11829   {
11830     element = Feld[x][y];
11831     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11832     last_gfx_frame = GfxFrame[x][y];
11833
11834     ResetGfxFrame(x, y);
11835
11836     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11837       DrawLevelGraphicAnimation(x, y, graphic);
11838
11839     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11840         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11841       ResetRandomAnimationValue(x, y);
11842
11843     SetRandomAnimationValue(x, y);
11844
11845     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11846
11847     if (IS_INACTIVE(element))
11848     {
11849       if (IS_ANIMATED(graphic))
11850         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11851
11852       continue;
11853     }
11854
11855     /* this may take place after moving, so 'element' may have changed */
11856     if (IS_CHANGING(x, y) &&
11857         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11858     {
11859       int page = element_info[element].event_page_nr[CE_DELAY];
11860
11861       HandleElementChange(x, y, page);
11862
11863       element = Feld[x][y];
11864       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11865     }
11866
11867     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11868     {
11869       StartMoving(x, y);
11870
11871       element = Feld[x][y];
11872       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11873
11874       if (IS_ANIMATED(graphic) &&
11875           !IS_MOVING(x, y) &&
11876           !Stop[x][y])
11877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878
11879       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11880         TEST_DrawTwinkleOnField(x, y);
11881     }
11882     else if (element == EL_ACID)
11883     {
11884       if (!Stop[x][y])
11885         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11886     }
11887     else if ((element == EL_EXIT_OPEN ||
11888               element == EL_EM_EXIT_OPEN ||
11889               element == EL_SP_EXIT_OPEN ||
11890               element == EL_STEEL_EXIT_OPEN ||
11891               element == EL_EM_STEEL_EXIT_OPEN ||
11892               element == EL_SP_TERMINAL ||
11893               element == EL_SP_TERMINAL_ACTIVE ||
11894               element == EL_EXTRA_TIME ||
11895               element == EL_SHIELD_NORMAL ||
11896               element == EL_SHIELD_DEADLY) &&
11897              IS_ANIMATED(graphic))
11898       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11899     else if (IS_MOVING(x, y))
11900       ContinueMoving(x, y);
11901     else if (IS_ACTIVE_BOMB(element))
11902       CheckDynamite(x, y);
11903     else if (element == EL_AMOEBA_GROWING)
11904       AmoebeWaechst(x, y);
11905     else if (element == EL_AMOEBA_SHRINKING)
11906       AmoebaDisappearing(x, y);
11907
11908 #if !USE_NEW_AMOEBA_CODE
11909     else if (IS_AMOEBALIVE(element))
11910       AmoebeAbleger(x, y);
11911 #endif
11912
11913     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11914       Life(x, y);
11915     else if (element == EL_EXIT_CLOSED)
11916       CheckExit(x, y);
11917     else if (element == EL_EM_EXIT_CLOSED)
11918       CheckExitEM(x, y);
11919     else if (element == EL_STEEL_EXIT_CLOSED)
11920       CheckExitSteel(x, y);
11921     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11922       CheckExitSteelEM(x, y);
11923     else if (element == EL_SP_EXIT_CLOSED)
11924       CheckExitSP(x, y);
11925     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11926              element == EL_EXPANDABLE_STEELWALL_GROWING)
11927       MauerWaechst(x, y);
11928     else if (element == EL_EXPANDABLE_WALL ||
11929              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11930              element == EL_EXPANDABLE_WALL_VERTICAL ||
11931              element == EL_EXPANDABLE_WALL_ANY ||
11932              element == EL_BD_EXPANDABLE_WALL)
11933       MauerAbleger(x, y);
11934     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11935              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11936              element == EL_EXPANDABLE_STEELWALL_ANY)
11937       MauerAblegerStahl(x, y);
11938     else if (element == EL_FLAMES)
11939       CheckForDragon(x, y);
11940     else if (element == EL_EXPLOSION)
11941       ; /* drawing of correct explosion animation is handled separately */
11942     else if (element == EL_ELEMENT_SNAPPING ||
11943              element == EL_DIAGONAL_SHRINKING ||
11944              element == EL_DIAGONAL_GROWING)
11945     {
11946       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11947
11948       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11949     }
11950     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11951       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11952
11953     if (IS_BELT_ACTIVE(element))
11954       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11955
11956     if (game.magic_wall_active)
11957     {
11958       int jx = local_player->jx, jy = local_player->jy;
11959
11960       /* play the element sound at the position nearest to the player */
11961       if ((element == EL_MAGIC_WALL_FULL ||
11962            element == EL_MAGIC_WALL_ACTIVE ||
11963            element == EL_MAGIC_WALL_EMPTYING ||
11964            element == EL_BD_MAGIC_WALL_FULL ||
11965            element == EL_BD_MAGIC_WALL_ACTIVE ||
11966            element == EL_BD_MAGIC_WALL_EMPTYING ||
11967            element == EL_DC_MAGIC_WALL_FULL ||
11968            element == EL_DC_MAGIC_WALL_ACTIVE ||
11969            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11970           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11971       {
11972         magic_wall_x = x;
11973         magic_wall_y = y;
11974       }
11975     }
11976   }
11977
11978 #if USE_NEW_AMOEBA_CODE
11979   /* new experimental amoeba growth stuff */
11980   if (!(FrameCounter % 8))
11981   {
11982     static unsigned int random = 1684108901;
11983
11984     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11985     {
11986       x = RND(lev_fieldx);
11987       y = RND(lev_fieldy);
11988       element = Feld[x][y];
11989
11990       if (!IS_PLAYER(x,y) &&
11991           (element == EL_EMPTY ||
11992            CAN_GROW_INTO(element) ||
11993            element == EL_QUICKSAND_EMPTY ||
11994            element == EL_QUICKSAND_FAST_EMPTY ||
11995            element == EL_ACID_SPLASH_LEFT ||
11996            element == EL_ACID_SPLASH_RIGHT))
11997       {
11998         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11999             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12000             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12001             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12002           Feld[x][y] = EL_AMOEBA_DROP;
12003       }
12004
12005       random = random * 129 + 1;
12006     }
12007   }
12008 #endif
12009
12010   game.explosions_delayed = FALSE;
12011
12012   SCAN_PLAYFIELD(x, y)
12013   {
12014     element = Feld[x][y];
12015
12016     if (ExplodeField[x][y])
12017       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12018     else if (element == EL_EXPLOSION)
12019       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12020
12021     ExplodeField[x][y] = EX_TYPE_NONE;
12022   }
12023
12024   game.explosions_delayed = TRUE;
12025
12026   if (game.magic_wall_active)
12027   {
12028     if (!(game.magic_wall_time_left % 4))
12029     {
12030       int element = Feld[magic_wall_x][magic_wall_y];
12031
12032       if (element == EL_BD_MAGIC_WALL_FULL ||
12033           element == EL_BD_MAGIC_WALL_ACTIVE ||
12034           element == EL_BD_MAGIC_WALL_EMPTYING)
12035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12036       else if (element == EL_DC_MAGIC_WALL_FULL ||
12037                element == EL_DC_MAGIC_WALL_ACTIVE ||
12038                element == EL_DC_MAGIC_WALL_EMPTYING)
12039         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12040       else
12041         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12042     }
12043
12044     if (game.magic_wall_time_left > 0)
12045     {
12046       game.magic_wall_time_left--;
12047
12048       if (!game.magic_wall_time_left)
12049       {
12050         SCAN_PLAYFIELD(x, y)
12051         {
12052           element = Feld[x][y];
12053
12054           if (element == EL_MAGIC_WALL_ACTIVE ||
12055               element == EL_MAGIC_WALL_FULL)
12056           {
12057             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12058             TEST_DrawLevelField(x, y);
12059           }
12060           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12061                    element == EL_BD_MAGIC_WALL_FULL)
12062           {
12063             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12064             TEST_DrawLevelField(x, y);
12065           }
12066           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12067                    element == EL_DC_MAGIC_WALL_FULL)
12068           {
12069             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12070             TEST_DrawLevelField(x, y);
12071           }
12072         }
12073
12074         game.magic_wall_active = FALSE;
12075       }
12076     }
12077   }
12078
12079   if (game.light_time_left > 0)
12080   {
12081     game.light_time_left--;
12082
12083     if (game.light_time_left == 0)
12084       RedrawAllLightSwitchesAndInvisibleElements();
12085   }
12086
12087   if (game.timegate_time_left > 0)
12088   {
12089     game.timegate_time_left--;
12090
12091     if (game.timegate_time_left == 0)
12092       CloseAllOpenTimegates();
12093   }
12094
12095   if (game.lenses_time_left > 0)
12096   {
12097     game.lenses_time_left--;
12098
12099     if (game.lenses_time_left == 0)
12100       RedrawAllInvisibleElementsForLenses();
12101   }
12102
12103   if (game.magnify_time_left > 0)
12104   {
12105     game.magnify_time_left--;
12106
12107     if (game.magnify_time_left == 0)
12108       RedrawAllInvisibleElementsForMagnifier();
12109   }
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112   {
12113     struct PlayerInfo *player = &stored_player[i];
12114
12115     if (SHIELD_ON(player))
12116     {
12117       if (player->shield_deadly_time_left)
12118         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12119       else if (player->shield_normal_time_left)
12120         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12121     }
12122   }
12123
12124 #if USE_DELAYED_GFX_REDRAW
12125   SCAN_PLAYFIELD(x, y)
12126   {
12127     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12128     {
12129       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12130          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12131
12132       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12133         DrawLevelField(x, y);
12134
12135       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12136         DrawLevelFieldCrumbled(x, y);
12137
12138       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12139         DrawLevelFieldCrumbledNeighbours(x, y);
12140
12141       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12142         DrawTwinkleOnField(x, y);
12143     }
12144
12145     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12146   }
12147 #endif
12148
12149   DrawAllPlayers();
12150   PlayAllPlayersSound();
12151
12152   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12153   {
12154     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12155
12156     local_player->show_envelope = 0;
12157   }
12158
12159   /* use random number generator in every frame to make it less predictable */
12160   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12161     RND(1);
12162 }
12163
12164 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12165 {
12166   int min_x = x, min_y = y, max_x = x, max_y = y;
12167   int i;
12168
12169   for (i = 0; i < MAX_PLAYERS; i++)
12170   {
12171     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12172
12173     if (!stored_player[i].active || &stored_player[i] == player)
12174       continue;
12175
12176     min_x = MIN(min_x, jx);
12177     min_y = MIN(min_y, jy);
12178     max_x = MAX(max_x, jx);
12179     max_y = MAX(max_y, jy);
12180   }
12181
12182   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12183 }
12184
12185 static boolean AllPlayersInVisibleScreen()
12186 {
12187   int i;
12188
12189   for (i = 0; i < MAX_PLAYERS; i++)
12190   {
12191     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12192
12193     if (!stored_player[i].active)
12194       continue;
12195
12196     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12197       return FALSE;
12198   }
12199
12200   return TRUE;
12201 }
12202
12203 void ScrollLevel(int dx, int dy)
12204 {
12205   int scroll_offset = 2 * TILEX_VAR;
12206   int x, y;
12207
12208   BlitBitmap(drawto_field, drawto_field,
12209              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12210              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12211              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12212              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12213              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12214              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12215
12216   if (dx != 0)
12217   {
12218     x = (dx == 1 ? BX1 : BX2);
12219     for (y = BY1; y <= BY2; y++)
12220       DrawScreenField(x, y);
12221   }
12222
12223   if (dy != 0)
12224   {
12225     y = (dy == 1 ? BY1 : BY2);
12226     for (x = BX1; x <= BX2; x++)
12227       DrawScreenField(x, y);
12228   }
12229
12230   redraw_mask |= REDRAW_FIELD;
12231 }
12232
12233 static boolean canFallDown(struct PlayerInfo *player)
12234 {
12235   int jx = player->jx, jy = player->jy;
12236
12237   return (IN_LEV_FIELD(jx, jy + 1) &&
12238           (IS_FREE(jx, jy + 1) ||
12239            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12240           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12241           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12242 }
12243
12244 static boolean canPassField(int x, int y, int move_dir)
12245 {
12246   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12247   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12248   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12249   int nextx = x + dx;
12250   int nexty = y + dy;
12251   int element = Feld[x][y];
12252
12253   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12254           !CAN_MOVE(element) &&
12255           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12256           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12257           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12258 }
12259
12260 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12261 {
12262   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12263   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12264   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12265   int newx = x + dx;
12266   int newy = y + dy;
12267
12268   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12269           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12270           (IS_DIGGABLE(Feld[newx][newy]) ||
12271            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12272            canPassField(newx, newy, move_dir)));
12273 }
12274
12275 static void CheckGravityMovement(struct PlayerInfo *player)
12276 {
12277   if (player->gravity && !player->programmed_action)
12278   {
12279     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12280     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12281     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12282     int jx = player->jx, jy = player->jy;
12283     boolean player_is_moving_to_valid_field =
12284       (!player_is_snapping &&
12285        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12286         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12287     boolean player_can_fall_down = canFallDown(player);
12288
12289     if (player_can_fall_down &&
12290         !player_is_moving_to_valid_field)
12291       player->programmed_action = MV_DOWN;
12292   }
12293 }
12294
12295 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12296 {
12297   return CheckGravityMovement(player);
12298
12299   if (player->gravity && !player->programmed_action)
12300   {
12301     int jx = player->jx, jy = player->jy;
12302     boolean field_under_player_is_free =
12303       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12304     boolean player_is_standing_on_valid_field =
12305       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12306        (IS_WALKABLE(Feld[jx][jy]) &&
12307         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12308
12309     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12310       player->programmed_action = MV_DOWN;
12311   }
12312 }
12313
12314 /*
12315   MovePlayerOneStep()
12316   -----------------------------------------------------------------------------
12317   dx, dy:               direction (non-diagonal) to try to move the player to
12318   real_dx, real_dy:     direction as read from input device (can be diagonal)
12319 */
12320
12321 boolean MovePlayerOneStep(struct PlayerInfo *player,
12322                           int dx, int dy, int real_dx, int real_dy)
12323 {
12324   int jx = player->jx, jy = player->jy;
12325   int new_jx = jx + dx, new_jy = jy + dy;
12326   int can_move;
12327   boolean player_can_move = !player->cannot_move;
12328
12329   if (!player->active || (!dx && !dy))
12330     return MP_NO_ACTION;
12331
12332   player->MovDir = (dx < 0 ? MV_LEFT :
12333                     dx > 0 ? MV_RIGHT :
12334                     dy < 0 ? MV_UP :
12335                     dy > 0 ? MV_DOWN :  MV_NONE);
12336
12337   if (!IN_LEV_FIELD(new_jx, new_jy))
12338     return MP_NO_ACTION;
12339
12340   if (!player_can_move)
12341   {
12342     if (player->MovPos == 0)
12343     {
12344       player->is_moving = FALSE;
12345       player->is_digging = FALSE;
12346       player->is_collecting = FALSE;
12347       player->is_snapping = FALSE;
12348       player->is_pushing = FALSE;
12349     }
12350   }
12351
12352   if (!options.network && game.centered_player_nr == -1 &&
12353       !AllPlayersInSight(player, new_jx, new_jy))
12354     return MP_NO_ACTION;
12355
12356   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12357   if (can_move != MP_MOVING)
12358     return can_move;
12359
12360   /* check if DigField() has caused relocation of the player */
12361   if (player->jx != jx || player->jy != jy)
12362     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12363
12364   StorePlayer[jx][jy] = 0;
12365   player->last_jx = jx;
12366   player->last_jy = jy;
12367   player->jx = new_jx;
12368   player->jy = new_jy;
12369   StorePlayer[new_jx][new_jy] = player->element_nr;
12370
12371   if (player->move_delay_value_next != -1)
12372   {
12373     player->move_delay_value = player->move_delay_value_next;
12374     player->move_delay_value_next = -1;
12375   }
12376
12377   player->MovPos =
12378     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12379
12380   player->step_counter++;
12381
12382   PlayerVisit[jx][jy] = FrameCounter;
12383
12384   player->is_moving = TRUE;
12385
12386 #if 1
12387   /* should better be called in MovePlayer(), but this breaks some tapes */
12388   ScrollPlayer(player, SCROLL_INIT);
12389 #endif
12390
12391   return MP_MOVING;
12392 }
12393
12394 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12395 {
12396   int jx = player->jx, jy = player->jy;
12397   int old_jx = jx, old_jy = jy;
12398   int moved = MP_NO_ACTION;
12399
12400   if (!player->active)
12401     return FALSE;
12402
12403   if (!dx && !dy)
12404   {
12405     if (player->MovPos == 0)
12406     {
12407       player->is_moving = FALSE;
12408       player->is_digging = FALSE;
12409       player->is_collecting = FALSE;
12410       player->is_snapping = FALSE;
12411       player->is_pushing = FALSE;
12412     }
12413
12414     return FALSE;
12415   }
12416
12417   if (player->move_delay > 0)
12418     return FALSE;
12419
12420   player->move_delay = -1;              /* set to "uninitialized" value */
12421
12422   /* store if player is automatically moved to next field */
12423   player->is_auto_moving = (player->programmed_action != MV_NONE);
12424
12425   /* remove the last programmed player action */
12426   player->programmed_action = 0;
12427
12428   if (player->MovPos)
12429   {
12430     /* should only happen if pre-1.2 tape recordings are played */
12431     /* this is only for backward compatibility */
12432
12433     int original_move_delay_value = player->move_delay_value;
12434
12435 #if DEBUG
12436     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12437            tape.counter);
12438 #endif
12439
12440     /* scroll remaining steps with finest movement resolution */
12441     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12442
12443     while (player->MovPos)
12444     {
12445       ScrollPlayer(player, SCROLL_GO_ON);
12446       ScrollScreen(NULL, SCROLL_GO_ON);
12447
12448       AdvanceFrameAndPlayerCounters(player->index_nr);
12449
12450       DrawAllPlayers();
12451       BackToFront_WithFrameDelay(0);
12452     }
12453
12454     player->move_delay_value = original_move_delay_value;
12455   }
12456
12457   player->is_active = FALSE;
12458
12459   if (player->last_move_dir & MV_HORIZONTAL)
12460   {
12461     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12462       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12463   }
12464   else
12465   {
12466     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12467       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12468   }
12469
12470   if (!moved && !player->is_active)
12471   {
12472     player->is_moving = FALSE;
12473     player->is_digging = FALSE;
12474     player->is_collecting = FALSE;
12475     player->is_snapping = FALSE;
12476     player->is_pushing = FALSE;
12477   }
12478
12479   jx = player->jx;
12480   jy = player->jy;
12481
12482   if (moved & MP_MOVING && !ScreenMovPos &&
12483       (player->index_nr == game.centered_player_nr ||
12484        game.centered_player_nr == -1))
12485   {
12486     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12487     int offset = game.scroll_delay_value;
12488
12489     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12490     {
12491       /* actual player has left the screen -- scroll in that direction */
12492       if (jx != old_jx)         /* player has moved horizontally */
12493         scroll_x += (jx - old_jx);
12494       else                      /* player has moved vertically */
12495         scroll_y += (jy - old_jy);
12496     }
12497     else
12498     {
12499       if (jx != old_jx)         /* player has moved horizontally */
12500       {
12501         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12502             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12503           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12504
12505         /* don't scroll over playfield boundaries */
12506         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12507           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12508
12509         /* don't scroll more than one field at a time */
12510         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12511
12512         /* don't scroll against the player's moving direction */
12513         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12514             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12515           scroll_x = old_scroll_x;
12516       }
12517       else                      /* player has moved vertically */
12518       {
12519         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12520             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12521           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12522
12523         /* don't scroll over playfield boundaries */
12524         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12525           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12526
12527         /* don't scroll more than one field at a time */
12528         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12529
12530         /* don't scroll against the player's moving direction */
12531         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12532             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12533           scroll_y = old_scroll_y;
12534       }
12535     }
12536
12537     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12538     {
12539       if (!options.network && game.centered_player_nr == -1 &&
12540           !AllPlayersInVisibleScreen())
12541       {
12542         scroll_x = old_scroll_x;
12543         scroll_y = old_scroll_y;
12544       }
12545       else
12546       {
12547         ScrollScreen(player, SCROLL_INIT);
12548         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12549       }
12550     }
12551   }
12552
12553   player->StepFrame = 0;
12554
12555   if (moved & MP_MOVING)
12556   {
12557     if (old_jx != jx && old_jy == jy)
12558       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12559     else if (old_jx == jx && old_jy != jy)
12560       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12561
12562     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12563
12564     player->last_move_dir = player->MovDir;
12565     player->is_moving = TRUE;
12566     player->is_snapping = FALSE;
12567     player->is_switching = FALSE;
12568     player->is_dropping = FALSE;
12569     player->is_dropping_pressed = FALSE;
12570     player->drop_pressed_delay = 0;
12571
12572 #if 0
12573     /* should better be called here than above, but this breaks some tapes */
12574     ScrollPlayer(player, SCROLL_INIT);
12575 #endif
12576   }
12577   else
12578   {
12579     CheckGravityMovementWhenNotMoving(player);
12580
12581     player->is_moving = FALSE;
12582
12583     /* at this point, the player is allowed to move, but cannot move right now
12584        (e.g. because of something blocking the way) -- ensure that the player
12585        is also allowed to move in the next frame (in old versions before 3.1.1,
12586        the player was forced to wait again for eight frames before next try) */
12587
12588     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12589       player->move_delay = 0;   /* allow direct movement in the next frame */
12590   }
12591
12592   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12593     player->move_delay = player->move_delay_value;
12594
12595   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12596   {
12597     TestIfPlayerTouchesBadThing(jx, jy);
12598     TestIfPlayerTouchesCustomElement(jx, jy);
12599   }
12600
12601   if (!player->active)
12602     RemovePlayer(player);
12603
12604   return moved;
12605 }
12606
12607 void ScrollPlayer(struct PlayerInfo *player, int mode)
12608 {
12609   int jx = player->jx, jy = player->jy;
12610   int last_jx = player->last_jx, last_jy = player->last_jy;
12611   int move_stepsize = TILEX / player->move_delay_value;
12612
12613   if (!player->active)
12614     return;
12615
12616   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12617     return;
12618
12619   if (mode == SCROLL_INIT)
12620   {
12621     player->actual_frame_counter = FrameCounter;
12622     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12623
12624     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12625         Feld[last_jx][last_jy] == EL_EMPTY)
12626     {
12627       int last_field_block_delay = 0;   /* start with no blocking at all */
12628       int block_delay_adjustment = player->block_delay_adjustment;
12629
12630       /* if player blocks last field, add delay for exactly one move */
12631       if (player->block_last_field)
12632       {
12633         last_field_block_delay += player->move_delay_value;
12634
12635         /* when blocking enabled, prevent moving up despite gravity */
12636         if (player->gravity && player->MovDir == MV_UP)
12637           block_delay_adjustment = -1;
12638       }
12639
12640       /* add block delay adjustment (also possible when not blocking) */
12641       last_field_block_delay += block_delay_adjustment;
12642
12643       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12644       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12645     }
12646
12647     if (player->MovPos != 0)    /* player has not yet reached destination */
12648       return;
12649   }
12650   else if (!FrameReached(&player->actual_frame_counter, 1))
12651     return;
12652
12653   if (player->MovPos != 0)
12654   {
12655     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12656     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12657
12658     /* before DrawPlayer() to draw correct player graphic for this case */
12659     if (player->MovPos == 0)
12660       CheckGravityMovement(player);
12661   }
12662
12663   if (player->MovPos == 0)      /* player reached destination field */
12664   {
12665     if (player->move_delay_reset_counter > 0)
12666     {
12667       player->move_delay_reset_counter--;
12668
12669       if (player->move_delay_reset_counter == 0)
12670       {
12671         /* continue with normal speed after quickly moving through gate */
12672         HALVE_PLAYER_SPEED(player);
12673
12674         /* be able to make the next move without delay */
12675         player->move_delay = 0;
12676       }
12677     }
12678
12679     player->last_jx = jx;
12680     player->last_jy = jy;
12681
12682     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12684         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12685         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12686         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12687         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12688         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12689         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12690     {
12691       DrawPlayer(player);       /* needed here only to cleanup last field */
12692       RemovePlayer(player);
12693
12694       if (local_player->friends_still_needed == 0 ||
12695           IS_SP_ELEMENT(Feld[jx][jy]))
12696         PlayerWins(player);
12697     }
12698
12699     /* this breaks one level: "machine", level 000 */
12700     {
12701       int move_direction = player->MovDir;
12702       int enter_side = MV_DIR_OPPOSITE(move_direction);
12703       int leave_side = move_direction;
12704       int old_jx = last_jx;
12705       int old_jy = last_jy;
12706       int old_element = Feld[old_jx][old_jy];
12707       int new_element = Feld[jx][jy];
12708
12709       if (IS_CUSTOM_ELEMENT(old_element))
12710         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12711                                    CE_LEFT_BY_PLAYER,
12712                                    player->index_bit, leave_side);
12713
12714       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12715                                           CE_PLAYER_LEAVES_X,
12716                                           player->index_bit, leave_side);
12717
12718       if (IS_CUSTOM_ELEMENT(new_element))
12719         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12720                                    player->index_bit, enter_side);
12721
12722       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12723                                           CE_PLAYER_ENTERS_X,
12724                                           player->index_bit, enter_side);
12725
12726       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12727                                         CE_MOVE_OF_X, move_direction);
12728     }
12729
12730     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12731     {
12732       TestIfPlayerTouchesBadThing(jx, jy);
12733       TestIfPlayerTouchesCustomElement(jx, jy);
12734
12735       /* needed because pushed element has not yet reached its destination,
12736          so it would trigger a change event at its previous field location */
12737       if (!player->is_pushing)
12738         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12739
12740       if (!player->active)
12741         RemovePlayer(player);
12742     }
12743
12744     if (!local_player->LevelSolved && level.use_step_counter)
12745     {
12746       int i;
12747
12748       TimePlayed++;
12749
12750       if (TimeLeft > 0)
12751       {
12752         TimeLeft--;
12753
12754         if (TimeLeft <= 10 && setup.time_limit)
12755           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12756
12757         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12758
12759         DisplayGameControlValues();
12760
12761         if (!TimeLeft && setup.time_limit)
12762           for (i = 0; i < MAX_PLAYERS; i++)
12763             KillPlayer(&stored_player[i]);
12764       }
12765       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12766       {
12767         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12768
12769         DisplayGameControlValues();
12770       }
12771     }
12772
12773     if (tape.single_step && tape.recording && !tape.pausing &&
12774         !player->programmed_action)
12775       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12776
12777     if (!player->programmed_action)
12778       CheckSaveEngineSnapshot(player);
12779   }
12780 }
12781
12782 void ScrollScreen(struct PlayerInfo *player, int mode)
12783 {
12784   static unsigned int screen_frame_counter = 0;
12785
12786   if (mode == SCROLL_INIT)
12787   {
12788     /* set scrolling step size according to actual player's moving speed */
12789     ScrollStepSize = TILEX / player->move_delay_value;
12790
12791     screen_frame_counter = FrameCounter;
12792     ScreenMovDir = player->MovDir;
12793     ScreenMovPos = player->MovPos;
12794     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12795     return;
12796   }
12797   else if (!FrameReached(&screen_frame_counter, 1))
12798     return;
12799
12800   if (ScreenMovPos)
12801   {
12802     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12803     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12804     redraw_mask |= REDRAW_FIELD;
12805   }
12806   else
12807     ScreenMovDir = MV_NONE;
12808 }
12809
12810 void TestIfPlayerTouchesCustomElement(int x, int y)
12811 {
12812   static int xy[4][2] =
12813   {
12814     { 0, -1 },
12815     { -1, 0 },
12816     { +1, 0 },
12817     { 0, +1 }
12818   };
12819   static int trigger_sides[4][2] =
12820   {
12821     /* center side       border side */
12822     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12823     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12824     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12825     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12826   };
12827   static int touch_dir[4] =
12828   {
12829     MV_LEFT | MV_RIGHT,
12830     MV_UP   | MV_DOWN,
12831     MV_UP   | MV_DOWN,
12832     MV_LEFT | MV_RIGHT
12833   };
12834   int center_element = Feld[x][y];      /* should always be non-moving! */
12835   int i;
12836
12837   for (i = 0; i < NUM_DIRECTIONS; i++)
12838   {
12839     int xx = x + xy[i][0];
12840     int yy = y + xy[i][1];
12841     int center_side = trigger_sides[i][0];
12842     int border_side = trigger_sides[i][1];
12843     int border_element;
12844
12845     if (!IN_LEV_FIELD(xx, yy))
12846       continue;
12847
12848     if (IS_PLAYER(x, y))                /* player found at center element */
12849     {
12850       struct PlayerInfo *player = PLAYERINFO(x, y);
12851
12852       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12853         border_element = Feld[xx][yy];          /* may be moving! */
12854       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12855         border_element = Feld[xx][yy];
12856       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12857         border_element = MovingOrBlocked2Element(xx, yy);
12858       else
12859         continue;               /* center and border element do not touch */
12860
12861       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12862                                  player->index_bit, border_side);
12863       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12864                                           CE_PLAYER_TOUCHES_X,
12865                                           player->index_bit, border_side);
12866
12867       {
12868         /* use player element that is initially defined in the level playfield,
12869            not the player element that corresponds to the runtime player number
12870            (example: a level that contains EL_PLAYER_3 as the only player would
12871            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12872         int player_element = PLAYERINFO(x, y)->initial_element;
12873
12874         CheckElementChangeBySide(xx, yy, border_element, player_element,
12875                                  CE_TOUCHING_X, border_side);
12876       }
12877     }
12878     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12879     {
12880       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12881
12882       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12883       {
12884         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12885           continue;             /* center and border element do not touch */
12886       }
12887
12888       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12889                                  player->index_bit, center_side);
12890       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12891                                           CE_PLAYER_TOUCHES_X,
12892                                           player->index_bit, center_side);
12893
12894       {
12895         /* use player element that is initially defined in the level playfield,
12896            not the player element that corresponds to the runtime player number
12897            (example: a level that contains EL_PLAYER_3 as the only player would
12898            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12899         int player_element = PLAYERINFO(xx, yy)->initial_element;
12900
12901         CheckElementChangeBySide(x, y, center_element, player_element,
12902                                  CE_TOUCHING_X, center_side);
12903       }
12904
12905       break;
12906     }
12907   }
12908 }
12909
12910 void TestIfElementTouchesCustomElement(int x, int y)
12911 {
12912   static int xy[4][2] =
12913   {
12914     { 0, -1 },
12915     { -1, 0 },
12916     { +1, 0 },
12917     { 0, +1 }
12918   };
12919   static int trigger_sides[4][2] =
12920   {
12921     /* center side      border side */
12922     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12923     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12924     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12925     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12926   };
12927   static int touch_dir[4] =
12928   {
12929     MV_LEFT | MV_RIGHT,
12930     MV_UP   | MV_DOWN,
12931     MV_UP   | MV_DOWN,
12932     MV_LEFT | MV_RIGHT
12933   };
12934   boolean change_center_element = FALSE;
12935   int center_element = Feld[x][y];      /* should always be non-moving! */
12936   int border_element_old[NUM_DIRECTIONS];
12937   int i;
12938
12939   for (i = 0; i < NUM_DIRECTIONS; i++)
12940   {
12941     int xx = x + xy[i][0];
12942     int yy = y + xy[i][1];
12943     int border_element;
12944
12945     border_element_old[i] = -1;
12946
12947     if (!IN_LEV_FIELD(xx, yy))
12948       continue;
12949
12950     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12951       border_element = Feld[xx][yy];    /* may be moving! */
12952     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12953       border_element = Feld[xx][yy];
12954     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12955       border_element = MovingOrBlocked2Element(xx, yy);
12956     else
12957       continue;                 /* center and border element do not touch */
12958
12959     border_element_old[i] = border_element;
12960   }
12961
12962   for (i = 0; i < NUM_DIRECTIONS; i++)
12963   {
12964     int xx = x + xy[i][0];
12965     int yy = y + xy[i][1];
12966     int center_side = trigger_sides[i][0];
12967     int border_element = border_element_old[i];
12968
12969     if (border_element == -1)
12970       continue;
12971
12972     /* check for change of border element */
12973     CheckElementChangeBySide(xx, yy, border_element, center_element,
12974                              CE_TOUCHING_X, center_side);
12975
12976     /* (center element cannot be player, so we dont have to check this here) */
12977   }
12978
12979   for (i = 0; i < NUM_DIRECTIONS; i++)
12980   {
12981     int xx = x + xy[i][0];
12982     int yy = y + xy[i][1];
12983     int border_side = trigger_sides[i][1];
12984     int border_element = border_element_old[i];
12985
12986     if (border_element == -1)
12987       continue;
12988
12989     /* check for change of center element (but change it only once) */
12990     if (!change_center_element)
12991       change_center_element =
12992         CheckElementChangeBySide(x, y, center_element, border_element,
12993                                  CE_TOUCHING_X, border_side);
12994
12995     if (IS_PLAYER(xx, yy))
12996     {
12997       /* use player element that is initially defined in the level playfield,
12998          not the player element that corresponds to the runtime player number
12999          (example: a level that contains EL_PLAYER_3 as the only player would
13000          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13001       int player_element = PLAYERINFO(xx, yy)->initial_element;
13002
13003       CheckElementChangeBySide(x, y, center_element, player_element,
13004                                CE_TOUCHING_X, border_side);
13005     }
13006   }
13007 }
13008
13009 void TestIfElementHitsCustomElement(int x, int y, int direction)
13010 {
13011   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13012   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13013   int hitx = x + dx, hity = y + dy;
13014   int hitting_element = Feld[x][y];
13015   int touched_element;
13016
13017   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13018     return;
13019
13020   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13021                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13022
13023   if (IN_LEV_FIELD(hitx, hity))
13024   {
13025     int opposite_direction = MV_DIR_OPPOSITE(direction);
13026     int hitting_side = direction;
13027     int touched_side = opposite_direction;
13028     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13029                           MovDir[hitx][hity] != direction ||
13030                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13031
13032     object_hit = TRUE;
13033
13034     if (object_hit)
13035     {
13036       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13037                                CE_HITTING_X, touched_side);
13038
13039       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13040                                CE_HIT_BY_X, hitting_side);
13041
13042       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13043                                CE_HIT_BY_SOMETHING, opposite_direction);
13044
13045       if (IS_PLAYER(hitx, hity))
13046       {
13047         /* use player element that is initially defined in the level playfield,
13048            not the player element that corresponds to the runtime player number
13049            (example: a level that contains EL_PLAYER_3 as the only player would
13050            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13051         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13052
13053         CheckElementChangeBySide(x, y, hitting_element, player_element,
13054                                  CE_HITTING_X, touched_side);
13055       }
13056     }
13057   }
13058
13059   /* "hitting something" is also true when hitting the playfield border */
13060   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13061                            CE_HITTING_SOMETHING, direction);
13062 }
13063
13064 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13065 {
13066   int i, kill_x = -1, kill_y = -1;
13067
13068   int bad_element = -1;
13069   static int test_xy[4][2] =
13070   {
13071     { 0, -1 },
13072     { -1, 0 },
13073     { +1, 0 },
13074     { 0, +1 }
13075   };
13076   static int test_dir[4] =
13077   {
13078     MV_UP,
13079     MV_LEFT,
13080     MV_RIGHT,
13081     MV_DOWN
13082   };
13083
13084   for (i = 0; i < NUM_DIRECTIONS; i++)
13085   {
13086     int test_x, test_y, test_move_dir, test_element;
13087
13088     test_x = good_x + test_xy[i][0];
13089     test_y = good_y + test_xy[i][1];
13090
13091     if (!IN_LEV_FIELD(test_x, test_y))
13092       continue;
13093
13094     test_move_dir =
13095       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13096
13097     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13098
13099     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13100        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13101     */
13102     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13103         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13104     {
13105       kill_x = test_x;
13106       kill_y = test_y;
13107       bad_element = test_element;
13108
13109       break;
13110     }
13111   }
13112
13113   if (kill_x != -1 || kill_y != -1)
13114   {
13115     if (IS_PLAYER(good_x, good_y))
13116     {
13117       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13118
13119       if (player->shield_deadly_time_left > 0 &&
13120           !IS_INDESTRUCTIBLE(bad_element))
13121         Bang(kill_x, kill_y);
13122       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13123         KillPlayer(player);
13124     }
13125     else
13126       Bang(good_x, good_y);
13127   }
13128 }
13129
13130 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13131 {
13132   int i, kill_x = -1, kill_y = -1;
13133   int bad_element = Feld[bad_x][bad_y];
13134   static int test_xy[4][2] =
13135   {
13136     { 0, -1 },
13137     { -1, 0 },
13138     { +1, 0 },
13139     { 0, +1 }
13140   };
13141   static int touch_dir[4] =
13142   {
13143     MV_LEFT | MV_RIGHT,
13144     MV_UP   | MV_DOWN,
13145     MV_UP   | MV_DOWN,
13146     MV_LEFT | MV_RIGHT
13147   };
13148   static int test_dir[4] =
13149   {
13150     MV_UP,
13151     MV_LEFT,
13152     MV_RIGHT,
13153     MV_DOWN
13154   };
13155
13156   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13157     return;
13158
13159   for (i = 0; i < NUM_DIRECTIONS; i++)
13160   {
13161     int test_x, test_y, test_move_dir, test_element;
13162
13163     test_x = bad_x + test_xy[i][0];
13164     test_y = bad_y + test_xy[i][1];
13165
13166     if (!IN_LEV_FIELD(test_x, test_y))
13167       continue;
13168
13169     test_move_dir =
13170       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13171
13172     test_element = Feld[test_x][test_y];
13173
13174     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13175        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13176     */
13177     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13178         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13179     {
13180       /* good thing is player or penguin that does not move away */
13181       if (IS_PLAYER(test_x, test_y))
13182       {
13183         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13184
13185         if (bad_element == EL_ROBOT && player->is_moving)
13186           continue;     /* robot does not kill player if he is moving */
13187
13188         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13189         {
13190           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13191             continue;           /* center and border element do not touch */
13192         }
13193
13194         kill_x = test_x;
13195         kill_y = test_y;
13196
13197         break;
13198       }
13199       else if (test_element == EL_PENGUIN)
13200       {
13201         kill_x = test_x;
13202         kill_y = test_y;
13203
13204         break;
13205       }
13206     }
13207   }
13208
13209   if (kill_x != -1 || kill_y != -1)
13210   {
13211     if (IS_PLAYER(kill_x, kill_y))
13212     {
13213       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13214
13215       if (player->shield_deadly_time_left > 0 &&
13216           !IS_INDESTRUCTIBLE(bad_element))
13217         Bang(bad_x, bad_y);
13218       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13219         KillPlayer(player);
13220     }
13221     else
13222       Bang(kill_x, kill_y);
13223   }
13224 }
13225
13226 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13227 {
13228   int bad_element = Feld[bad_x][bad_y];
13229   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13230   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13231   int test_x = bad_x + dx, test_y = bad_y + dy;
13232   int test_move_dir, test_element;
13233   int kill_x = -1, kill_y = -1;
13234
13235   if (!IN_LEV_FIELD(test_x, test_y))
13236     return;
13237
13238   test_move_dir =
13239     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13240
13241   test_element = Feld[test_x][test_y];
13242
13243   if (test_move_dir != bad_move_dir)
13244   {
13245     /* good thing can be player or penguin that does not move away */
13246     if (IS_PLAYER(test_x, test_y))
13247     {
13248       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13249
13250       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13251          player as being hit when he is moving towards the bad thing, because
13252          the "get hit by" condition would be lost after the player stops) */
13253       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13254         return;         /* player moves away from bad thing */
13255
13256       kill_x = test_x;
13257       kill_y = test_y;
13258     }
13259     else if (test_element == EL_PENGUIN)
13260     {
13261       kill_x = test_x;
13262       kill_y = test_y;
13263     }
13264   }
13265
13266   if (kill_x != -1 || kill_y != -1)
13267   {
13268     if (IS_PLAYER(kill_x, kill_y))
13269     {
13270       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13271
13272       if (player->shield_deadly_time_left > 0 &&
13273           !IS_INDESTRUCTIBLE(bad_element))
13274         Bang(bad_x, bad_y);
13275       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13276         KillPlayer(player);
13277     }
13278     else
13279       Bang(kill_x, kill_y);
13280   }
13281 }
13282
13283 void TestIfPlayerTouchesBadThing(int x, int y)
13284 {
13285   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13286 }
13287
13288 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13289 {
13290   TestIfGoodThingHitsBadThing(x, y, move_dir);
13291 }
13292
13293 void TestIfBadThingTouchesPlayer(int x, int y)
13294 {
13295   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13296 }
13297
13298 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13299 {
13300   TestIfBadThingHitsGoodThing(x, y, move_dir);
13301 }
13302
13303 void TestIfFriendTouchesBadThing(int x, int y)
13304 {
13305   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13306 }
13307
13308 void TestIfBadThingTouchesFriend(int x, int y)
13309 {
13310   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13311 }
13312
13313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13314 {
13315   int i, kill_x = bad_x, kill_y = bad_y;
13316   static int xy[4][2] =
13317   {
13318     { 0, -1 },
13319     { -1, 0 },
13320     { +1, 0 },
13321     { 0, +1 }
13322   };
13323
13324   for (i = 0; i < NUM_DIRECTIONS; i++)
13325   {
13326     int x, y, element;
13327
13328     x = bad_x + xy[i][0];
13329     y = bad_y + xy[i][1];
13330     if (!IN_LEV_FIELD(x, y))
13331       continue;
13332
13333     element = Feld[x][y];
13334     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13335         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13336     {
13337       kill_x = x;
13338       kill_y = y;
13339       break;
13340     }
13341   }
13342
13343   if (kill_x != bad_x || kill_y != bad_y)
13344     Bang(bad_x, bad_y);
13345 }
13346
13347 void KillPlayer(struct PlayerInfo *player)
13348 {
13349   int jx = player->jx, jy = player->jy;
13350
13351   if (!player->active)
13352     return;
13353
13354 #if 0
13355   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13356          player->killed, player->active, player->reanimated);
13357 #endif
13358
13359   /* the following code was introduced to prevent an infinite loop when calling
13360      -> Bang()
13361      -> CheckTriggeredElementChangeExt()
13362      -> ExecuteCustomElementAction()
13363      -> KillPlayer()
13364      -> (infinitely repeating the above sequence of function calls)
13365      which occurs when killing the player while having a CE with the setting
13366      "kill player X when explosion of <player X>"; the solution using a new
13367      field "player->killed" was chosen for backwards compatibility, although
13368      clever use of the fields "player->active" etc. would probably also work */
13369 #if 1
13370   if (player->killed)
13371     return;
13372 #endif
13373
13374   player->killed = TRUE;
13375
13376   /* remove accessible field at the player's position */
13377   Feld[jx][jy] = EL_EMPTY;
13378
13379   /* deactivate shield (else Bang()/Explode() would not work right) */
13380   player->shield_normal_time_left = 0;
13381   player->shield_deadly_time_left = 0;
13382
13383 #if 0
13384   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13385          player->killed, player->active, player->reanimated);
13386 #endif
13387
13388   Bang(jx, jy);
13389
13390 #if 0
13391   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13392          player->killed, player->active, player->reanimated);
13393 #endif
13394
13395   if (player->reanimated)       /* killed player may have been reanimated */
13396     player->killed = player->reanimated = FALSE;
13397   else
13398     BuryPlayer(player);
13399 }
13400
13401 static void KillPlayerUnlessEnemyProtected(int x, int y)
13402 {
13403   if (!PLAYER_ENEMY_PROTECTED(x, y))
13404     KillPlayer(PLAYERINFO(x, y));
13405 }
13406
13407 static void KillPlayerUnlessExplosionProtected(int x, int y)
13408 {
13409   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13410     KillPlayer(PLAYERINFO(x, y));
13411 }
13412
13413 void BuryPlayer(struct PlayerInfo *player)
13414 {
13415   int jx = player->jx, jy = player->jy;
13416
13417   if (!player->active)
13418     return;
13419
13420   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13421   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13422
13423   player->GameOver = TRUE;
13424   RemovePlayer(player);
13425 }
13426
13427 void RemovePlayer(struct PlayerInfo *player)
13428 {
13429   int jx = player->jx, jy = player->jy;
13430   int i, found = FALSE;
13431
13432   player->present = FALSE;
13433   player->active = FALSE;
13434
13435   if (!ExplodeField[jx][jy])
13436     StorePlayer[jx][jy] = 0;
13437
13438   if (player->is_moving)
13439     TEST_DrawLevelField(player->last_jx, player->last_jy);
13440
13441   for (i = 0; i < MAX_PLAYERS; i++)
13442     if (stored_player[i].active)
13443       found = TRUE;
13444
13445   if (!found)
13446     AllPlayersGone = TRUE;
13447
13448   ExitX = ZX = jx;
13449   ExitY = ZY = jy;
13450 }
13451
13452 static void setFieldForSnapping(int x, int y, int element, int direction)
13453 {
13454   struct ElementInfo *ei = &element_info[element];
13455   int direction_bit = MV_DIR_TO_BIT(direction);
13456   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13457   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13458                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13459
13460   Feld[x][y] = EL_ELEMENT_SNAPPING;
13461   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13462
13463   ResetGfxAnimation(x, y);
13464
13465   GfxElement[x][y] = element;
13466   GfxAction[x][y] = action;
13467   GfxDir[x][y] = direction;
13468   GfxFrame[x][y] = -1;
13469 }
13470
13471 /*
13472   =============================================================================
13473   checkDiagonalPushing()
13474   -----------------------------------------------------------------------------
13475   check if diagonal input device direction results in pushing of object
13476   (by checking if the alternative direction is walkable, diggable, ...)
13477   =============================================================================
13478 */
13479
13480 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13481                                     int x, int y, int real_dx, int real_dy)
13482 {
13483   int jx, jy, dx, dy, xx, yy;
13484
13485   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13486     return TRUE;
13487
13488   /* diagonal direction: check alternative direction */
13489   jx = player->jx;
13490   jy = player->jy;
13491   dx = x - jx;
13492   dy = y - jy;
13493   xx = jx + (dx == 0 ? real_dx : 0);
13494   yy = jy + (dy == 0 ? real_dy : 0);
13495
13496   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13497 }
13498
13499 /*
13500   =============================================================================
13501   DigField()
13502   -----------------------------------------------------------------------------
13503   x, y:                 field next to player (non-diagonal) to try to dig to
13504   real_dx, real_dy:     direction as read from input device (can be diagonal)
13505   =============================================================================
13506 */
13507
13508 static int DigField(struct PlayerInfo *player,
13509                     int oldx, int oldy, int x, int y,
13510                     int real_dx, int real_dy, int mode)
13511 {
13512   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13513   boolean player_was_pushing = player->is_pushing;
13514   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13515   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13516   int jx = oldx, jy = oldy;
13517   int dx = x - jx, dy = y - jy;
13518   int nextx = x + dx, nexty = y + dy;
13519   int move_direction = (dx == -1 ? MV_LEFT  :
13520                         dx == +1 ? MV_RIGHT :
13521                         dy == -1 ? MV_UP    :
13522                         dy == +1 ? MV_DOWN  : MV_NONE);
13523   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13524   int dig_side = MV_DIR_OPPOSITE(move_direction);
13525   int old_element = Feld[jx][jy];
13526   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13527   int collect_count;
13528
13529   if (is_player)                /* function can also be called by EL_PENGUIN */
13530   {
13531     if (player->MovPos == 0)
13532     {
13533       player->is_digging = FALSE;
13534       player->is_collecting = FALSE;
13535     }
13536
13537     if (player->MovPos == 0)    /* last pushing move finished */
13538       player->is_pushing = FALSE;
13539
13540     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13541     {
13542       player->is_switching = FALSE;
13543       player->push_delay = -1;
13544
13545       return MP_NO_ACTION;
13546     }
13547   }
13548
13549   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13550     old_element = Back[jx][jy];
13551
13552   /* in case of element dropped at player position, check background */
13553   else if (Back[jx][jy] != EL_EMPTY &&
13554            game.engine_version >= VERSION_IDENT(2,2,0,0))
13555     old_element = Back[jx][jy];
13556
13557   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13558     return MP_NO_ACTION;        /* field has no opening in this direction */
13559
13560   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13561     return MP_NO_ACTION;        /* field has no opening in this direction */
13562
13563   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13564   {
13565     SplashAcid(x, y);
13566
13567     Feld[jx][jy] = player->artwork_element;
13568     InitMovingField(jx, jy, MV_DOWN);
13569     Store[jx][jy] = EL_ACID;
13570     ContinueMoving(jx, jy);
13571     BuryPlayer(player);
13572
13573     return MP_DONT_RUN_INTO;
13574   }
13575
13576   if (player_can_move && DONT_RUN_INTO(element))
13577   {
13578     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13579
13580     return MP_DONT_RUN_INTO;
13581   }
13582
13583   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13584     return MP_NO_ACTION;
13585
13586   collect_count = element_info[element].collect_count_initial;
13587
13588   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13589     return MP_NO_ACTION;
13590
13591   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13592     player_can_move = player_can_move_or_snap;
13593
13594   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13595       game.engine_version >= VERSION_IDENT(2,2,0,0))
13596   {
13597     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13598                                player->index_bit, dig_side);
13599     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13600                                         player->index_bit, dig_side);
13601
13602     if (element == EL_DC_LANDMINE)
13603       Bang(x, y);
13604
13605     if (Feld[x][y] != element)          /* field changed by snapping */
13606       return MP_ACTION;
13607
13608     return MP_NO_ACTION;
13609   }
13610
13611   if (player->gravity && is_player && !player->is_auto_moving &&
13612       canFallDown(player) && move_direction != MV_DOWN &&
13613       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13614     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13615
13616   if (player_can_move &&
13617       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13618   {
13619     int sound_element = SND_ELEMENT(element);
13620     int sound_action = ACTION_WALKING;
13621
13622     if (IS_RND_GATE(element))
13623     {
13624       if (!player->key[RND_GATE_NR(element)])
13625         return MP_NO_ACTION;
13626     }
13627     else if (IS_RND_GATE_GRAY(element))
13628     {
13629       if (!player->key[RND_GATE_GRAY_NR(element)])
13630         return MP_NO_ACTION;
13631     }
13632     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13633     {
13634       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13635         return MP_NO_ACTION;
13636     }
13637     else if (element == EL_EXIT_OPEN ||
13638              element == EL_EM_EXIT_OPEN ||
13639              element == EL_EM_EXIT_OPENING ||
13640              element == EL_STEEL_EXIT_OPEN ||
13641              element == EL_EM_STEEL_EXIT_OPEN ||
13642              element == EL_EM_STEEL_EXIT_OPENING ||
13643              element == EL_SP_EXIT_OPEN ||
13644              element == EL_SP_EXIT_OPENING)
13645     {
13646       sound_action = ACTION_PASSING;    /* player is passing exit */
13647     }
13648     else if (element == EL_EMPTY)
13649     {
13650       sound_action = ACTION_MOVING;             /* nothing to walk on */
13651     }
13652
13653     /* play sound from background or player, whatever is available */
13654     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13655       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13656     else
13657       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13658   }
13659   else if (player_can_move &&
13660            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13661   {
13662     if (!ACCESS_FROM(element, opposite_direction))
13663       return MP_NO_ACTION;      /* field not accessible from this direction */
13664
13665     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13666       return MP_NO_ACTION;
13667
13668     if (IS_EM_GATE(element))
13669     {
13670       if (!player->key[EM_GATE_NR(element)])
13671         return MP_NO_ACTION;
13672     }
13673     else if (IS_EM_GATE_GRAY(element))
13674     {
13675       if (!player->key[EM_GATE_GRAY_NR(element)])
13676         return MP_NO_ACTION;
13677     }
13678     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13679     {
13680       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13681         return MP_NO_ACTION;
13682     }
13683     else if (IS_EMC_GATE(element))
13684     {
13685       if (!player->key[EMC_GATE_NR(element)])
13686         return MP_NO_ACTION;
13687     }
13688     else if (IS_EMC_GATE_GRAY(element))
13689     {
13690       if (!player->key[EMC_GATE_GRAY_NR(element)])
13691         return MP_NO_ACTION;
13692     }
13693     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13694     {
13695       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13696         return MP_NO_ACTION;
13697     }
13698     else if (element == EL_DC_GATE_WHITE ||
13699              element == EL_DC_GATE_WHITE_GRAY ||
13700              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13701     {
13702       if (player->num_white_keys == 0)
13703         return MP_NO_ACTION;
13704
13705       player->num_white_keys--;
13706     }
13707     else if (IS_SP_PORT(element))
13708     {
13709       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13710           element == EL_SP_GRAVITY_PORT_RIGHT ||
13711           element == EL_SP_GRAVITY_PORT_UP ||
13712           element == EL_SP_GRAVITY_PORT_DOWN)
13713         player->gravity = !player->gravity;
13714       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13715                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13716                element == EL_SP_GRAVITY_ON_PORT_UP ||
13717                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13718         player->gravity = TRUE;
13719       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13720                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13721                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13722                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13723         player->gravity = FALSE;
13724     }
13725
13726     /* automatically move to the next field with double speed */
13727     player->programmed_action = move_direction;
13728
13729     if (player->move_delay_reset_counter == 0)
13730     {
13731       player->move_delay_reset_counter = 2;     /* two double speed steps */
13732
13733       DOUBLE_PLAYER_SPEED(player);
13734     }
13735
13736     PlayLevelSoundAction(x, y, ACTION_PASSING);
13737   }
13738   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13739   {
13740     RemoveField(x, y);
13741
13742     if (mode != DF_SNAP)
13743     {
13744       GfxElement[x][y] = GFX_ELEMENT(element);
13745       player->is_digging = TRUE;
13746     }
13747
13748     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13749
13750     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13751                                         player->index_bit, dig_side);
13752
13753     if (mode == DF_SNAP)
13754     {
13755       if (level.block_snap_field)
13756         setFieldForSnapping(x, y, element, move_direction);
13757       else
13758         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13759
13760       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13761                                           player->index_bit, dig_side);
13762     }
13763   }
13764   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13765   {
13766     RemoveField(x, y);
13767
13768     if (is_player && mode != DF_SNAP)
13769     {
13770       GfxElement[x][y] = element;
13771       player->is_collecting = TRUE;
13772     }
13773
13774     if (element == EL_SPEED_PILL)
13775     {
13776       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13777     }
13778     else if (element == EL_EXTRA_TIME && level.time > 0)
13779     {
13780       TimeLeft += level.extra_time;
13781
13782       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13783
13784       DisplayGameControlValues();
13785     }
13786     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13787     {
13788       player->shield_normal_time_left += level.shield_normal_time;
13789       if (element == EL_SHIELD_DEADLY)
13790         player->shield_deadly_time_left += level.shield_deadly_time;
13791     }
13792     else if (element == EL_DYNAMITE ||
13793              element == EL_EM_DYNAMITE ||
13794              element == EL_SP_DISK_RED)
13795     {
13796       if (player->inventory_size < MAX_INVENTORY_SIZE)
13797         player->inventory_element[player->inventory_size++] = element;
13798
13799       DrawGameDoorValues();
13800     }
13801     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13802     {
13803       player->dynabomb_count++;
13804       player->dynabombs_left++;
13805     }
13806     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13807     {
13808       player->dynabomb_size++;
13809     }
13810     else if (element == EL_DYNABOMB_INCREASE_POWER)
13811     {
13812       player->dynabomb_xl = TRUE;
13813     }
13814     else if (IS_KEY(element))
13815     {
13816       player->key[KEY_NR(element)] = TRUE;
13817
13818       DrawGameDoorValues();
13819     }
13820     else if (element == EL_DC_KEY_WHITE)
13821     {
13822       player->num_white_keys++;
13823
13824       /* display white keys? */
13825       /* DrawGameDoorValues(); */
13826     }
13827     else if (IS_ENVELOPE(element))
13828     {
13829       player->show_envelope = element;
13830     }
13831     else if (element == EL_EMC_LENSES)
13832     {
13833       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13834
13835       RedrawAllInvisibleElementsForLenses();
13836     }
13837     else if (element == EL_EMC_MAGNIFIER)
13838     {
13839       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13840
13841       RedrawAllInvisibleElementsForMagnifier();
13842     }
13843     else if (IS_DROPPABLE(element) ||
13844              IS_THROWABLE(element))     /* can be collected and dropped */
13845     {
13846       int i;
13847
13848       if (collect_count == 0)
13849         player->inventory_infinite_element = element;
13850       else
13851         for (i = 0; i < collect_count; i++)
13852           if (player->inventory_size < MAX_INVENTORY_SIZE)
13853             player->inventory_element[player->inventory_size++] = element;
13854
13855       DrawGameDoorValues();
13856     }
13857     else if (collect_count > 0)
13858     {
13859       local_player->gems_still_needed -= collect_count;
13860       if (local_player->gems_still_needed < 0)
13861         local_player->gems_still_needed = 0;
13862
13863       game.snapshot.collected_item = TRUE;
13864
13865       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13866
13867       DisplayGameControlValues();
13868     }
13869
13870     RaiseScoreElement(element);
13871     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13872
13873     if (is_player)
13874       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13875                                           player->index_bit, dig_side);
13876
13877     if (mode == DF_SNAP)
13878     {
13879       if (level.block_snap_field)
13880         setFieldForSnapping(x, y, element, move_direction);
13881       else
13882         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13883
13884       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13885                                           player->index_bit, dig_side);
13886     }
13887   }
13888   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13889   {
13890     if (mode == DF_SNAP && element != EL_BD_ROCK)
13891       return MP_NO_ACTION;
13892
13893     if (CAN_FALL(element) && dy)
13894       return MP_NO_ACTION;
13895
13896     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13897         !(element == EL_SPRING && level.use_spring_bug))
13898       return MP_NO_ACTION;
13899
13900     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13901         ((move_direction & MV_VERTICAL &&
13902           ((element_info[element].move_pattern & MV_LEFT &&
13903             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13904            (element_info[element].move_pattern & MV_RIGHT &&
13905             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13906          (move_direction & MV_HORIZONTAL &&
13907           ((element_info[element].move_pattern & MV_UP &&
13908             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13909            (element_info[element].move_pattern & MV_DOWN &&
13910             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13911       return MP_NO_ACTION;
13912
13913     /* do not push elements already moving away faster than player */
13914     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13915         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13916       return MP_NO_ACTION;
13917
13918     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13919     {
13920       if (player->push_delay_value == -1 || !player_was_pushing)
13921         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13922     }
13923     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13924     {
13925       if (player->push_delay_value == -1)
13926         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13927     }
13928     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13929     {
13930       if (!player->is_pushing)
13931         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13932     }
13933
13934     player->is_pushing = TRUE;
13935     player->is_active = TRUE;
13936
13937     if (!(IN_LEV_FIELD(nextx, nexty) &&
13938           (IS_FREE(nextx, nexty) ||
13939            (IS_SB_ELEMENT(element) &&
13940             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13941            (IS_CUSTOM_ELEMENT(element) &&
13942             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13943       return MP_NO_ACTION;
13944
13945     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13946       return MP_NO_ACTION;
13947
13948     if (player->push_delay == -1)       /* new pushing; restart delay */
13949       player->push_delay = 0;
13950
13951     if (player->push_delay < player->push_delay_value &&
13952         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13953         element != EL_SPRING && element != EL_BALLOON)
13954     {
13955       /* make sure that there is no move delay before next try to push */
13956       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13957         player->move_delay = 0;
13958
13959       return MP_NO_ACTION;
13960     }
13961
13962     if (IS_CUSTOM_ELEMENT(element) &&
13963         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13964     {
13965       if (!DigFieldByCE(nextx, nexty, element))
13966         return MP_NO_ACTION;
13967     }
13968
13969     if (IS_SB_ELEMENT(element))
13970     {
13971       if (element == EL_SOKOBAN_FIELD_FULL)
13972       {
13973         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13974         local_player->sokobanfields_still_needed++;
13975       }
13976
13977       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13978       {
13979         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13980         local_player->sokobanfields_still_needed--;
13981       }
13982
13983       Feld[x][y] = EL_SOKOBAN_OBJECT;
13984
13985       if (Back[x][y] == Back[nextx][nexty])
13986         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13987       else if (Back[x][y] != 0)
13988         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13989                                     ACTION_EMPTYING);
13990       else
13991         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13992                                     ACTION_FILLING);
13993
13994       if (local_player->sokobanfields_still_needed == 0 &&
13995           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13996       {
13997         PlayerWins(player);
13998
13999         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14000       }
14001     }
14002     else
14003       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14004
14005     InitMovingField(x, y, move_direction);
14006     GfxAction[x][y] = ACTION_PUSHING;
14007
14008     if (mode == DF_SNAP)
14009       ContinueMoving(x, y);
14010     else
14011       MovPos[x][y] = (dx != 0 ? dx : dy);
14012
14013     Pushed[x][y] = TRUE;
14014     Pushed[nextx][nexty] = TRUE;
14015
14016     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14017       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14018     else
14019       player->push_delay_value = -1;    /* get new value later */
14020
14021     /* check for element change _after_ element has been pushed */
14022     if (game.use_change_when_pushing_bug)
14023     {
14024       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14025                                  player->index_bit, dig_side);
14026       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14027                                           player->index_bit, dig_side);
14028     }
14029   }
14030   else if (IS_SWITCHABLE(element))
14031   {
14032     if (PLAYER_SWITCHING(player, x, y))
14033     {
14034       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14035                                           player->index_bit, dig_side);
14036
14037       return MP_ACTION;
14038     }
14039
14040     player->is_switching = TRUE;
14041     player->switch_x = x;
14042     player->switch_y = y;
14043
14044     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14045
14046     if (element == EL_ROBOT_WHEEL)
14047     {
14048       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14049       ZX = x;
14050       ZY = y;
14051
14052       game.robot_wheel_active = TRUE;
14053
14054       TEST_DrawLevelField(x, y);
14055     }
14056     else if (element == EL_SP_TERMINAL)
14057     {
14058       int xx, yy;
14059
14060       SCAN_PLAYFIELD(xx, yy)
14061       {
14062         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14063         {
14064           Bang(xx, yy);
14065         }
14066         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14067         {
14068           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14069
14070           ResetGfxAnimation(xx, yy);
14071           TEST_DrawLevelField(xx, yy);
14072         }
14073       }
14074     }
14075     else if (IS_BELT_SWITCH(element))
14076     {
14077       ToggleBeltSwitch(x, y);
14078     }
14079     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14080              element == EL_SWITCHGATE_SWITCH_DOWN ||
14081              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14082              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14083     {
14084       ToggleSwitchgateSwitch(x, y);
14085     }
14086     else if (element == EL_LIGHT_SWITCH ||
14087              element == EL_LIGHT_SWITCH_ACTIVE)
14088     {
14089       ToggleLightSwitch(x, y);
14090     }
14091     else if (element == EL_TIMEGATE_SWITCH ||
14092              element == EL_DC_TIMEGATE_SWITCH)
14093     {
14094       ActivateTimegateSwitch(x, y);
14095     }
14096     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14097              element == EL_BALLOON_SWITCH_RIGHT ||
14098              element == EL_BALLOON_SWITCH_UP    ||
14099              element == EL_BALLOON_SWITCH_DOWN  ||
14100              element == EL_BALLOON_SWITCH_NONE  ||
14101              element == EL_BALLOON_SWITCH_ANY)
14102     {
14103       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14104                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14105                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14106                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14107                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14108                              move_direction);
14109     }
14110     else if (element == EL_LAMP)
14111     {
14112       Feld[x][y] = EL_LAMP_ACTIVE;
14113       local_player->lights_still_needed--;
14114
14115       ResetGfxAnimation(x, y);
14116       TEST_DrawLevelField(x, y);
14117     }
14118     else if (element == EL_TIME_ORB_FULL)
14119     {
14120       Feld[x][y] = EL_TIME_ORB_EMPTY;
14121
14122       if (level.time > 0 || level.use_time_orb_bug)
14123       {
14124         TimeLeft += level.time_orb_time;
14125         game.no_time_limit = FALSE;
14126
14127         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14128
14129         DisplayGameControlValues();
14130       }
14131
14132       ResetGfxAnimation(x, y);
14133       TEST_DrawLevelField(x, y);
14134     }
14135     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14136              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14137     {
14138       int xx, yy;
14139
14140       game.ball_state = !game.ball_state;
14141
14142       SCAN_PLAYFIELD(xx, yy)
14143       {
14144         int e = Feld[xx][yy];
14145
14146         if (game.ball_state)
14147         {
14148           if (e == EL_EMC_MAGIC_BALL)
14149             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14150           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14151             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14152         }
14153         else
14154         {
14155           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14156             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14157           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14158             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14159         }
14160       }
14161     }
14162
14163     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14164                                         player->index_bit, dig_side);
14165
14166     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14167                                         player->index_bit, dig_side);
14168
14169     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14170                                         player->index_bit, dig_side);
14171
14172     return MP_ACTION;
14173   }
14174   else
14175   {
14176     if (!PLAYER_SWITCHING(player, x, y))
14177     {
14178       player->is_switching = TRUE;
14179       player->switch_x = x;
14180       player->switch_y = y;
14181
14182       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14183                                  player->index_bit, dig_side);
14184       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14185                                           player->index_bit, dig_side);
14186
14187       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14188                                  player->index_bit, dig_side);
14189       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14190                                           player->index_bit, dig_side);
14191     }
14192
14193     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14194                                player->index_bit, dig_side);
14195     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14196                                         player->index_bit, dig_side);
14197
14198     return MP_NO_ACTION;
14199   }
14200
14201   player->push_delay = -1;
14202
14203   if (is_player)                /* function can also be called by EL_PENGUIN */
14204   {
14205     if (Feld[x][y] != element)          /* really digged/collected something */
14206     {
14207       player->is_collecting = !player->is_digging;
14208       player->is_active = TRUE;
14209     }
14210   }
14211
14212   return MP_MOVING;
14213 }
14214
14215 static boolean DigFieldByCE(int x, int y, int digging_element)
14216 {
14217   int element = Feld[x][y];
14218
14219   if (!IS_FREE(x, y))
14220   {
14221     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14222                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14223                   ACTION_BREAKING);
14224
14225     /* no element can dig solid indestructible elements */
14226     if (IS_INDESTRUCTIBLE(element) &&
14227         !IS_DIGGABLE(element) &&
14228         !IS_COLLECTIBLE(element))
14229       return FALSE;
14230
14231     if (AmoebaNr[x][y] &&
14232         (element == EL_AMOEBA_FULL ||
14233          element == EL_BD_AMOEBA ||
14234          element == EL_AMOEBA_GROWING))
14235     {
14236       AmoebaCnt[AmoebaNr[x][y]]--;
14237       AmoebaCnt2[AmoebaNr[x][y]]--;
14238     }
14239
14240     if (IS_MOVING(x, y))
14241       RemoveMovingField(x, y);
14242     else
14243     {
14244       RemoveField(x, y);
14245       TEST_DrawLevelField(x, y);
14246     }
14247
14248     /* if digged element was about to explode, prevent the explosion */
14249     ExplodeField[x][y] = EX_TYPE_NONE;
14250
14251     PlayLevelSoundAction(x, y, action);
14252   }
14253
14254   Store[x][y] = EL_EMPTY;
14255
14256   /* this makes it possible to leave the removed element again */
14257   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14258     Store[x][y] = element;
14259
14260   return TRUE;
14261 }
14262
14263 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14264 {
14265   int jx = player->jx, jy = player->jy;
14266   int x = jx + dx, y = jy + dy;
14267   int snap_direction = (dx == -1 ? MV_LEFT  :
14268                         dx == +1 ? MV_RIGHT :
14269                         dy == -1 ? MV_UP    :
14270                         dy == +1 ? MV_DOWN  : MV_NONE);
14271   boolean can_continue_snapping = (level.continuous_snapping &&
14272                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14273
14274   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14275     return FALSE;
14276
14277   if (!player->active || !IN_LEV_FIELD(x, y))
14278     return FALSE;
14279
14280   if (dx && dy)
14281     return FALSE;
14282
14283   if (!dx && !dy)
14284   {
14285     if (player->MovPos == 0)
14286       player->is_pushing = FALSE;
14287
14288     player->is_snapping = FALSE;
14289
14290     if (player->MovPos == 0)
14291     {
14292       player->is_moving = FALSE;
14293       player->is_digging = FALSE;
14294       player->is_collecting = FALSE;
14295     }
14296
14297     return FALSE;
14298   }
14299
14300   /* prevent snapping with already pressed snap key when not allowed */
14301   if (player->is_snapping && !can_continue_snapping)
14302     return FALSE;
14303
14304   player->MovDir = snap_direction;
14305
14306   if (player->MovPos == 0)
14307   {
14308     player->is_moving = FALSE;
14309     player->is_digging = FALSE;
14310     player->is_collecting = FALSE;
14311   }
14312
14313   player->is_dropping = FALSE;
14314   player->is_dropping_pressed = FALSE;
14315   player->drop_pressed_delay = 0;
14316
14317   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14318     return FALSE;
14319
14320   player->is_snapping = TRUE;
14321   player->is_active = TRUE;
14322
14323   if (player->MovPos == 0)
14324   {
14325     player->is_moving = FALSE;
14326     player->is_digging = FALSE;
14327     player->is_collecting = FALSE;
14328   }
14329
14330   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14331     TEST_DrawLevelField(player->last_jx, player->last_jy);
14332
14333   TEST_DrawLevelField(x, y);
14334
14335   return TRUE;
14336 }
14337
14338 static boolean DropElement(struct PlayerInfo *player)
14339 {
14340   int old_element, new_element;
14341   int dropx = player->jx, dropy = player->jy;
14342   int drop_direction = player->MovDir;
14343   int drop_side = drop_direction;
14344   int drop_element = get_next_dropped_element(player);
14345
14346   /* do not drop an element on top of another element; when holding drop key
14347      pressed without moving, dropped element must move away before the next
14348      element can be dropped (this is especially important if the next element
14349      is dynamite, which can be placed on background for historical reasons) */
14350   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14351     return MP_ACTION;
14352
14353   if (IS_THROWABLE(drop_element))
14354   {
14355     dropx += GET_DX_FROM_DIR(drop_direction);
14356     dropy += GET_DY_FROM_DIR(drop_direction);
14357
14358     if (!IN_LEV_FIELD(dropx, dropy))
14359       return FALSE;
14360   }
14361
14362   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14363   new_element = drop_element;           /* default: no change when dropping */
14364
14365   /* check if player is active, not moving and ready to drop */
14366   if (!player->active || player->MovPos || player->drop_delay > 0)
14367     return FALSE;
14368
14369   /* check if player has anything that can be dropped */
14370   if (new_element == EL_UNDEFINED)
14371     return FALSE;
14372
14373   /* only set if player has anything that can be dropped */
14374   player->is_dropping_pressed = TRUE;
14375
14376   /* check if drop key was pressed long enough for EM style dynamite */
14377   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14378     return FALSE;
14379
14380   /* check if anything can be dropped at the current position */
14381   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14382     return FALSE;
14383
14384   /* collected custom elements can only be dropped on empty fields */
14385   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14386     return FALSE;
14387
14388   if (old_element != EL_EMPTY)
14389     Back[dropx][dropy] = old_element;   /* store old element on this field */
14390
14391   ResetGfxAnimation(dropx, dropy);
14392   ResetRandomAnimationValue(dropx, dropy);
14393
14394   if (player->inventory_size > 0 ||
14395       player->inventory_infinite_element != EL_UNDEFINED)
14396   {
14397     if (player->inventory_size > 0)
14398     {
14399       player->inventory_size--;
14400
14401       DrawGameDoorValues();
14402
14403       if (new_element == EL_DYNAMITE)
14404         new_element = EL_DYNAMITE_ACTIVE;
14405       else if (new_element == EL_EM_DYNAMITE)
14406         new_element = EL_EM_DYNAMITE_ACTIVE;
14407       else if (new_element == EL_SP_DISK_RED)
14408         new_element = EL_SP_DISK_RED_ACTIVE;
14409     }
14410
14411     Feld[dropx][dropy] = new_element;
14412
14413     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14414       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14415                           el2img(Feld[dropx][dropy]), 0);
14416
14417     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14418
14419     /* needed if previous element just changed to "empty" in the last frame */
14420     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14421
14422     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14423                                player->index_bit, drop_side);
14424     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14425                                         CE_PLAYER_DROPS_X,
14426                                         player->index_bit, drop_side);
14427
14428     TestIfElementTouchesCustomElement(dropx, dropy);
14429   }
14430   else          /* player is dropping a dyna bomb */
14431   {
14432     player->dynabombs_left--;
14433
14434     Feld[dropx][dropy] = new_element;
14435
14436     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14437       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14438                           el2img(Feld[dropx][dropy]), 0);
14439
14440     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14441   }
14442
14443   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14444     InitField_WithBug1(dropx, dropy, FALSE);
14445
14446   new_element = Feld[dropx][dropy];     /* element might have changed */
14447
14448   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14449       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14450   {
14451     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14452       MovDir[dropx][dropy] = drop_direction;
14453
14454     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14455
14456     /* do not cause impact style collision by dropping elements that can fall */
14457     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14458   }
14459
14460   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14461   player->is_dropping = TRUE;
14462
14463   player->drop_pressed_delay = 0;
14464   player->is_dropping_pressed = FALSE;
14465
14466   player->drop_x = dropx;
14467   player->drop_y = dropy;
14468
14469   return TRUE;
14470 }
14471
14472 /* ------------------------------------------------------------------------- */
14473 /* game sound playing functions                                              */
14474 /* ------------------------------------------------------------------------- */
14475
14476 static int *loop_sound_frame = NULL;
14477 static int *loop_sound_volume = NULL;
14478
14479 void InitPlayLevelSound()
14480 {
14481   int num_sounds = getSoundListSize();
14482
14483   checked_free(loop_sound_frame);
14484   checked_free(loop_sound_volume);
14485
14486   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14487   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14488 }
14489
14490 static void PlayLevelSound(int x, int y, int nr)
14491 {
14492   int sx = SCREENX(x), sy = SCREENY(y);
14493   int volume, stereo_position;
14494   int max_distance = 8;
14495   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14496
14497   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14498       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14499     return;
14500
14501   if (!IN_LEV_FIELD(x, y) ||
14502       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14503       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14504     return;
14505
14506   volume = SOUND_MAX_VOLUME;
14507
14508   if (!IN_SCR_FIELD(sx, sy))
14509   {
14510     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14511     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14512
14513     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14514   }
14515
14516   stereo_position = (SOUND_MAX_LEFT +
14517                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14518                      (SCR_FIELDX + 2 * max_distance));
14519
14520   if (IS_LOOP_SOUND(nr))
14521   {
14522     /* This assures that quieter loop sounds do not overwrite louder ones,
14523        while restarting sound volume comparison with each new game frame. */
14524
14525     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14526       return;
14527
14528     loop_sound_volume[nr] = volume;
14529     loop_sound_frame[nr] = FrameCounter;
14530   }
14531
14532   PlaySoundExt(nr, volume, stereo_position, type);
14533 }
14534
14535 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14536 {
14537   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14538                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14539                  y < LEVELY(BY1) ? LEVELY(BY1) :
14540                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14541                  sound_action);
14542 }
14543
14544 static void PlayLevelSoundAction(int x, int y, int action)
14545 {
14546   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14547 }
14548
14549 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14550 {
14551   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14552
14553   if (sound_effect != SND_UNDEFINED)
14554     PlayLevelSound(x, y, sound_effect);
14555 }
14556
14557 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14558                                               int action)
14559 {
14560   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14561
14562   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14563     PlayLevelSound(x, y, sound_effect);
14564 }
14565
14566 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14567 {
14568   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14569
14570   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14571     PlayLevelSound(x, y, sound_effect);
14572 }
14573
14574 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14575 {
14576   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14577
14578   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14579     StopSound(sound_effect);
14580 }
14581
14582 static int getLevelMusicNr()
14583 {
14584   if (levelset.music[level_nr] != MUS_UNDEFINED)
14585     return levelset.music[level_nr];            /* from config file */
14586   else
14587     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14588 }
14589
14590 static void FadeLevelSounds()
14591 {
14592   FadeSounds();
14593 }
14594
14595 static void FadeLevelMusic()
14596 {
14597   int music_nr = getLevelMusicNr();
14598   char *curr_music = getCurrentlyPlayingMusicFilename();
14599   char *next_music = getMusicInfoEntryFilename(music_nr);
14600
14601   if (!strEqual(curr_music, next_music))
14602     FadeMusic();
14603 }
14604
14605 void FadeLevelSoundsAndMusic()
14606 {
14607   FadeLevelSounds();
14608   FadeLevelMusic();
14609 }
14610
14611 static void PlayLevelMusic()
14612 {
14613   int music_nr = getLevelMusicNr();
14614   char *curr_music = getCurrentlyPlayingMusicFilename();
14615   char *next_music = getMusicInfoEntryFilename(music_nr);
14616
14617   if (!strEqual(curr_music, next_music))
14618     PlayMusic(music_nr);
14619 }
14620
14621 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14622 {
14623   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14624   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14625   int x = xx - 1 - offset;
14626   int y = yy - 1 - offset;
14627
14628   switch (sample)
14629   {
14630     case SAMPLE_blank:
14631       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14632       break;
14633
14634     case SAMPLE_roll:
14635       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14636       break;
14637
14638     case SAMPLE_stone:
14639       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14640       break;
14641
14642     case SAMPLE_nut:
14643       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14644       break;
14645
14646     case SAMPLE_crack:
14647       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14648       break;
14649
14650     case SAMPLE_bug:
14651       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14652       break;
14653
14654     case SAMPLE_tank:
14655       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14656       break;
14657
14658     case SAMPLE_android_clone:
14659       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14660       break;
14661
14662     case SAMPLE_android_move:
14663       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14664       break;
14665
14666     case SAMPLE_spring:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14668       break;
14669
14670     case SAMPLE_slurp:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14672       break;
14673
14674     case SAMPLE_eater:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14676       break;
14677
14678     case SAMPLE_eater_eat:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14680       break;
14681
14682     case SAMPLE_alien:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14684       break;
14685
14686     case SAMPLE_collect:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14688       break;
14689
14690     case SAMPLE_diamond:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14692       break;
14693
14694     case SAMPLE_squash:
14695       /* !!! CHECK THIS !!! */
14696 #if 1
14697       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14698 #else
14699       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14700 #endif
14701       break;
14702
14703     case SAMPLE_wonderfall:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14705       break;
14706
14707     case SAMPLE_drip:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14709       break;
14710
14711     case SAMPLE_push:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14713       break;
14714
14715     case SAMPLE_dirt:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14717       break;
14718
14719     case SAMPLE_acid:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14721       break;
14722
14723     case SAMPLE_ball:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14725       break;
14726
14727     case SAMPLE_grow:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14729       break;
14730
14731     case SAMPLE_wonder:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14733       break;
14734
14735     case SAMPLE_door:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14737       break;
14738
14739     case SAMPLE_exit_open:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14741       break;
14742
14743     case SAMPLE_exit_leave:
14744       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14745       break;
14746
14747     case SAMPLE_dynamite:
14748       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14749       break;
14750
14751     case SAMPLE_tick:
14752       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14753       break;
14754
14755     case SAMPLE_press:
14756       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14757       break;
14758
14759     case SAMPLE_wheel:
14760       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14761       break;
14762
14763     case SAMPLE_boom:
14764       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14765       break;
14766
14767     case SAMPLE_die:
14768       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14769       break;
14770
14771     case SAMPLE_time:
14772       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14773       break;
14774
14775     default:
14776       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14777       break;
14778   }
14779 }
14780
14781 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14782 {
14783   int element = map_element_SP_to_RND(element_sp);
14784   int action = map_action_SP_to_RND(action_sp);
14785   int offset = (setup.sp_show_border_elements ? 0 : 1);
14786   int x = xx - offset;
14787   int y = yy - offset;
14788
14789   PlayLevelSoundElementAction(x, y, element, action);
14790 }
14791
14792 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14793 {
14794   int element = map_element_MM_to_RND(element_mm);
14795   int action = map_action_MM_to_RND(action_mm);
14796   int offset = 0;
14797   int x = xx - offset;
14798   int y = yy - offset;
14799
14800   if (!IS_MM_ELEMENT(element))
14801     element = EL_MM_DEFAULT;
14802
14803   PlayLevelSoundElementAction(x, y, element, action);
14804 }
14805
14806 void PlaySound_MM(int sound_mm)
14807 {
14808   int sound = map_sound_MM_to_RND(sound_mm);
14809
14810   if (sound == SND_UNDEFINED)
14811     return;
14812
14813   PlaySound(sound);
14814 }
14815
14816 void PlaySoundLoop_MM(int sound_mm)
14817 {
14818   int sound = map_sound_MM_to_RND(sound_mm);
14819
14820   if (sound == SND_UNDEFINED)
14821     return;
14822
14823   PlaySoundLoop(sound);
14824 }
14825
14826 void StopSound_MM(int sound_mm)
14827 {
14828   int sound = map_sound_MM_to_RND(sound_mm);
14829
14830   if (sound == SND_UNDEFINED)
14831     return;
14832
14833   StopSound(sound);
14834 }
14835
14836 void RaiseScore(int value)
14837 {
14838   local_player->score += value;
14839
14840   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14841
14842   DisplayGameControlValues();
14843 }
14844
14845 void RaiseScoreElement(int element)
14846 {
14847   switch (element)
14848   {
14849     case EL_EMERALD:
14850     case EL_BD_DIAMOND:
14851     case EL_EMERALD_YELLOW:
14852     case EL_EMERALD_RED:
14853     case EL_EMERALD_PURPLE:
14854     case EL_SP_INFOTRON:
14855       RaiseScore(level.score[SC_EMERALD]);
14856       break;
14857     case EL_DIAMOND:
14858       RaiseScore(level.score[SC_DIAMOND]);
14859       break;
14860     case EL_CRYSTAL:
14861       RaiseScore(level.score[SC_CRYSTAL]);
14862       break;
14863     case EL_PEARL:
14864       RaiseScore(level.score[SC_PEARL]);
14865       break;
14866     case EL_BUG:
14867     case EL_BD_BUTTERFLY:
14868     case EL_SP_ELECTRON:
14869       RaiseScore(level.score[SC_BUG]);
14870       break;
14871     case EL_SPACESHIP:
14872     case EL_BD_FIREFLY:
14873     case EL_SP_SNIKSNAK:
14874       RaiseScore(level.score[SC_SPACESHIP]);
14875       break;
14876     case EL_YAMYAM:
14877     case EL_DARK_YAMYAM:
14878       RaiseScore(level.score[SC_YAMYAM]);
14879       break;
14880     case EL_ROBOT:
14881       RaiseScore(level.score[SC_ROBOT]);
14882       break;
14883     case EL_PACMAN:
14884       RaiseScore(level.score[SC_PACMAN]);
14885       break;
14886     case EL_NUT:
14887       RaiseScore(level.score[SC_NUT]);
14888       break;
14889     case EL_DYNAMITE:
14890     case EL_EM_DYNAMITE:
14891     case EL_SP_DISK_RED:
14892     case EL_DYNABOMB_INCREASE_NUMBER:
14893     case EL_DYNABOMB_INCREASE_SIZE:
14894     case EL_DYNABOMB_INCREASE_POWER:
14895       RaiseScore(level.score[SC_DYNAMITE]);
14896       break;
14897     case EL_SHIELD_NORMAL:
14898     case EL_SHIELD_DEADLY:
14899       RaiseScore(level.score[SC_SHIELD]);
14900       break;
14901     case EL_EXTRA_TIME:
14902       RaiseScore(level.extra_time_score);
14903       break;
14904     case EL_KEY_1:
14905     case EL_KEY_2:
14906     case EL_KEY_3:
14907     case EL_KEY_4:
14908     case EL_EM_KEY_1:
14909     case EL_EM_KEY_2:
14910     case EL_EM_KEY_3:
14911     case EL_EM_KEY_4:
14912     case EL_EMC_KEY_5:
14913     case EL_EMC_KEY_6:
14914     case EL_EMC_KEY_7:
14915     case EL_EMC_KEY_8:
14916     case EL_DC_KEY_WHITE:
14917       RaiseScore(level.score[SC_KEY]);
14918       break;
14919     default:
14920       RaiseScore(element_info[element].collect_score);
14921       break;
14922   }
14923 }
14924
14925 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14926 {
14927   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14928   {
14929     /* closing door required in case of envelope style request dialogs */
14930     if (!skip_request)
14931       CloseDoor(DOOR_CLOSE_1);
14932
14933 #if defined(NETWORK_AVALIABLE)
14934     if (options.network)
14935       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14936     else
14937 #endif
14938     {
14939       if (quick_quit)
14940         FadeSkipNextFadeIn();
14941
14942       SetGameStatus(GAME_MODE_MAIN);
14943
14944       DrawMainMenu();
14945     }
14946   }
14947   else          /* continue playing the game */
14948   {
14949     if (tape.playing && tape.deactivate_display)
14950       TapeDeactivateDisplayOff(TRUE);
14951
14952     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14953
14954     if (tape.playing && tape.deactivate_display)
14955       TapeDeactivateDisplayOn();
14956   }
14957 }
14958
14959 void RequestQuitGame(boolean ask_if_really_quit)
14960 {
14961   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14962   boolean skip_request = AllPlayersGone || quick_quit;
14963
14964   RequestQuitGameExt(skip_request, quick_quit,
14965                      "Do you really want to quit the game?");
14966 }
14967
14968 void RequestRestartGame(char *message)
14969 {
14970   game.restart_game_message = NULL;
14971
14972   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14973   {
14974     StartGameActions(options.network, setup.autorecord, level.random_seed);
14975   }
14976   else
14977   {
14978     SetGameStatus(GAME_MODE_MAIN);
14979
14980     DrawMainMenu();
14981   }
14982 }
14983
14984
14985 /* ------------------------------------------------------------------------- */
14986 /* random generator functions                                                */
14987 /* ------------------------------------------------------------------------- */
14988
14989 unsigned int InitEngineRandom_RND(int seed)
14990 {
14991   game.num_random_calls = 0;
14992
14993   return InitEngineRandom(seed);
14994 }
14995
14996 unsigned int RND(int max)
14997 {
14998   if (max > 0)
14999   {
15000     game.num_random_calls++;
15001
15002     return GetEngineRandom(max);
15003   }
15004
15005   return 0;
15006 }
15007
15008
15009 /* ------------------------------------------------------------------------- */
15010 /* game engine snapshot handling functions                                   */
15011 /* ------------------------------------------------------------------------- */
15012
15013 struct EngineSnapshotInfo
15014 {
15015   /* runtime values for custom element collect score */
15016   int collect_score[NUM_CUSTOM_ELEMENTS];
15017
15018   /* runtime values for group element choice position */
15019   int choice_pos[NUM_GROUP_ELEMENTS];
15020
15021   /* runtime values for belt position animations */
15022   int belt_graphic[4][NUM_BELT_PARTS];
15023   int belt_anim_mode[4][NUM_BELT_PARTS];
15024 };
15025
15026 static struct EngineSnapshotInfo engine_snapshot_rnd;
15027 static char *snapshot_level_identifier = NULL;
15028 static int snapshot_level_nr = -1;
15029
15030 static void SaveEngineSnapshotValues_RND()
15031 {
15032   static int belt_base_active_element[4] =
15033   {
15034     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15035     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15036     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15037     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15038   };
15039   int i, j;
15040
15041   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15042   {
15043     int element = EL_CUSTOM_START + i;
15044
15045     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15046   }
15047
15048   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15049   {
15050     int element = EL_GROUP_START + i;
15051
15052     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15053   }
15054
15055   for (i = 0; i < 4; i++)
15056   {
15057     for (j = 0; j < NUM_BELT_PARTS; j++)
15058     {
15059       int element = belt_base_active_element[i] + j;
15060       int graphic = el2img(element);
15061       int anim_mode = graphic_info[graphic].anim_mode;
15062
15063       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15064       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15065     }
15066   }
15067 }
15068
15069 static void LoadEngineSnapshotValues_RND()
15070 {
15071   unsigned int num_random_calls = game.num_random_calls;
15072   int i, j;
15073
15074   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15075   {
15076     int element = EL_CUSTOM_START + i;
15077
15078     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15079   }
15080
15081   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15082   {
15083     int element = EL_GROUP_START + i;
15084
15085     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15086   }
15087
15088   for (i = 0; i < 4; i++)
15089   {
15090     for (j = 0; j < NUM_BELT_PARTS; j++)
15091     {
15092       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15093       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15094
15095       graphic_info[graphic].anim_mode = anim_mode;
15096     }
15097   }
15098
15099   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15100   {
15101     InitRND(tape.random_seed);
15102     for (i = 0; i < num_random_calls; i++)
15103       RND(1);
15104   }
15105
15106   if (game.num_random_calls != num_random_calls)
15107   {
15108     Error(ERR_INFO, "number of random calls out of sync");
15109     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15110     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15111     Error(ERR_EXIT, "this should not happen -- please debug");
15112   }
15113 }
15114
15115 void FreeEngineSnapshotSingle()
15116 {
15117   FreeSnapshotSingle();
15118
15119   setString(&snapshot_level_identifier, NULL);
15120   snapshot_level_nr = -1;
15121 }
15122
15123 void FreeEngineSnapshotList()
15124 {
15125   FreeSnapshotList();
15126 }
15127
15128 ListNode *SaveEngineSnapshotBuffers()
15129 {
15130   ListNode *buffers = NULL;
15131
15132   /* copy some special values to a structure better suited for the snapshot */
15133
15134   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15135     SaveEngineSnapshotValues_RND();
15136   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15137     SaveEngineSnapshotValues_EM();
15138   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15139     SaveEngineSnapshotValues_SP(&buffers);
15140   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15141     SaveEngineSnapshotValues_MM(&buffers);
15142
15143   /* save values stored in special snapshot structure */
15144
15145   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15146     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15148     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15149   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15150     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15151   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15152     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15153
15154   /* save further RND engine values */
15155
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15159
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15170
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15174
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15176
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15178
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15181
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15200
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15203
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15207
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15210
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15216
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15219
15220 #if 0
15221   ListNode *node = engine_snapshot_list_rnd;
15222   int num_bytes = 0;
15223
15224   while (node != NULL)
15225   {
15226     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15227
15228     node = node->next;
15229   }
15230
15231   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15232 #endif
15233
15234   return buffers;
15235 }
15236
15237 void SaveEngineSnapshotSingle()
15238 {
15239   ListNode *buffers = SaveEngineSnapshotBuffers();
15240
15241   /* finally save all snapshot buffers to single snapshot */
15242   SaveSnapshotSingle(buffers);
15243
15244   /* save level identification information */
15245   setString(&snapshot_level_identifier, leveldir_current->identifier);
15246   snapshot_level_nr = level_nr;
15247 }
15248
15249 boolean CheckSaveEngineSnapshotToList()
15250 {
15251   boolean save_snapshot =
15252     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15253      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15254       game.snapshot.changed_action) ||
15255      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15256       game.snapshot.collected_item));
15257
15258   game.snapshot.changed_action = FALSE;
15259   game.snapshot.collected_item = FALSE;
15260   game.snapshot.save_snapshot = save_snapshot;
15261
15262   return save_snapshot;
15263 }
15264
15265 void SaveEngineSnapshotToList()
15266 {
15267   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15268       tape.quick_resume)
15269     return;
15270
15271   ListNode *buffers = SaveEngineSnapshotBuffers();
15272
15273   /* finally save all snapshot buffers to snapshot list */
15274   SaveSnapshotToList(buffers);
15275 }
15276
15277 void SaveEngineSnapshotToListInitial()
15278 {
15279   FreeEngineSnapshotList();
15280
15281   SaveEngineSnapshotToList();
15282 }
15283
15284 void LoadEngineSnapshotValues()
15285 {
15286   /* restore special values from snapshot structure */
15287
15288   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15289     LoadEngineSnapshotValues_RND();
15290   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15291     LoadEngineSnapshotValues_EM();
15292   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15293     LoadEngineSnapshotValues_SP();
15294   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15295     LoadEngineSnapshotValues_MM();
15296 }
15297
15298 void LoadEngineSnapshotSingle()
15299 {
15300   LoadSnapshotSingle();
15301
15302   LoadEngineSnapshotValues();
15303 }
15304
15305 void LoadEngineSnapshot_Undo(int steps)
15306 {
15307   LoadSnapshotFromList_Older(steps);
15308
15309   LoadEngineSnapshotValues();
15310 }
15311
15312 void LoadEngineSnapshot_Redo(int steps)
15313 {
15314   LoadSnapshotFromList_Newer(steps);
15315
15316   LoadEngineSnapshotValues();
15317 }
15318
15319 boolean CheckEngineSnapshotSingle()
15320 {
15321   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15322           snapshot_level_nr == level_nr);
15323 }
15324
15325 boolean CheckEngineSnapshotList()
15326 {
15327   return CheckSnapshotList();
15328 }
15329
15330
15331 /* ---------- new game button stuff ---------------------------------------- */
15332
15333 static struct
15334 {
15335   int graphic;
15336   struct XY *pos;
15337   int gadget_id;
15338   boolean *setup_value;
15339   boolean allowed_on_tape;
15340   char *infotext;
15341 } gamebutton_info[NUM_GAME_BUTTONS] =
15342 {
15343   {
15344     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15345     GAME_CTRL_ID_STOP,                          NULL,
15346     TRUE,                                       "stop game"
15347   },
15348   {
15349     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15350     GAME_CTRL_ID_PAUSE,                         NULL,
15351     TRUE,                                       "pause game"
15352   },
15353   {
15354     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15355     GAME_CTRL_ID_PLAY,                          NULL,
15356     TRUE,                                       "play game"
15357   },
15358   {
15359     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15360     GAME_CTRL_ID_UNDO,                          NULL,
15361     TRUE,                                       "undo step"
15362   },
15363   {
15364     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15365     GAME_CTRL_ID_REDO,                          NULL,
15366     TRUE,                                       "redo step"
15367   },
15368   {
15369     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15370     GAME_CTRL_ID_SAVE,                          NULL,
15371     TRUE,                                       "save game"
15372   },
15373   {
15374     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15375     GAME_CTRL_ID_PAUSE2,                        NULL,
15376     TRUE,                                       "pause game"
15377   },
15378   {
15379     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15380     GAME_CTRL_ID_LOAD,                          NULL,
15381     TRUE,                                       "load game"
15382   },
15383   {
15384     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15385     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15386     FALSE,                                      "stop game"
15387   },
15388   {
15389     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15390     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15391     FALSE,                                      "pause game"
15392   },
15393   {
15394     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15395     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15396     FALSE,                                      "play game"
15397   },
15398   {
15399     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15400     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15401     TRUE,                                       "background music on/off"
15402   },
15403   {
15404     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15405     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15406     TRUE,                                       "sound loops on/off"
15407   },
15408   {
15409     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15410     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15411     TRUE,                                       "normal sounds on/off"
15412   },
15413   {
15414     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15415     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15416     FALSE,                                      "background music on/off"
15417   },
15418   {
15419     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15420     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15421     FALSE,                                      "sound loops on/off"
15422   },
15423   {
15424     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15425     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15426     FALSE,                                      "normal sounds on/off"
15427   }
15428 };
15429
15430 void CreateGameButtons()
15431 {
15432   int i;
15433
15434   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15435   {
15436     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15437     struct XY *pos = gamebutton_info[i].pos;
15438     struct GadgetInfo *gi;
15439     int button_type;
15440     boolean checked;
15441     unsigned int event_mask;
15442     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15443     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15444     int base_x = (on_tape ? VX : DX);
15445     int base_y = (on_tape ? VY : DY);
15446     int gd_x   = gfx->src_x;
15447     int gd_y   = gfx->src_y;
15448     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15449     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15450     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15451     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15452     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15453     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15454     int id = i;
15455
15456     if (gfx->bitmap == NULL)
15457     {
15458       game_gadget[id] = NULL;
15459
15460       continue;
15461     }
15462
15463     if (id == GAME_CTRL_ID_STOP ||
15464         id == GAME_CTRL_ID_PANEL_STOP ||
15465         id == GAME_CTRL_ID_PLAY ||
15466         id == GAME_CTRL_ID_PANEL_PLAY ||
15467         id == GAME_CTRL_ID_SAVE ||
15468         id == GAME_CTRL_ID_LOAD)
15469     {
15470       button_type = GD_TYPE_NORMAL_BUTTON;
15471       checked = FALSE;
15472       event_mask = GD_EVENT_RELEASED;
15473     }
15474     else if (id == GAME_CTRL_ID_UNDO ||
15475              id == GAME_CTRL_ID_REDO)
15476     {
15477       button_type = GD_TYPE_NORMAL_BUTTON;
15478       checked = FALSE;
15479       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15480     }
15481     else
15482     {
15483       button_type = GD_TYPE_CHECK_BUTTON;
15484       checked = (gamebutton_info[i].setup_value != NULL ?
15485                  *gamebutton_info[i].setup_value : FALSE);
15486       event_mask = GD_EVENT_PRESSED;
15487     }
15488
15489     gi = CreateGadget(GDI_CUSTOM_ID, id,
15490                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15491                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15492                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15493                       GDI_WIDTH, gfx->width,
15494                       GDI_HEIGHT, gfx->height,
15495                       GDI_TYPE, button_type,
15496                       GDI_STATE, GD_BUTTON_UNPRESSED,
15497                       GDI_CHECKED, checked,
15498                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15499                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15500                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15501                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15502                       GDI_DIRECT_DRAW, FALSE,
15503                       GDI_EVENT_MASK, event_mask,
15504                       GDI_CALLBACK_ACTION, HandleGameButtons,
15505                       GDI_END);
15506
15507     if (gi == NULL)
15508       Error(ERR_EXIT, "cannot create gadget");
15509
15510     game_gadget[id] = gi;
15511   }
15512 }
15513
15514 void FreeGameButtons()
15515 {
15516   int i;
15517
15518   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15519     FreeGadget(game_gadget[i]);
15520 }
15521
15522 static void UnmapGameButtonsAtSamePosition(int id)
15523 {
15524   int i;
15525
15526   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15527     if (i != id &&
15528         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15529         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15530       UnmapGadget(game_gadget[i]);
15531 }
15532
15533 static void UnmapGameButtonsAtSamePosition_All()
15534 {
15535   if (setup.show_snapshot_buttons)
15536   {
15537     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15538     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15539     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15540   }
15541   else
15542   {
15543     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15544     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15545     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15546
15547     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15548     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15549     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15550   }
15551 }
15552
15553 static void MapGameButtonsAtSamePosition(int id)
15554 {
15555   int i;
15556
15557   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15558     if (i != id &&
15559         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15560         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15561       MapGadget(game_gadget[i]);
15562
15563   UnmapGameButtonsAtSamePosition_All();
15564 }
15565
15566 void MapUndoRedoButtons()
15567 {
15568   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15569   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15570
15571   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15572   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15573
15574   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15575 }
15576
15577 void UnmapUndoRedoButtons()
15578 {
15579   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15580   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15581
15582   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15583   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15584
15585   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15586 }
15587
15588 void MapGameButtonsExt(boolean on_tape)
15589 {
15590   int i;
15591
15592   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15593     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15594         i != GAME_CTRL_ID_UNDO &&
15595         i != GAME_CTRL_ID_REDO)
15596       MapGadget(game_gadget[i]);
15597
15598   UnmapGameButtonsAtSamePosition_All();
15599
15600   RedrawGameButtons();
15601 }
15602
15603 void UnmapGameButtonsExt(boolean on_tape)
15604 {
15605   int i;
15606
15607   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15608     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15609       UnmapGadget(game_gadget[i]);
15610 }
15611
15612 void RedrawGameButtonsExt(boolean on_tape)
15613 {
15614   int i;
15615
15616   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15617     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15618       RedrawGadget(game_gadget[i]);
15619
15620   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15621   redraw_mask &= ~REDRAW_ALL;
15622 }
15623
15624 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15625 {
15626   if (gi == NULL)
15627     return;
15628
15629   gi->checked = state;
15630 }
15631
15632 void RedrawSoundButtonGadget(int id)
15633 {
15634   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15635              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15636              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15637              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15638              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15639              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15640              id);
15641
15642   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15643   RedrawGadget(game_gadget[id2]);
15644 }
15645
15646 void MapGameButtons()
15647 {
15648   MapGameButtonsExt(FALSE);
15649 }
15650
15651 void UnmapGameButtons()
15652 {
15653   UnmapGameButtonsExt(FALSE);
15654 }
15655
15656 void RedrawGameButtons()
15657 {
15658   RedrawGameButtonsExt(FALSE);
15659 }
15660
15661 void MapGameButtonsOnTape()
15662 {
15663   MapGameButtonsExt(TRUE);
15664 }
15665
15666 void UnmapGameButtonsOnTape()
15667 {
15668   UnmapGameButtonsExt(TRUE);
15669 }
15670
15671 void RedrawGameButtonsOnTape()
15672 {
15673   RedrawGameButtonsExt(TRUE);
15674 }
15675
15676 void GameUndoRedoExt()
15677 {
15678   ClearPlayerAction();
15679
15680   tape.pausing = TRUE;
15681
15682   RedrawPlayfield();
15683   UpdateAndDisplayGameControlValues();
15684
15685   DrawCompleteVideoDisplay();
15686   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15687   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15688   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15689
15690   BackToFront();
15691 }
15692
15693 void GameUndo(int steps)
15694 {
15695   if (!CheckEngineSnapshotList())
15696     return;
15697
15698   LoadEngineSnapshot_Undo(steps);
15699
15700   GameUndoRedoExt();
15701 }
15702
15703 void GameRedo(int steps)
15704 {
15705   if (!CheckEngineSnapshotList())
15706     return;
15707
15708   LoadEngineSnapshot_Redo(steps);
15709
15710   GameUndoRedoExt();
15711 }
15712
15713 static void HandleGameButtonsExt(int id, int button)
15714 {
15715   static boolean game_undo_executed = FALSE;
15716   int steps = BUTTON_STEPSIZE(button);
15717   boolean handle_game_buttons =
15718     (game_status == GAME_MODE_PLAYING ||
15719      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15720
15721   if (!handle_game_buttons)
15722     return;
15723
15724   switch (id)
15725   {
15726     case GAME_CTRL_ID_STOP:
15727     case GAME_CTRL_ID_PANEL_STOP:
15728       if (game_status == GAME_MODE_MAIN)
15729         break;
15730
15731       if (tape.playing)
15732         TapeStop();
15733       else
15734         RequestQuitGame(TRUE);
15735
15736       break;
15737
15738     case GAME_CTRL_ID_PAUSE:
15739     case GAME_CTRL_ID_PAUSE2:
15740     case GAME_CTRL_ID_PANEL_PAUSE:
15741       if (options.network && game_status == GAME_MODE_PLAYING)
15742       {
15743 #if defined(NETWORK_AVALIABLE)
15744         if (tape.pausing)
15745           SendToServer_ContinuePlaying();
15746         else
15747           SendToServer_PausePlaying();
15748 #endif
15749       }
15750       else
15751         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15752
15753       game_undo_executed = FALSE;
15754
15755       break;
15756
15757     case GAME_CTRL_ID_PLAY:
15758     case GAME_CTRL_ID_PANEL_PLAY:
15759       if (game_status == GAME_MODE_MAIN)
15760       {
15761         StartGameActions(options.network, setup.autorecord, level.random_seed);
15762       }
15763       else if (tape.pausing)
15764       {
15765 #if defined(NETWORK_AVALIABLE)
15766         if (options.network)
15767           SendToServer_ContinuePlaying();
15768         else
15769 #endif
15770           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15771       }
15772       break;
15773
15774     case GAME_CTRL_ID_UNDO:
15775       // Important: When using "save snapshot when collecting an item" mode,
15776       // load last (current) snapshot for first "undo" after pressing "pause"
15777       // (else the last-but-one snapshot would be loaded, because the snapshot
15778       // pointer already points to the last snapshot when pressing "pause",
15779       // which is fine for "every step/move" mode, but not for "every collect")
15780       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15781           !game_undo_executed)
15782         steps--;
15783
15784       game_undo_executed = TRUE;
15785
15786       GameUndo(steps);
15787       break;
15788
15789     case GAME_CTRL_ID_REDO:
15790       GameRedo(steps);
15791       break;
15792
15793     case GAME_CTRL_ID_SAVE:
15794       TapeQuickSave();
15795       break;
15796
15797     case GAME_CTRL_ID_LOAD:
15798       TapeQuickLoad();
15799       break;
15800
15801     case SOUND_CTRL_ID_MUSIC:
15802     case SOUND_CTRL_ID_PANEL_MUSIC:
15803       if (setup.sound_music)
15804       { 
15805         setup.sound_music = FALSE;
15806
15807         FadeMusic();
15808       }
15809       else if (audio.music_available)
15810       { 
15811         setup.sound = setup.sound_music = TRUE;
15812
15813         SetAudioMode(setup.sound);
15814
15815         if (game_status == GAME_MODE_PLAYING)
15816           PlayLevelMusic();
15817       }
15818
15819       RedrawSoundButtonGadget(id);
15820
15821       break;
15822
15823     case SOUND_CTRL_ID_LOOPS:
15824     case SOUND_CTRL_ID_PANEL_LOOPS:
15825       if (setup.sound_loops)
15826         setup.sound_loops = FALSE;
15827       else if (audio.loops_available)
15828       {
15829         setup.sound = setup.sound_loops = TRUE;
15830
15831         SetAudioMode(setup.sound);
15832       }
15833
15834       RedrawSoundButtonGadget(id);
15835
15836       break;
15837
15838     case SOUND_CTRL_ID_SIMPLE:
15839     case SOUND_CTRL_ID_PANEL_SIMPLE:
15840       if (setup.sound_simple)
15841         setup.sound_simple = FALSE;
15842       else if (audio.sound_available)
15843       {
15844         setup.sound = setup.sound_simple = TRUE;
15845
15846         SetAudioMode(setup.sound);
15847       }
15848
15849       RedrawSoundButtonGadget(id);
15850
15851       break;
15852
15853     default:
15854       break;
15855   }
15856 }
15857
15858 static void HandleGameButtons(struct GadgetInfo *gi)
15859 {
15860   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15861 }
15862
15863 void HandleSoundButtonKeys(Key key)
15864 {
15865   if (key == setup.shortcut.sound_simple)
15866     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15867   else if (key == setup.shortcut.sound_loops)
15868     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15869   else if (key == setup.shortcut.sound_music)
15870     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15871 }